How to run WildFly and JBoss EAP through a firewall

This tutorial will teach you how to configure WildFly or JBoss EAP to run through a firewall.

Linux Machine

To configure firewall rules on a Linux machine you can use the command-line tool firewall-cmd which is part of the firewalld application, that is installed by default. It can be used to make permanent and non-permanent runtime changes.

Typically, you would allow the HTTP port to be reached from outside. So, to allow the port 8080:

sudo firewall-cmd --zone=public --add-port 8080/tcp

The above rule will not however survive a server restart. In order to make it permanent, you would need to add the –permanent parameter:

sudo firewall-cmd --zone=public --add-port 8080/tcp --permanent

Windows machine

On a Windows machine go into Control Panel and click Windows Firewall. There select “Allow an app or feature through Windows Firewall” and it will open this window:

Next, click on “Change Settings” and select the “Allow another app...” button and in the next window choose the Browse button next to the Path field.

You will need to add the WildFly service so that it can run through the Windows Firewall

 

Solving java.io.IOException: Invalid secret key format

This issue happens when a Java module is unable to access com.sun.crypto libraries earlier than its Classloader modules. You will typically see a Stack trace which looks like this:

Caused by: java.io.IOException: Invalid secret key format
        at com.sun.crypto.provider.JceKeyStore.engineLoad(JceKeyStore.java:856)
        at java.security.KeyStore.load(Unknown Source)
        at org.picketbox.util.KeyStoreUtil.getKeyStore(KeyStoreUtil.java:201)
        at org.picketbox.util.KeyStoreUtil.getKeyStore(KeyStoreUtil.java:151)
        at org.picketbox.plugins.vault.PicketBoxSecurityVault.getKeyStore(PicketBoxSecurityVault.java:688)

One way to solve this issue is to upgrade the keystore from JCEKS to PKCS12. As the JCEKS keystore uses a proprietary format is it subject to changes for example if you update your JDK.

You can use the keytool command line to migrate a keystore from JCEKS to PKCS12:

keytool -importkeystore -srckeystore [KEYSTORE.jks] -destkeystore [FILE.p12] -srcstoretype JKS - deststoretype PKCS12 -deststorepass [PASSWORD_PKCS12]

If your application requires using a JCEKS keystore, then you have to force early access to the sun.jdk libraries.

If you are using WildFly application server, then you can add into the specific module.xml file (for example the one of  “org.picketbox” module) a dependency to “sun.jdk” module:

<module name="sun.jdk"/>

Also, if you want to add visibility to the “com.sun” packages to all WildFly modules, then in your configuration script (standalone.conf), add to the JBOSS_MODULES_SYSTEM_PKGS also the “com.sun.crypto.provider” package as follows:

if [ "x$JBOSS_MODULES_SYSTEM_PKGS" = "x" ]; 
   then   JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman,com.sun.crypto.provider"
fi

 

How to enable certificate forwarding in WildFly

In this tutorial we will learn how to do client certificate authentications when WildFly is located behind a reverse proxy.

The most common use cases for reverse proxies are:

  • When the reverse proxy is located on a DMZ
  • When youe Web applications are located on a VLAN (e.g. private network).
  • When the reverse proxy reads the initial request, then it initiates a new (even if similar) request to the internal Web applications.

The problem is that, by definition, an HTTPS request’s content cannot be spied. This is why when putting a reverse proxy behind the client and the Web application, the HTTPS stream will be broken and we will loose all the client certificate data contained in the X509 client certificate .

Let’s see how to safely forward the client certificate data to the web application running on WildFly. Let’s start from the front-end, where we will set the SSL information headers:

 RequestHeader set SSL_CLIENT_CERT "%{SSL_CLIENT_CERT}s"
 RequestHeader set SSL_CIPHER "%{SSL_CIPHER}s"
 RequestHeader set SSL_SESSION_ID "%{SSL_SESSION_ID}s"
 RequestHeader set SSL_CIPHER_USEKEYSIZE "%{SSL_CIPHER_USEKEYSIZE}s"

Then, on WildFly, enable the certificate-forwarding=”true” attribute on the Undertow server:

/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=certificate-forwarding,value=true)

This will result in the following configuration:

 <subsystem xmlns="urn:jboss:domain:undertow:10.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
   <buffer-cache name="default"/>
   <server name="default-server">
      <http-listener name="default" socket-binding="http" redirect-socket="https" certificate-forwarding="true"/>

Configuring HTTP Basic Authentication with WildFly

In this tutorial we will learn how to configure HTTP basic authentication with WildFly.

Basic authentication is a simple authentication policy built into the HTTP protocol. The client sends an HTTP request with the Authorization header that contains the word Basic word followed by a space and a base64-encoded string username:password.

As an example, in order to authorize as demo / p@55w0rd the client would send

    Authorization: Basic ZGVtbzpwQDU1dzByZA==

Note: Because base64 is easily decoded, Basic authentication should only be used together with other security mechanisms such as HTTPS/SSL.

Example: enabling HTTP Basic Authentication in WildFly

In order to enable HTTP Basic Authentication in WildFly we will define a Security Realm with Elytron. The most basic example of a security realm is the FileSystem Realm, which stores the identity information on a filesystem, by paging each identity in an XML file containing credentials and Roles. Start the application server and connect to it from a CLI (jboss-cli.sh). Then execute the following commands:

# add a Simple Role Decoder which maps the application Roles from the attribute Roles in the File system.

if (outcome != success) of /subsystem=elytron/simple-role-decoder=from-roles-attribute:read-resource
     /subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)
end-if

batch

# Let’s define a new filesystem-realm named fsRealm and its respective path on the file system:
/subsystem=elytron/filesystem-realm=demoFsRealm:add(path=demofs-realm-users,relative-to=jboss.server.config.dir)

# Next, we add some identities to the Realm:
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity(identity=frank)
/subsystem=elytron/filesystem-realm=demoFsRealm:set-password(identity=frank,clear={password="password123"})
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=frank,name=Roles, value=["Admin","Guest"])

# Create a new Security Domain which maps our Realm:
/subsystem=elytron/security-domain=fsSD:add(realms=[{realm=demoFsRealm,role-decoder=from-roles-attribute}],default-realm=demoFsRealm,permission-mapper=default-permission-mapper)

# We need an Http Authentication Factory which references our Security Domain:
/subsystem=elytron/http-authentication-factory=example-fs-http-auth:add(http-server-mechanism-factory=global,security-domain=fsSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=RealmUsersRoles}]}])

# Finally, a Security Domain in the undertow’s subsystem will be associated with our Http Authentication Factory:
/subsystem=undertow/application-security-domain=FSSecurityDomain:add(http-authentication-factory=example-fs-http-auth)

run-batch

reload

Then set the security domain in “jboss-web.xml“:

<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.jboss.org/schema/jbossas
    http://www.jboss.org/schema/jbossas/jboss-web_7_2.xsd">
   <security-domain>FSSecurityDomain</security-domain>
</jboss-web>

And configure th auth-method in your web.xml:

<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">

   <login-config>
      <auth-method>BASIC</auth-method>
      <realm-name>RealmUsersRoles</realm-name>
   </login-config>
</web-app>

Our application defines a single endpoint, which has an HttpConstraint allowing access only to “Admin” users:

@WebServlet("/secure")
@ServletSecurity(@HttpConstraint(rolesAllowed = { "Admin" }))
public class SecuredServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        Principal principal = null;
        String authType = null;
        String remoteUser = null;

        principal = req.getUserPrincipal();

        remoteUser = req.getRemoteUser();

        authType = req.getAuthType();

        writer.println("servlet-security");
        writer.println("Successfully called Secured Servlet");
        writer.println("Principal : " + principal.getName() + ");
        writer.println("Remote User : " + remoteUser + ");
        writer.println("Authentication Type : " + authType + ");
        writer.println("");
        writer.close();
    }

}

Let’s send a request to the secured endpoint:

curl -u frank:password123 http://localhost:8080/http-basic/secure
Successfully called Secured Servlet 
Principal  : frank
Remote User : frank
Authentication Type : BASIC

You can find the souce code for this example here: https://github.com/fmarchioni/mastertheboss/tree/master/security/http-basic

Setting HTTP Basic authentication for Web Services

In case you are using EJB-based Web Services the configuration is slightly different; because the security domain is not specified into the web descriptors, we have to provide it by means of annotations:

@Stateless
@WebService(targetNamespace = "https://www.mastertheboss.com/",
serviceName = "SecureService")
@WebContext(authMethod = "BASIC",
secureWSDLAccess = false)
@SecurityDomain(value = "other")
public class SecureEJB { }

As you can see, the @WebContext annotation basically reflects the same configuration options of POJO-based Web Services, with BASIC authentication and unrestricted WSDL access.

Basic authentication using Database

If you want to learn how to configure authentication using a Database instead of a property file, check this tutorial: Configure an Elytron JDBC Realm on WildFly

SSL secured EJBs with Elytron

Elytron security framework enables developers to use an unified security infrastructure to authenticate/authorize your business methods but also to encrypt the communication. In this tutorial we will see how to do that both in an EJB client application which uses remote+https to secure each remote method call.

The starting point of this tutorial is a client-server EJB application which is made up of the following EJB implementations:

package com.itbuzzpress.chapter16.ejb;

import java.util.concurrent.Future;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.*;

import com.itbuzzpress.chapter16.exception.InsufficientFundsException;
 

@Stateful
@Remote(Account.class)
@RolesAllowed("employee")
@org.jboss.ejb3.annotation.SecurityDomain("other")
public class AccountEJB implements Account {

    long money;
 
    
	@Override
	public long getMoney() {
		return money;

	}

	public void createAccount(long amount)  
	{
		this.money= amount;
		 
	}

	@Override
	public void deposit(long amount)  
	{
		 
		this.money+= amount;
			 
		System.out.println("Money deposit. total is "+money);
	}
 
	 
	@Override
	public void withdraw(long amount) throws InsufficientFundsException {
		
		long newAmount = money - amount;
		if (newAmount < 0) {
			throw new InsufficientFundsException("Unsufficient funds for account! ");
		}
		
		money = newAmount;	 
		System.out.println("Money withdrawal. total is "+money);

	}
}
package com.itbuzzpress.chapter16.ejb;

import java.util.List;
import java.util.Timer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;

@Stateless
@Remote(Calculator.class)
@RolesAllowed("employee")
@org.jboss.ejb3.annotation.SecurityDomain("other")
public class CalculatorEJB implements Calculator {
   
	float interest=5;
 
	@Override
	public float calculateInterest(long money) {
	 
	    return money * (1+ (interest/100));
	    
	   
   }
	
	 
	
}

So as you could see, both EJBs are secured using the @javax.annotation.security.RolesAllowed annotation which states that only members of “employee” group are allowed. Also the @org.jboss.ejb3.annotation.SecurityDomain is the default one, that is “other”.

We will now develop a simple Remote EJB Client to access this application:

package com.itbuzzpress.chapter16.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.itbuzzpress.chapter16.ejb.Account;
import com.itbuzzpress.chapter16.ejb.Calculator;
import com.itbuzzpress.chapter16.exception.InsufficientFundsException;

import java.util.Hashtable;

public class RemoteEJBClient {

	public static void main(String[] args) throws Exception {

		Account account = lookupAccountEJB();
		Calculator calculator = lookupCalculatorEJB();
		System.out.println("Going to deposit 1000$ ");

		account.createAccount(1000l);

		account.deposit(250);

		try {
			System.out.println("Going to withdraw 500$ ");
			account.withdraw(500);
		} catch (InsufficientFundsException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		long money = account.getMoney();
		System.out.println("Money left " + money);
		float totalMoney = calculator.calculateInterest(money);
		System.out.println("Money plus interests " + totalMoney);

	}

	private static Account lookupAccountEJB() throws NamingException {
		final Hashtable jndiProperties = new Hashtable();
                jndiProperties.put(Context.PROVIDER_URL, "remote+https://127.0.0.1:8443");
                jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");

		final Context context = new InitialContext(jndiProperties);

		return (Account) context
				.lookup("ejb:/javaee7-ejb-server-ssl-elytron/AccountEJB!com.itbuzzpress.chapter16.ejb.Account?stateful");
	}

	private static Calculator lookupCalculatorEJB() throws NamingException {
		final Hashtable jndiProperties = new Hashtable();
                jndiProperties.put(Context.PROVIDER_URL, "remote+https://127.0.0.1:8443");
                jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
		final Context context = new InitialContext(jndiProperties);

		return (Calculator) context
				.lookup("ejb:/javaee7-ejb-server-ssl-elytron/CalculatorEJB!com.itbuzzpress.chapter16.ejb.Calculator");
	}
}

The most interesting lines are highlighted. As you can see, we are using “remote+https” as protocol on the port 8443. Another valid option is using “remoting+ssl” connector on port 4448.

Also, please note that the Context.INITIAL_CONTEXT_FACTORY is now org.wildfly.naming.client.WildFlyInitialContextFactory which differes from older WildFly versions.

Next step will be creating client and server certificates for this example.

Creating keystores and truststores

In order to perform client-server mutual authentication we will create the server and client certificates and import the client public key in the server truststore and the server public key into the client trustore.

Let’s start from creating the server keystore:

keytool -genkey -v -alias jbossalias -keyalg RSA -keysize 1024 -keystore server.keystore -validity 3650 -keypass 123456 -storepass 123456 -dname "cn=Server Administrator,o=Acme,c=GB" 

Now we will export the Server’s Public Key in a file named server.cer

keytool -export -keystore server.keystore -alias jbossalias -file server.cer -keypass 123456 -storepass 123456

Then we export Client Key Store into the file client.keystore

keytool -genkey -v -alias clientalias -keyalg RSA -keysize 1024 -keystore client.keystore -validity 3650 -keypass abcdef -storepass abcdef -dname "cn=Server Administrator,o=Acme,c=GB" 

Now we will be exporting the Client’s Public Key in the file client.cer

keytool -export -keystore client.keystore -alias clientalias -file client.cer -keypass abcdef -storepass abcdef

We are almost done. Now we import the Client’s Public key into server’s truststore

keytool -import -v -trustcacerts -alias clientalias -file client.cer -keystore server.truststore -keypass 123456 -storepass 123456

Last, we will be importing the Server’s Public key into client’s truststore:

keytool -import -v -trustcacerts -alias jbossalias -file server.cer -keystore client.truststore -keypass abcdef -storepass abcdef

Now copy the keystore and trustore files into the JBOSS_HOME/standalone/configuration folder and you are done.

Changes in the server configuration

Done with the server and client certificates, we will create the elytron key-store, key-manager, trust-manager and server-ssl-context to store reference the client and server certificates:

/subsystem=elytron/key-store=server-key-store:add(path=server.keystore, relative-to=jboss.server.config.dir, credential-reference={clear-text=123456}, type=JKS)
/subsystem=elytron/key-store=server-trust-store:add(path=server.truststore, relative-to=jboss.server.config.dir, credential-reference={clear-text=123456}, type=JKS)
/subsystem=elytron/key-manager=example-key-manager:add(key-store=server-key-store, credential-reference={clear-text=123456})
/subsystem=elytron/trust-manager=example-trust-manager:add(key-store=server-trust-store)
/subsystem=elytron/server-ssl-context=example-ssl-context:add(trust-manager=example-trust-manager, key-manager=example-key-manager, need-client-auth=true, want-client-auth=true)
/subsystem=ejb3/application-security-domain=other:add(security-domain=ApplicationDomain)

We are almost done. Before deploying running the client application we need to provide the authentication mechanism and users which will connect to the remote EJB. Also, as we didn’t specify in the client application the System Properties for the client-keystore and client-trustore, we will specify it in the wildfly-config.xml file placed in the src/main/resources/META-INF folder of your client application:

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="default"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
	        <sasl-mechanism-selector selector="DIGEST-MD5"/>
                <set-user-name name="francesco"/>
                <credentials>
                    <clear-password password="Password1!"/>
                </credentials>
            </configuration>
        </authentication-configurations>
        <key-stores>
            <key-store name="client-keystore" type="JKS">
                <file name="/home/jboss/wildfly-12.0.0.Final/standalone/configuration/client.keystore"/>
                <key-store-clear-password password="abcdef"/>
            </key-store>
            <key-store name="client-truststore" type="JKS">
                <file name="/home/jboss/wildfly-12.0.0.Final/standalone/configuration/client.truststore"/>
            </key-store>
        </key-stores>
        <ssl-contexts>
            <ssl-context name="client-ssl-context">
                <trust-store key-store-name="client-truststore"/>
                <key-store-ssl-certificate key-store-name="client-keystore" alias="clientalias">
                    <key-store-clear-password password="abcdef"/>
                </key-store-ssl-certificate>
            </ssl-context>
        </ssl-contexts>
        <ssl-context-rules>
            <rule use-ssl-context="client-ssl-context"/>
        </ssl-context-rules>
    </authentication-client>
</configuration>

So the user “francesco” with password “Password1!” will be used to connect remotely. We will add this user into the “other” security domain by simply using the add-user.sh script:

$ add-user.sh -a -g employee -u francesco -p Password1!

The full source code is available on: https://github.com/fmarchioni/practical-javaee7-development-wildfly/tree/javaee8/code/chapter16/javaee7-ejb-elytron-ssl

Within the folder https://github.com/fmarchioni/practical-javaee7-development-wildfly/tree/javaee8/code/chapter16/javaee7-ejb-elytron-ssl/ssl you will find also the scripts to configure elytron subsystem using the default Application Realm or a more advanced example using a custom FileSystem Security Realm.

Securing EJBs with Elytron

This tutorial will demonstrate how you can configure Authentication/Authorization for your EJBs on WildFly using Elytron Security Framework.

Two core authentication factories are provided as part of Elytron security framework:

  • HTTP Authentication Factory which is obviously used for Web applications performing HTTP Authentication
  • SASL Authentication Factory which is used for other network protocols, including standard protocols such as LDAP, IMAP, etc., but also JBoss Remoting which is the EJB primary transport.

We will learn now how to deploy a FileSystem Realm to be used as repository for identities that will authenticate in an EJB application.

First of all, connect to the Command Line Interface of the application server.

$ ./jboss-cli.sh -c

Now let’s define the new File System Realm through the CLI as follows:

/subsystem=elytron/filesystem-realm=demoFsRealm:add(path=demofs-realm-users,relative-to=jboss.server.config.dir)

Once created the File System Realm called “demoFSRealm” we will add one Identity to it:

/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity(identity=ejbuser)
/subsystem=elytron/filesystem-realm=demoFsRealm:set-password(identity=ejbuser,clear={password="password123"})
/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=ejbuser,name=Roles, value=["guest","manager"])

The roles needed by the application will be collected from the “Roles” attribute:

/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)

Done with the Realm, we will create a Security Domain which is bound to this Realm:

/subsystem="elytron"/security-domain="fsSD":add(default-realm="demoFsRealm",permission-mapper="default-permission-mapper",realms=[{realm="demoFsRealm",role-decoder="from-roles-attribute"},{realm="local"}])

As we will be using SASL as authentication mechanism, we will need a SASL Authentication Factory for it:

/subsystem="elytron"/sasl-authentication-factory="fs-application-sasl-authentication":add(mechanism-configurations=[{mechanism-name="JBOSS-LOCAL-USER",realm-mapper="local"},{mechanism-name="DIGEST-MD5",mechanism-realm-configurations=[{realm-name="demoFsRealm"}]}],sasl-server-factory="configured",security-domain="fsSD")

Done with Elytron, the last steps needed are adding a reference to Elytron Security Domain in the “other” Security Domain contained in the “ejb3” subsystem:

/subsystem=ejb3/application-security-domain=other:add(security-domain=fsSD)

Finally, as the EJB call initially lands on the HTTP Connector, we will add a reference to the SASL authentication factory we have created:

/subsystem=remoting/http-connector=http-remoting-connector:write-attribute(name=sasl-authentication-factory,value=fs-application-sasl-authentication)

Configuring the EJB Server Side

Done with the server side, we will configure our EJBs to use the “other ” Security Domain (which in turn references Elytron’s “fsSD” Security Domain:

import java.security.Principal;

import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

import org.jboss.ejb3.annotation.SecurityDomain;

 
@Stateless
@Remote(SecuredEJBRemote.class)
@RolesAllowed({ "guest" })
@SecurityDomain("other")
public class SecuredEJB implements SecuredEJBRemote {

    // Inject the Session Context
    @Resource
    private SessionContext ctx;

 
    public String getSecurityInfo() {
 
        Principal principal = ctx.getCallerPrincipal();
        return principal.toString();
    }

    @RolesAllowed("manager")
    public boolean secured() {
        return true;
    }
}

As you can see, two “guest” Role is required to access this class but for the “secured” method which requires a manager role.

Configuring the EJB Client side

From the EJB Client side, the only requirement is to provide a reference to the Authentication Configuration, by specifying the Users that are configured in the Realm:

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="default-config"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default-config">
                <set-user-name name="ejbuser"/>
                <credentials>
                    <clear-password password="password123"/>
                </credentials>
                <sasl-mechanism-selector selector="DIGEST-MD5"/>
                <providers>
                    <use-service-loader />
                </providers>
            </configuration>
        </authentication-configurations>
    </authentication-client>
</configuration>

The Client code is straightforward: the basic getSecurityInfo() will be available to anyone with the “guest” role, while the secured() will be available just for the “manager” role.

import java.util.Hashtable;

import javax.ejb.EJBAccessException;
import javax.naming.Context;
import javax.naming.InitialContext;

 
public class RemoteEJBClient {

    public static void main(String[] args) throws Exception {

        final Hashtable jndiProperties = new Hashtable();
 
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "remote+http://localhost:8080");
        final Context context = new InitialContext(jndiProperties);

        SecuredEJBRemote reference = (SecuredEJBRemote) context.lookup("ejb:/ejb-remote-server/SecuredEJB!"
                + SecuredEJBRemote.class.getName());

        System.out.println("Successfully called secured bean, caller principal " + reference.getSecurityInfo());
        boolean hasFullPermission = false;
        try {
            hasFullPermission = reference.secured();
        } catch (EJBAccessException e) {
        }
        System.out.println("\nPrincipal has admin permission: " + hasFullPermission);
        
    }

}

If you configured (or if you update) the Realm to have both Roles, then you would be able to call both methods successfully:

/subsystem=elytron/filesystem-realm=demoFsRealm:add-identity-attribute(identity=ejbuser,name=Roles, value=["guest","manager"])

How to configure SSL/HTTPS on WildFly

This is a complete tutorial about configuring SSL/HTTPS support for JBoss EAP / WildFly application server.

Generally speaking, to configure SSL/HTTPS you can either use the pure JSSE implementation (and the keytool utility) or a native implementation based on OpenSSL. We will cover at first the JSSE implementation with keytool. Later we will show how to enable OpenSSL on WildFly 11 or newer.

Create server and client certificates

The keytool utility stores the keys and certificates in a file termed as keystore, a repository of certificates used for identifying a client or a server. Typically, a keystore contains one client or one server’s identity, which are protected by using a password.

You can create a certificate for your server using the following command:

$  keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -validity 365 -keystore server.keystore -dname "cn=Server Administrator,o=Acme,c=GB" -keypass secret -storepass secret

This command created the keystore named server.keystore in the working directory, with the password “secret” . It generates a public/private key pair for the entity whose “distinguished name” has a common name of Server Administrator , organization of Acme and two-letter country code of GB.

TIP: The message “The JKS keystore uses a proprietary format” simply reminds you that JKS is a format specific to Java. PKCS12, on the other hand, is the standardized and language-neutral way of storing encrypted private keys and certificates. You can generate a PKCS12 format using the option -storetype PKCS12 or importing the JKS file into a PKCS12 one with the following command:

keytool -importkeystore -srckeystore server.keystore -destkeystore server.keystore -deststoretype pkcs12

Now let’s store the server keystore into the configuration folder of the application server:

$ cp server.keystore $JBOSS_HOME/standalone/configuration

If you only need a one-way authentication (Server–>Client) then you are done.

Otherwise, if you need a two-way authentication (Server<–>Client) then we need to create as well the client certificates and export them to create a truststore.

The following command, will create the client certificate, which is used to authenticate against the server when accessing a resource through SSL:

$ keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -validity 365 -keystore client.keystore -dname "CN=client" -keypass secret -storepass secret

Now export both the client and the server keystores in a certificate file:

$ keytool -exportcert  -keystore server.keystore -alias localhost -keypass secret -storepass secret -file server.crt
 
$ keytool -exportcert  -keystore client.keystore -alias client -keypass secret -storepass secret -file client.crt

Finally, import the certificates into the server’s and client’s truststores:

$ keytool -importcert -keystore server.truststore -storepass secret -alias client -trustcacerts -file client.crt -noprompt
 
$ keytool -importcert -keystore client.truststore -storepass secret -alias localhost -trustcacerts -file server.crt -noprompt

The above commands are contained in a shell script which is available at: http://bit.ly/2GB4PKV

Done with certificates, we will be storing as well the the client.truststore into the configuration folder of the application server:

$ cp client.truststore $JBOSS_HOME/standalone/configuration

Configuring One-Way SSL / HTTPS for WildFly applications

When using WildFly 11 or newer you can either use Elytron or the Legacy SSL configuration.

To verify which one is the default one check and see if the https-listener is configured to use a legacy security realm for its SSL configuration:

/subsystem=undertow/server=default-server/https-listener=https:read-attribute(name=security-realm)
{
    "outcome" => "success",
    "result" => "ApplicationRealm"
}

The above command shows that the https-listener is configured to use the legacy ApplicationRealm for its SSL configuration. Therefore we will undefine the security-realm attribute in the https-listener as Undertow cannot reference both a legacy security realm and an ssl-context in Elytron.

The following CLI batch script will add the keystores the key manager and the ssl context configuration in the elytron subsystem, removing the reference to the Undertow’s https-listener.

batch
# Configure Server Keystore 
/subsystem=elytron/key-store=demoKeyStore:add(path=server.keystore,relative-to=jboss.server.config.dir, credential-reference={clear-text=secret},type=JKS)
# Server Keystore credentials   
/subsystem=elytron/key-manager=demoKeyManager:add(key-store=demoKeyStore,credential-reference={clear-text=secret})
# Server keystore Protocols   
/subsystem=elytron/server-ssl-context=demoSSLContext:add(key-manager=demoKeyManager,protocols=["TLSv1.2"])  
# This is only needed if WildFly uses by default the Legacy security realm
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)
# Store SSL Context information in undertow 
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=demoSSLContext)

run-batch 

reload

TIP: You can also define a default SSL Context to be used by the Elytron subsystem, by setting the property default-ssl-context that will reference to the SSLContext which should be globally registered as the default.

You can find the above CLI script at: http://bit.ly/2FGCMNg

That’s all. Now if you try to access a Web application through the https://localhost:8443 address, you will be informed that you are using a self-signed certificate. If you add an exception to the browser you will be running through the SSL channel, with your certificate. You can also dump your SSL session from the command line using openssl tool as follows:

$ openssl s_client -showcerts -connect localhost:8443

CONNECTED(00000003)
Can't use SSL_get_servername
depth=0 C = GB, O = Acme, CN = Server Administrator
verify error:num=18:self signed certificate
verify return:1
depth=0 C = GB, O = Acme, CN = Server Administrator
verify return:1
---
Certificate chain
 0 s:C = GB, O = Acme, CN = Server Administrator
   i:C = GB, O = Acme, CN = Server Administrator
-----BEGIN CERTIFICATE-----
MIIDFTCCAf2gAwIBAgIEYWcexzANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJH
QjENMAsGA1UEChMEQWNtZTEdMBsGA1UEAxMUU2VydmVyIEFkbWluaXN0cmF0b3Iw
HhcNMjEwMjI3MTEzNzQ3WhcNMjIwMjI3MTEzNzQ3WjA7MQswCQYDVQQGEwJHQjEN
MAsGA1UEChMEQWNtZTEdMBsGA1UEAxMUU2VydmVyIEFkbWluaXN0cmF0b3IwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtBmaeo7l2/CaW+FPY5UZOnlOY
Pg4xQtr5SHIR7mF3Q5+jfxi4trGWia0yRd6ae0pBF3tyVQAf/C7gZDPRczxnshoc
TQD/7vtEJcUJ5N7k+DXqoFtn7vkXyFpvilSVT1PCnlkuAXD9jZnDhRl8xfs16pVQ
Sm+NxOGFLlsLr5iJWsGHSLmRqTrGPcY1aPbXAahGLtqE/rPwVGt4nXhbKV9s3tsD
LDQHOfsbbm8qbxmR47IdMR5XNx8vhJt+fhB/ZVgoFUw+CVMmEa8PfNxWY/fDFiPU
ePv3/ZRzagLfnlpN+vVZTaZsJUHysumfEhlpRjiqPWLXlk3SVfIboL6nrPfBAgMB
AAGjITAfMB0GA1UdDgQWBBRy5STkvTGZuddpCBPR+CcBXHzVXzANBgkqhkiG9w0B
AQsFAAOCAQEAc0NYtsjBBueHjc6hgeMZmBqq7agZ7IkRSNrHQTUtznYAD/K3aZU9
G4NbxZ+weHpsaJc9qg0zmdDjLgSmYOAR87OU0OAAXf47h2ZUMQIpdmJMf/swBXsH
/QmWS6CBmaGGm6ND29mNyzdRDwYbeG45j2DYZI9a1TK2x3WLS0H5on4qRcqUWElW
iItwR22QlKrWdiBcl0jSxxJNPdFU0DSCGNwJqByYalS6Z0d+AQG6y7BMIsdyLi06
zNP+8auhvex5LmUx8tEWO4pxhLe+G4iVm6XXVUkgw+6tBwDLzpaL+eL/EQZZWLbP
WrrpXVlYc/kuxq4iJtTFWEMCZEnzWblyYA==
-----END CERTIFICATE-----
---
Server certificate
subject=C = GB, O = Acme, CN = Server Administrator
issuer=C = GB, O = Acme, CN = Server Administrator

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1281 bytes and written 429 bytes
Verification error: self signed certificate
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: DFB40F6008C4BF507807DFBBDFD3E937195595E191221631EF20F18296C6F845
    Session-ID-ctx: 
    Master-Key: 87A7C384BE6F232D63903BC18CD385A6F29216CA1D0F281E9976785D89C990BE92E5B0F4C20618619B416407F6D24055
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    Start Time: 1614426182
    Timeout   : 7200 (sec)
    Verify return code: 18 (self signed certificate)
    Extended master secret: yes
---

In terms of configuration, here is the tls section which has been added to WildFly:

<tls>
    <key-stores>
        <key-store name="demoKeyStore">
        <credential-reference clear-text="secret"/>
        <implementation type="JKS"/>
        <file path="server.keystore" relative-to="jboss.server.config.dir"/>
        </key-store>
    </key-stores>
    <key-managers>
        <key-manager name="demoKeyManager" key-store="demoKeyStore">
        <credential-reference clear-text="secret"/>
        </key-manager>
    </key-managers>
    <server-ssl-contexts>
        <server-ssl-context name="demoSSLContext" protocols="TLSv1.2" key-manager="demoKeyManager"/>
    </server-ssl-contexts>
</tls>

And here is the corresponding undertow section:

 <subsystem xmlns="urn:jboss:domain:undertow:10.0" default-server="default-server" default-virtual-host="default-host" default-servlet-container="default" default-security-domain="other" statistics-enabled="${wildfly.undertow.statistics-enabled:${wildfly.statistics-enabled:false}}">
          <buffer-cache name="default"/>
          <server name="default-server">
              <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
              <https-listener name="https" socket-binding="https" ssl-context="demoSSLContext" enable-http2="true"/>
              <host name="default-host" alias="localhost">
                  <location name="/" handler="welcome-content"/>
                  <filter-ref name="server-header"/>
                  <filter-ref name="x-powered-by-header"/>
                  <http-invoker security-realm="ApplicationRealm"/>
              </host>
          </server>

Configuring Mutual SSL Authentication for WildFly applications

Mutual SSL provides the same security as SSL, with the addition of authentication and non-repudiation of the client authentication, using digital signatures. When mutual authentication is used, the server would request the client to provide a certificate in addition to the server certificate issued to the client. Mutual authentication requires an extra round trip time for client certificate exchange. In addition, the client must buy and maintain a digital certificate. We can secure our war application deployed over WildFly with mutual(two-way) client certificate authentication and provide access permissions or privileges to legitimate users.

IMPORTANT: It is assumed that you have already completed the One-Way SSL configuration for the server as discussed earlier in this tutorial!

In order to update your One-Way configuration to use Mutual SSL configuration, we need a SSL Context which includes also the Client Truststore and TrustManager in its configuration:

batch
 
# Add the Truststore, TrustManager to a SSL Context configuration
/subsystem=elytron/key-store=demoTrustStore:add(path=client.truststore,relative-to=jboss.server.config.dir,type=JKS,credential-reference={clear-text=secret})

/subsystem=elytron/trust-manager=demoTrustManager:add(key-store=demoTrustStore)

/subsystem=elytron/server-ssl-context=twoWaySSL:add(key-manager=demoKeyManager,trust-manager=demoTrustManager,protocols=[TLSv1.2],need-client-auth=true)

# This is only needed if WildFly uses by default the Legacy security realm 
/subsystem=undertow/server=default-server/https-listener=https:undefine-attribute(name=security-realm)

# Store SSL Context information in undertow 
/subsystem=undertow/server=default-server/https-listener=https:write-attribute(name=ssl-context,value=twoWaySSL)
 
run-batch

reload

Using the CLI security command to configure One-Way HTTPS

If you prefer, a simpler way to enable SSL for the HTTP server is by means of the **security enable-ssl-http-server** CLI command. This command has the advantage of combining the definition of the key-store, key-manager and ssl-context in just one command. So, assumed that the file server.keystore is in the same folder as the jboss-cli, you can enable SSL for the HTTP server as follows:

[standalone@localhost:9990 /] security enable-ssl-http-server --key-store-path=server.keystore --key-store-password=secret 

Server reloaded. SSL enabled for default-server ssl-context is ssl-context-server.keystore key-manager is key-manager-server.keystore key-store is server.keystore

Your One-Way SSL configuration is ready and the server has been reloaded for you to reflect the changes. You can also use the **–interactive** option which will let you create also the keystore. Here is a transcript of a sample ssl configuration for the HTTP server which will eventually create the file wildfly.keystore, the certificate file wildfly.pem and wildfly.csr file in server configuration directory:

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

Certificate info:
Key-store file name (default default-server.keystore): wildfly.keystore
Password (blank generated): password
What is your first and last name? [Unknown]: John Smith
What is the name of your organizational unit? [Unknown]: QA
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=John Smith, OU=QA, O=Acme, L=London, ST=Unknown, C=UK correct y/n [y]?y
Validity (in days, blank default): 
Alias (blank generated): jsmith
Enable SSL Mutual Authentication y/n (blank n):n

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

Do you confirm y/n :y
Server reloaded.
SSL enabled for default-server
ssl-context is ssl-context-24f3d44b-a511-4b54-9610-ac414a8b6143
key-manager is key-manager-24f3d44b-a511-4b54-9610-ac414a8b6143
key-store   is key-store-24f3d44b-a511-4b54-9610-ac414a8b6143

Configuring OpenSSL as provider

When using Elytron for SSL/HTTPS you can opt for two different providers:

<providers>
        <aggregate-providers name="combined-providers">
            <providers name="openssl"/>
            <providers name="elytron"/>
        </aggregate-providers>
        <provider-loader name="elytron" module="org.wildfly.security.elytron"/>
        <provider-loader name="openssl" module="org.wildfly.openssl"/>
</providers> 

In order to switch to OpenSSL, at first set your security Realm to use OpenSSL TSL as protocol:

/core-service=management/security-realm=ApplicationRealm/server-identity=ssl:write-attribute(name=protocol, value=openssl.TLS)

This will show in your console that the OpenSSL protocol has been loaded:

12:08:00,027 INFO  [org.wildfly.openssl.SSL] (MSC service thread 1-7) WFOPENSSL0002 OpenSSL Version OpenSSL 1.0.2j-fips  26 Sep 2016

Next, we need to change the ordering of the providers in the elytron combined-providers, which means that OpenSSL will now take precedence:

/subsystem=elytron/aggregate-providers=combined-providers:list-add(index=0, name=providers, value=openssl)
/subsystem=elytron/aggregate-providers=combined-providers:list-remove(index=2, name=providers)

Configuring SSL using Legacy mode

If you are using an earlier version of WildFly (8/9/10) or you simply don’t want to use Elytron as security mechanism, you can still use the Legacy security for securing your HTTP communication.

Start by creating a Security Realm which will contain the keystore and trustore references

/core-service=management/security-realm=SSLRealm:add

Next, for one-way SSL, set the path to the keystore, along with the keystore path and password:

/core-service=management/security-realm=SSLRealm/server-identity=ssl:add(keystore-path="server.keystore", keystore-relative-to="jboss.server.config.dir", keystore-password="secret")

If you are using mutual authentication, you will need to set the path to the truststore, along with the its path and password:

/core-service=management/security-realm=SSLRealm/authentication=truststore:add(keystore-password="secret",keystore-path="server.truststore",keystore-relative-to="jboss.server.config.dir")

Finally, set the value of Undertow’s https listener to your Security Realm:

/subsystem=undertow/server=default-server/https-listener=default-https:write-attribute(name=security-realm,value=SSLRealm)

Conclusion

This tutorial has covered how to configure TLS/SSL on WildFly application server. If you want to use the latest version of TLS (1.3), you can continue reading here: Configuring TLS 1.3 on WildFly application Server

On the other hand, if you want to configure TLS/SSL for WildFly Management interfaces, check out this tutorial: Securing JBoss / WildFly Management Interfaces: the easy way

How to configure an Elytron LDAP Realm on WildFly

In this tutorial we will learn how to configure Authentication with an Elytron LDAP-Based Identity Store on the top of WildFly application Server

Hard requirements

  • WildFly 11 or newer
  • A LDAP Server or a Docker deamon to start an LDAP Server in a Container

For the sake of simplicity, we will start a Containerised version of OpenLdap, which is available in the DockerHub, using as BASE DN wildfly.org:

$ docker run --env LDAP_ORGANISATION="wildfly" --env LDAP_DOMAIN="wildfly.org" --env LDAP_ADMIN_PASSWORD="admin" --detach osixia/openldap

As an alternative, you can set the BASE DN in your slapf.conf and set the default admin password.

Check your IP Address from your container:

$ docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(docker ps -q)
172.17.0.2

Now verify the connection with any LDAP Browser:

Ok, the connection worked so now upload a sample ldif file which will contain one user named “frank” which is granted the Role “Admin”:

dn: ou=Users,dc=wildfly,dc=org
objectClass: organizationalUnit
objectClass: top
ou: Users

dn: uid=frank,ou=Users,dc=wildfly,dc=org
objectClass: top
objectClass: person
objectClass: inetOrgPerson
cn: Franky
sn: frank
uid: frank
userPassword: secret123

dn: ou=Roles,dc=wildfly,dc=org
objectclass: top
objectclass: organizationalUnit
ou: Roles

dn: cn=Admin,ou=Roles,dc=wildfly,dc=org
objectClass: top
objectClass: groupOfNames
cn: Admin
member: uid=frank,ou=Users,dc=wildfly,dc=org

You should be able to see the updated Directory from your LDAP Browser:

Configuring the Elytron LDAP Realm

Firstly, start WildFly and connect to the Command Line Interface.

The first thing we will need to do is configuring a Directory Context with the URL of the LDAP Server and the information related to the Principal:

/subsystem=elytron/dir-context=exampleDC:add(url="ldap://172.17.0.2:389",principal="cn=admin,dc=wildfly,dc=org",credential-reference={clear-text="secret"})

Next, it’s time to create an LDAP Realm which references the Directory Context, specifying the Search Base DN, how and Users are mapped:

/subsystem=elytron/ldap-realm=exampleLR:add(dir-context=exampleDC,identity-mapping={search-base-dn="ou=Users,dc=wildfly,dc=org",rdn-identifier="uid",user-password-mapper={from="userPassword"},attribute-mapping=[{filter-base-dn="ou=Roles,dc=wildfly,dc=org",filter="(&(objectClass=groupOfNames)(member={1}))",from="cn",to="Roles"}]})

Next, you will need creating a Role Decoder which, in its simplest form, takes a single attribute and maps it directly to roles.

/subsystem=elytron/simple-role-decoder=from-roles-attribute:add(attribute=Roles)

Ok, we have the LDAP Realm, the Role Decoder so we will create a Security Domain which uses this information:

/subsystem=elytron/security-domain=exampleLdapSD:add(realms=[{realm=exampleLR,role-decoder=from-roles-attribute}],default-realm=exampleLR,permission-mapper=default-permission-mapper)

As the Security Domain will be used to authenticate users through HTTP, we will need to add an Http Authentication Factory which is configured to use the above defined Security Domain and LDAP Realm:

/subsystem=elytron/http-authentication-factory=example-ldap-http-auth:add(http-server-mechanism-factory=global,security-domain=exampleLdapSD,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=myApplicationDomain}]}])

We are finished with Elytron. The last piece of the puzzle will be the Http Authentication Factory into Undertow, so that incoming request will be handled by the Security Domain:

/subsystem=undertow/application-security-domain=myApplicationDomain:add(http-authentication-factory=example-ldap-http-auth)

We are done! The above CLI command should reflect in the following server configuration:

<?xml version="1.0" encoding="UTF-8"?>
<subsystem xmlns="urn:wildfly:elytron:1.2" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
  
   <security-domains>
      . . .
      <security-domain name="exampleLdapSD" default-realm="exampleLR" permission-mapper="default-permission-mapper">
         <realm name="exampleLR" role-decoder="from-roles-attribute" />
      </security-domain>
   </security-domains>
   <security-realms>
       . . . . .
      <ldap-realm name="exampleLR" dir-context="exampleDC">
         <identity-mapping rdn-identifier="uid" search-base-dn="ou=Users,dc=wildfly,dc=org">
            <attribute-mapping>
               <attribute from="cn" to="Roles" filter="(&(objectClass=groupOfNames)(member={1}))" filter-base-dn="ou=Roles,dc=wildfly,dc=org" />
            </attribute-mapping>
            <user-password-mapper from="userPassword" />
         </identity-mapping>
      </ldap-realm>
   </security-realms>
   <mappers>
        . . . .
      <simple-role-decoder name="from-roles-attribute" attribute="Roles" />
   </mappers>
   <http>
      <http-authentication-factory name="management-http-authentication" http-server-mechanism-factory="global" security-domain="ManagementDomain">
       . . . .
      <http-authentication-factory name="example-ldap-http-auth" http-server-mechanism-factory="global" security-domain="exampleLdapSD">
         <mechanism-configuration>
            <mechanism mechanism-name="BASIC">
               <mechanism-realm realm-name="exampleApplicationDomain" />
            </mechanism>
         </mechanism-configuration>
      </http-authentication-factory>
      <provider-http-server-mechanism-factory name="global" />
   </http>
   <sasl>
      . . . .
   </sasl>
   <dir-contexts>
      <dir-context name="exampleDC" url="ldap://172.17.0.2:389" principal="cn=admin,dc=wildfly,dc=org">
         <credential-reference clear-text="admin" />
      </dir-context>
   </dir-contexts>
</subsystem>

Application Configuration

In order to test your application, include the Security bindings in your jboss-web.xml and web.xml.

Here is the jboss-web.xml bit:

<jboss-web>
  <security-domain>myApplicationDomain</security-domain>
</jboss-web>

And this is a sample web.xml:

<web-app>
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>secure</web-resource-name>
      <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>Admin</role-name>
    </auth-constraint>
  </security-constraint>
  <security-role>
    <description>Ldap Secured</description>
    <role-name>Admin</role-name>
  </security-role>
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>myApplicationDomain</realm-name>
  </login-config>
</web-app>

How to configure an Elytron JDBC Realm on WildFly

In this tutorial we will learn how to configure an Elytron JDBC Realm on WildFly 11 using enterely the Web console of the application server.

WildFly 11 has much improved its Web console. As proof of concept, we will show how to configure JDBCSecurity realm using enterely the Web console against a MySQL Database.

Prerequisites

  • A MySQL Database
  • A WildFly 11 installation

The first thing we will do is creating a Datasource which will connect to an existing MySQL Database

If you don’t have an available MySQL Database, you can easily complete this tutorial by starting MySQL as Docker container:

$ docker run -d --name mysql -e MYSQL_USER=mysql -e MYSQL_PASSWORD=mysql -e MYSQL_DATABASE=demodb -e MYSQL_ROOT_PASSWORD=secret mysql

Now move to the WildFly 11 console and Create a new Datasource using the DataSource wizard as in the following example.

Choose the Database:

Enter the DataSource Attributes:

Pickup the Driver to be used (in our example we have deployed the MySQL Driver)

Finally, complete the Datasource configuration entering the Connection Settings

Configuring Elytron JDBC Realm

Next step will be configuring the JDBC Realm for Elytron.

A JDBCSecurity realm is a security realm implementation backed by a database based whose implementation is the class org.wildfly.security.auth.realm.jdbc.JdbcSecurityRealm

Move in the Configuration > Subsystems > Security – Elytron window:

Click on the Add button. You will need to define a JDBC Realm as in the following picture. The JDBC Realm needs to be bound against the MySQLDS Datasource we have created.

WildFly 11 Web console is able to autocomplete the text field which reference another element of the configuration. For example, just click Arrow-Down key on the Principal Query Datasource and you will be able to browse across the available Datasources

As a result, you should have now a JDBC Realm available in your configuration:

Now we need adding an Elytron Security Domain to reference our JDBC Realm. Move into the Configuration > Subsystems > Security – Elytron > Settings: Other window:

Click on Add and complete the Security Domain UI with the Name and Realm name:

As a result, you should be able to see the “jdbcdomain” in your SecurityDomain window:

Now we will need to add a new HTTP Server authentication mechanism in your Elytron configuration. Move into the Configuration > Subsystems > Security – Elytron > Settings: Factory/Transformer window:

Click on Add and define a new HTTP Authentication based on the “global” HTTP server mechanism factory and the “jdbcdomain”:

Now the last step will be registering the Security Domain into Undertow web server. Move into the Configuration > Subsystems > Web/HTTP – Undertow > HTTP window and select the Application security Tab:

Click on Add. Bind the Security Domain the the Http autentication Factory we have created on the Elytron subsystem:

 

Now if you try to deploy an application bound to the “web-security-domain” security domain:

<jboss-web>
<security-domain>web-security</security-domain>
</jboss-web>

As a result, you will receive a BASIC HTTP Authentication challenge:

Creating an Elytron Security Realm for WildFly

Elytron is the new security subsytem of WildFly which will completely replace the combination of PicketBox and JAAS as the application server’s client and server security system. In this tutorial we will have an overview of it and learn how to create a sample Elytron JDBC Realm to secure applications against the database

On of the main advantages of Elytron is that it will provide an unified security solution across the application server which is now fragmented into several parts of your configuration. Elytron will provide as well support of:

  • Secure server-side authentication mechanisms based on HTTP, SASL,and TLS, as well as supporting other authentication protocols in the future
  • Support for password credential types using the standard Java cryptography extension structure (such as DES, MD5, SHA, bcrypt, and so on)

The following picture shows Elytron building blocks:

An elytron Security Realm encapsulates and integrates the application server with an identity store or repository (such as an LDAP server or a database). Hence, they are mainly responsible for obtaining or verifying credentials, obtaining the attributes associated with a given identity and last, but also important, creating an internal representation based on this information that will be used by a security domain to authenticate and perform role and permission mappings for a given principal.

By default, elytron provides some built-in implementations of security realms that are capable of managing your identities. Here are some examples:

  • JDBCSecurity realm : The class org.wildfly.security.auth.realm.jdbc.JdbcSecurityRealm is a security realm implementation backed by a database
  • LDAP security realm : The class org.wildfly.security.auth.realm.ldap.LdapSecurityRealm is a security realm implementation backed by LDAP
  • Property security realm: The class org.wildfly.security.auth.realm.LegacyPropertiesSecurityRealm is an implementation that makes use of the legacy property files
  • Java KeyStores security realm: The class org.wildfly.security.auth.realm.KeyStoreBackedSecurityRealm is a Keystore-backed security realm
  • Token security realm: The class org.wildfly.security.auth.realm.token.TokenSecurityRealm is a security realm capable of building identities based on different security token formats based on a TokenValidator

The other key block of elytron is the security domain which is the entry point to all security operations available in your server infrastructure. It contains a high-level view of security policies and resources associated with your IT domain.

The term domain should not be confused with an Internet Protocol (IP) resource. An elytron domain can be composed of a single application or multiple applications which share the same security policies.

In terms of responsibilities, a security domain is in charge of:

  • Mapping a principal to its corresponding identity on a specific security realm
  • Permission mapping
  • Role mapping
  • Obtain the current and authorized identity and all information associated with it, such as roles, permissions, and attributes

Creating a JDBC Security realm

We will present here an example of JDBC Security Realm

At the time of writing, the elytron subsystem is still not included in WildFly 10 therefore we will use a snapshot build of wildfly-11.0.0.Alpha1-SNAPSHOT which includes the standalone-elytron.xml configuration file.

Start the server as follows:

$ ./standalone.sh -c standalone-elytron.xml

The prerequisite is that you have already configured a MySQLPool Datasource which in turn references a MySQL database where user names and password are stored in the USERS table

Start by creating a new JDBC realm that refers our MySQLPool. For the sake of simplicity, we will be using a clear text password strategy.

/subsystem=elytron/jdbc-realm=jdbcRealm:add(principal-query=[{sql="SELECT passwd from USERS where login = ? ",data-source=MySQLPool,clear-password-mapper={password-index="1"}}])

/subsystem=elytron/security-domain=jdbcdomain:add(default-realm=jdbcRealm,realms=[{realm="jdbcRealm"}])

Next, since this security domain will use HTTP Basic authentication, we will define a new HTTP Server Authentication policy based on the jdbcdomain security domain and named http-db-auth:

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

In order to propagate the HTTP Authentication policy with Undertow, which governs your web applications, a component named HTTP Authenticating factory is needed. This will eventually trigger the HttpServerAuthenticationMechanism from within the Undertow web server. Here is the command needed to create an HTTP Authenticating factory:

/subsystem=undertow/application-security-domain=web-security-domain:add(http-authentication-factory=http-db-auth)

Reload your server configuration for the changes to take effect. In terms of configuration, the following XML section will be included in your elytron subsystem:

<subsystem xmlns="urn:wildfly:elytron:1.0">
    <security-domains>
        <security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="login-permission-mapper">
            <realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
        </security-domain>
        <security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="login-permission-mapper">
            <realm name="ManagementRealm" role-decoder="groups-to-roles"/>
        </security-domain>
        <security-domain name="jdbcdomain" default-realm="jdbcRealm">
            <realm name="jdbcRealm"/>
        </security-domain>
    </security-domains>
    <security-realms>
        <jdbc-realm name="jdbcRealm">
            <principal-query sql="SELECT passwd from USERS where login = ? " data-source="MySQLPool">
                <clear-password-mapper password-index="1"/>
            </principal-query>
        </jdbc-realm>
        <properties-realm name="ApplicationRealm">
            <users-properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
            <groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
        </properties-realm>
        <properties-realm name="ManagementRealm">
            <users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
            <groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
        </properties-realm>
    </security-realms>
    <mappers>
        <constant-permission-mapper name="login-permission-mapper">
            <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
        </constant-permission-mapper>
        <simple-role-decoder name="groups-to-roles" attribute="groups"/>
    </mappers>
    <http>
        <http-authentication-factory name="management-http-authentication" http-server-mechanism-factory="global" security-domain="ManagementDomain">
            <mechanism-configuration>
                <mechanism mechanism-name="BASIC">
                    <mechanism-realm realm-name="Management Realm"/>
                </mechanism>
            </mechanism-configuration>
        </http-authentication-factory>
        <http-authentication-factory name="application-http-authentication" http-server-mechanism-factory="global" security-domain="ApplicationDomain">
            <mechanism-configuration>
                <mechanism mechanism-name="BASIC">
                    <mechanism-realm realm-name="Application Realm"/>
                </mechanism>
                <mechanism mechanism-name="FORM"/>
            </mechanism-configuration>
        </http-authentication-factory>
        <http-authentication-factory name="http-db-auth" http-server-mechanism-factory="global" security-domain="jdbcdomain">
            <mechanism-configuration>
                <mechanism mechanism-name="BASIC">
                    <mechanism-realm realm-name="Secured by Elytron"/>
                </mechanism>
            </mechanism-configuration>
        </http-authentication-factory>
        <provider-http-server-mechanism-factory name="global"/>
    </http>
    <sasl>
        <sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="global" security-domain="ManagementDomain"/>
        <sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="global" security-domain="ApplicationDomain"/>
        <provider-sasl-server-factory name="global"/>
    </sasl>
</subsystem>

On the other hand, the Undertow configuration will include the following application-security-domains definition:

<subsystem xmlns="urn:jboss:domain:undertow:4.0">
    <buffer-cache name="default"/>
    <server name="default-server">
        <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
        <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
        <host name="default-host" alias="localhost">
            <location name="/" handler="welcome-content"/>
            <filter-ref name="server-header"/>
            <filter-ref name="x-powered-by-header"/>
        </host>
    </server>
    <servlet-container name="default">
        <jsp-config/>
        <websockets/>
    </servlet-container>
    <handlers>
        <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
    </handlers>
    <filters>
        <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
        <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
    </filters>
    <application-security-domains>
        <application-security-domain name="other" http-authentication-factory="application-http-authentication"/>
        <application-security-domain name="web-security-domain" http-authentication-factory="http-db-auth"/>
    </application-security-domains>
</subsystem>

Now if you try to deploy an application bound to the “web-security-domain” security domain:

<jboss-web>
<security-domain>web-security</security-domain>
</jboss-web>

As a result, you will receive a BASIC HTTP Authentication challenge:

That’s all for now. In the next tutorial we will show some more examples of Elytron Security realms. Stay tuned!