Securing JBoss / WildFly Management Interfaces: the easy way

This is the second tutorial about securing WildFly. In the first one, we have discussed how to secure the HTTP channel for Web applications:  How to configure SSL/HTTPS on WildFly

In this tutorial we will learn how to secure JBoss / WildFly Management interfaces using Elytron.

Firstly, we will at first demonstrate how to create a JDBC Realm and use that for accessing the Management interfaces.

Then, we will show how to encrypt the communication between the Web Console or the CLI and the application server

Secure Management interfaces with a JDBC Realm

Firstly, to configuring a JDBC Realm, you need to define a Datasource. For this purpose we will create a PostgreSQL Datasource, define a table for USERS and add at least one Admin user in it.

Start by creating the PostgreSQL Datasource which connects to a local PostgreSQL DB using “postgres/postgres” as credentials:

module add --name=org.postgres --resources=postgresql-42.2.5.jar --dependencies=javax.api,javax.transaction.api

/subsystem=datasources/jdbc-driver=postgres:add(driver-name="postgres",driver-module-name="org.postgres",driver-class-name=org.postgresql.Driver)

data-source add --jndi-name=java:/PostGreDS --name=PostgrePool --connection-url=jdbc:postgresql://localhost/postgres --driver-name=postgres --user-name=postgres --password=postgres

reload

Next, verify that the Datasource is available:

/subsystem=datasources/data-source=PostgrePool:test-connection-in-pool

Finally, create a table USERS with one row in it:

$ psql postgres postgres
psql (11.2 (Debian 11.2-1.pgdg90+1))
Type "help" for help.

postgres=# CREATE TABLE USERS(login VARCHAR(64) PRIMARY KEY, password VARCHAR(64), role VARCHAR(64));
postgres=# INSERT into USERS (login,password,role) values('admin','secret','Admin');

Creating the JDBC Realm

With the Datasource in place, let’s create the JDBC Realm and Security Domain which maps our Database table:

#Define the JDBC Realm
/subsystem=elytron/jdbc-realm=demoJdbcRealm:add(principal-query=[{sql="SELECT password,role FROM USERS WHERE login=?",data-source=PostgrePool,clear-password-mapper={password-index=1},attribute-mapping=[{index=2,to=groups}]}])

#Define the Security Domain
/subsystem=elytron/security-domain=jdbcSD:add(realms=[{realm=demoJdbcRealm,role-decoder=groups-to-roles}],default-realm=demoJdbcRealm,permission-mapper=default-permission-mapper)

As our authentication will pass through the HTTP connectors, we will add an HTTP Authentication factory to our configuration:

/subsystem=elytron/http-authentication-factory=db-http-auth:add(http-server-mechanism-factory=global,security-domain=jdbcSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=RealmUsersRoles}]}])

You should be able to see in your XML configuration:

<security-domains>
    <!-- default security domains -->

    <security-domain name="jdbcSD" default-realm="demoJdbcRealm" permission-mapper="default-permission-mapper">
        <realm name="demoJdbcRealm" role-decoder="groups-to-roles"/>
    </security-domain>

</security-domains>
<security-realms>
    <identity-realm name="local" identity="$local"/>
    <jdbc-realm name="demoJdbcRealm">
        <principal-query sql="SELECT password,role FROM USERS WHERE login=?" data-source="PostgrePool">
            <attribute-mapping>
                <attribute to="groups" index="2"/>
            </attribute-mapping>
            <clear-password-mapper password-index="1"/>
        </principal-query>
    </jdbc-realm>

    <!-- default Property realms -->
</security-realms>

Then, in the HTTP Management factory section, the following should have been added:

<http-authentication-factory name="db-http-auth" security-domain="jdbcSD" http-server-mechanism-factory="global">
    <mechanism-configuration>
        <mechanism mechanism-name="BASIC">
            <mechanism-realm realm-name="RealmUsersRoles"/>
        </mechanism>
    </mechanism-configuration>
</http-authentication-factory>

Binding Management interfaces to the HTTP Authentication factory

Finally, we can bind the management interface with the HTTP Authentication Factory. This can be done as follows:

/core-service=management/management-interface=http-interface:write-attribute(name=http-authentication-factory,value=db-http-auth)

As a proof of concept, verify that by logging into the management console (http://localhost:9990), you are able to connect using the users in the Database:

Enabling SSL for the Web console

The second step to secure our management interfaces will be enabling SSL. This can be done using the new security enable-ssl-management CLI command that allows creating the required key pairs and install them in your configuration. We will use this command with the –interactive options that requires passing the arguments on the command line:

 security enable-ssl-management --interactive
Please provide required pieces of information to enable SSL:

Certificate info:
Key-store file name (default management.keystore): 
Password (blank generated): wildfly
What is your first and last name? [Unknown]: Admin
What is the name of your organizational unit? [Unknown]: Administrators
What is the name of your organization? [Unknown]: Acme
What is the name of your City or Locality? [Unknown]: London
What is the name of your State or Province? [Unknown]: 
What is the two-letter country code for this unit? [Unknown]: UK
Is CN=Admin, OU=Administrators, O=Acme, L=London, ST=Unknown, C=UK correct y/n [y]?y
Validity (in days, blank default): 
Alias (blank generated): admin
Enable SSL Mutual Authentication y/n (blank n):n

SSL options:
key store file: management.keystore
distinguished name: CN=Admin, OU=Administrators, O=Acme, L=London, ST=Unknown, C=UK
password: wildfly
validity: default
alias: admin
Server keystore file management.keystore, certificate file management.pem and management.csr file will be generated in server configuration directory.

Do you confirm y/n :y
Unable to connect due to unrecognised server certificate
Subject    - CN=Admin,OU=Administrators,O=Acme,L=London,ST=Unknown,C=UK
Issuer     - CN=Admin, OU=Administrators, O=Acme, L=London, ST=Unknown, C=UK
Valid From - Wed Jan 08 10:15:46 CET 2020
Valid To   - Tue Apr 07 10:15:46 CEST 2020
MD5 : 7c:63:76:48:ec:8d:e2:2c:96:74:4d:19:7d:81:e1:6d
SHA1 : de:3e:ba:f5:9b:c1:9c:4c:e5:48:ca:cf:f4:e2:71:63:d3:20:19:1a


Accept certificate? [N]o, [T]emporarily, [P]ermanently : P
Server reloaded.
SSL enabled for http-interface
ssl-context is ssl-context-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86
key-manager is key-manager-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86
key-store   is key-store-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86

As you can see, the certificate has been installed in the configuration folder and the CLI connection already switched to port 9993.

In terms of configuration, the following ssl-context has been added to the management interface:

 <management-interfaces>
            <http-interface ssl-context="ssl-context-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86" security-realm="ManagementRealm">
                <http-upgrade enabled="true"/>
                <socket-binding http="management-http" https="management-https"/>
            </http-interface>
 </management-interfaces>

The SSL Context, in turn, is defined in the Elytron TSL section, which contains the key-store definition, the related key-manager and the server-ssl-context:

<tls>
    <key-stores>
        <key-store name="key-store-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86">
            <credential-reference clear-text="wildfly"/>
            <implementation type="JKS"/>
            <file required="false" path="management.keystore" relative-to="jboss.server.config.dir"/>
        </key-store>
    </key-stores>
    <key-managers>
        <key-manager name="key-manager-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86" key-store="key-store-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86">
            <credential-reference clear-text="wildfly"/>
        </key-manager>
    </key-managers>
    <server-ssl-contexts>
        <server-ssl-context name="ssl-context-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86" cipher-suite-filter="DEFAULT" protocols="TLSv1.2" want-client-auth="false" need-client-auth="false" authentication-optional="false" use-cipher-suites-order="false" key-manager="key-manager-eb0e29ad-6cf6-4c28-aab2-ffba55eb1d86"/>
    </server-ssl-contexts>
</tls>

As a proof of concept, you can now connect to the Management interface through https://localhost:9993

In order to disable SSL for your management interface, simply issue from the CLI:

 security disable-ssl-management 

Enabling SSL for the CLI

We will now show how to secure the CLI using mutual SSL authentication. We will still use the security enable-ssl-management command, however we will answer “yes” to Enable SSL Mutual Authentication” and provide the path where the Client Certificate is stored:

[standalone@localhost:9990 /] security enable-ssl-management --interactive
Please provide required pieces of information to enable SSL:

Certificate info:
Key-store file name (default management.keystore):
Password (blank generated): secret
What is your first and last name? [Unknown]:
What is the name of your organizational unit? [Unknown]:
What is the name of your organization? [Unknown]:
What is the name of your City or Locality? [Unknown]:
What is the name of your State or Province? [Unknown]:
What is the two-letter country code for this unit? [Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct y/n [y]?y
Validity (in days, blank default):
Alias (blank generated): localhost
Enable SSL Mutual Authentication y/n (blank n):y
Client certificate (path to pem file): /home/jboss/wildfly24.0.0.Final/standalone/configuration/client.crt
Validate certificate y/n (blank y): n
Trust-store file name (management.truststore):
Password (blank generated): secret

SSL options:
key store file: management.keystore
distinguished name: CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=
Unknown
password: secret
validity: default
alias: localhost
client certificate: /home/jboss/wildfly-24.0.0.Final/standalone/config
uration/client.crt
trust store file: management.truststore
trust store password: secret
Server keystore file management.keystore, certificate file management.pem and management.csr file will be generated in server configuration directory.
Server truststore file management.truststore will be generated in server configuration directory.

Do you confirm y/n :y
Unable to connect due to unrecognised server certificate
Subject – CN=Unknown,OU=Unknown,O=Unknown,L=Unknown,ST=Unknown,C=Unknown
Issuer – CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown
Valid From – Fri Sep 03 14:27:48 CEST 2021
Valid To – Thu Dec 02 14:27:48 CET 2021
MD5 : e5:53:0e:99:65:9d:fa:23:b1:f2:9c:6a:d1:a4:25:2a
SHA1 : d6:60:06:5f:db:bb:13:1f:53:94:6d:ed:c3:d4:f8:7c:b5:18:78:19

Accept certificate? [N]o, [T]emporarily, [P]ermanently : P

Done with the certificate, we now need to include this information in the jboss-cli.xml file.

First, set the defaut protocol to “https-remoting“:

<default-protocol use-legacy-override="false">https-remoting</default-protocol>

Then, at the bottom of the file, specify the keystore and truststore settings, based on the information provided in the security enable-ssl-management command:

<ssl>
     <alias>localhost</alias>
     <key-store>/home/jboss/wildfly-24.0.0.Final/standalone/configuration/management.keystore</key-store>
     <key-store-password>secret</key-store-password>
     <trust-store>/home/jboss/wildfly-24.0.0.Final/standalone/configuration/management.truststore</trust-store>
     <trust-store-password>secret</trust-store-password>
     <modify-trust-store>true</modify-trust-store>
</ssl>

Now, reload your server and try connecting with the CLI:

$ ./jboss-cli.sh -c
[standalone@localhost:9993 /]

As you can see from the prompt, we are not using the secure 9993 port to connect with the CLI.

Securing WildFly Management Interfaces (legacy)

In the second part of this tutorial we will demonstrate how to secure access to the Administration console for older installations of WildFly / JBoss EAP which are not using Elytron to manage Secure Sockets Layer (SSL).

By default, the communication between the browser and the Management console happens in clear text. The only security applied is an authentication which is required before accessing the console. If you have strict security requirements, however you might need to encrypt the communication with the management console. For this purpose we will use a self-signed certificate. If you need to expose the Management console to other entities (for example outside your network) you might consider creating a Certificate Request which has to be signed by a CA.

So start by creating a keystore with the following keytool command:

keytool -genkeypair -alias serverkey -keyalg RSA -keysize 2048 -validity 7360 -keystore server.keystore -keypass mypassword -storepass mypassword -dname "cn=Server Administrator,o=Acme,c=GB"

Now copy the server.keystore under your server’s configuration folder (e.g. C:\wildfly-8.0.0.Final\standalone\configuration ).

Next, include in your ManagementRealm configuration a server-identities definition which references our keystore as follows:

<security-realm name="ManagementRealm">
    <authentication>
        <local default-user="$local"/>
        <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
    </authentication>
    <authorization map-groups-to-roles="false">
        <properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
    </authorization>
    <server-identities>
        <ssl>
            <keystore path="server.keystore" relative-to="jboss.server.config.dir" keystore-password="mypassword" alias="serverkey"/>
        </ssl>
    </server-identities>
</security-realm>

Last tweak is needed in the management-interfaces section, where you have to replace the http socket binding with an https socket binding:

<management-interfaces>
    <http-interface security-realm="ManagementRealm" http-upgrade-enabled="true">
       <!-- <socket-binding http="management-http"/> -->
              <socket-binding https="management-https"/>
    </http-interface>
</management-interfaces>

Please note that the management-https in turn references a socket binding in your configuration which is by default included:

<socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>

So, as you can see, the management console, when using https will be bound on port 9993.

Restart your server and check that the management console is available on https://localhost:9993

As you can see from the above definition, WildFly is using https as communication protocol, although it is marked as unsecure site because the certificate is not signed by a CA.