Skip navigation
1 2 Previous Next

Farah Juma's Blog

23 posts

For certificate-based authentication, the client presents its X.509 certificate chain to the server. The server then verifies this certificate chain using its truststore. The truststore only needs to contain certificates for root certificate authorities or intermediate certificate authorities, it doesn’t need to contain the individual client certificates. Once a server has successfully verified a client’s certificate chain, it checks if the corresponding identity is authorized to access a particular resource. This authorization check is done using a security realm that contains the roles that are associated with identities. This security realm also does not need to contain individual client certificates. Instead, it can contain principals that can be derived from a portion of the client’s certificate. As mentioned in a previous blog post, we are enhancing the way an X.509 certificate can be mapped to an underlying identity using Elytron.

 

In this blog post, we’ll take a look at how to secure a web application deployed to WildFly using the CLIENT_CERT HTTP authentication mechanism with two-way SSL and authorization. The server’s truststore will only contain an example CA certificate, it won’t contain any client certificates. Similarly, the security realm used for authorization in this example won’t contain any client certificates. It will contain principals that are derived from a portion of the CN value from the subject name from a client’s X.509 certificate.

 

 

Certificate generation

 

First, clone the elytron-examples repo locally:

 

git clone https://github.com/wildfly-security-incubator/elytron-examples
cd elytron-examples

 

Next, let's generate some client and server certificates that will be used in this example to set up two-way SSL:

 

cd dynamic-certificates
mvn clean install exec:java -Dexec.mainClass="org.wildfly.security.examples.CertificateGenerationExample" -Dexec.args="CN=Bob.Smith.123456 CN=Alice.Smith.456789"

 

Notice that the above command generates the following keystores and truststores in the dynamic-certificates/target directory:

 

client1.keystore

client2.keystore

server.keystore

server.truststore

 

  • client1.keystore contains a certificate with distinguished name: CN=Bob.Smith.123456
  • client2.keystore contains a certificate with distinguished name: CN=Alice.Smith.456789
  • Both client certificates are issued by an example certificate authority with distinguished name: CN=Elytron CA, ST=Elytron, C=UK, EMAILADDRESS=elytron@wildfly.org, O=Root Certificate Authority
  • server.truststore contains only this certificate authority's certificate

 

Next, convert the client keystores into PKCS12 format and import them into your browser so you can pick which one to present to the server later on:

 

keytool -importkeystore -srckeystore client1.keystore -srcstoretype jks -destkeystore client1.keystore.pkcs12 -deststoretype pkcs12 -srcstorepass keystorepass -deststorepass keystorepass

keytool -importkeystore -srckeystore client2.keystore -srcstoretype jks -destkeystore client2.keystore.pkcs12 -deststoretype pkcs12 -srcstorepass keystorepass -deststorepass keystorepass

 

Finally, copy the server.keystore and server.truststore files to your WildFly server instance:

 

cp /PATH/TO/ELYTRON/EXAMPLES/dynamic-certificates/target/server.* $WILDFLY_HOME/standalone/configuration

 

Server configuration

 

A WildFly CLI script that contains all of the commands that are used in this example can be found in the client-cert-with-authorization project in elytron-examples repository:

 

https://github.com/wildfly-security-incubator/elytron-examples/tree/master/client-cert-with-authorization

 

Security domain configuration

 

We’re going to configure a security domain such that only the client with ID "123456" will be able to access our secured web application.

 

First, create a principal decoder that can be used to obtain the CN value from the subject name from an X.509 client certificate:

 

/subsystem=elytron/x500-attribute-principal-decoder=cnDecoder:add(attribute-name=CN, maximum-segments=1)

 

Next, create a principal transformer that can be used to extract the ID portion from the CN value:

 

/subsystem=elytron/regex-principal-transformer=myRegexTransformer:add(pattern=".*\\.([0-9]+)",replacement="$1")

 

Now, let’s combine these two principal transformers into a chained principal transformer:

 

/subsystem=elytron/chained-principal-transformer=myChainedTransformer:add(principal-transformers=[cnDecoder, myRegexTransformer])

 

As an example, for client1’s certificate, the above chained-principal-transformer would map the subject name "CN=Bob.Smith.123456" to just the ID portion: "123456".

 

Next, create a file-system based security realm that will be used to store the client IDs and their roles:

 

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

 

Now, let’s add the two IDs from the client certificates to our filesystem-realm. Notice that the user with ID "123546" has “Users” role but the second user with ID "456879" does not have any roles.

 

/subsystem=elytron/filesystem-realm=idsRealm:add-identity(identity=123456)  
/subsystem=elytron/filesystem-realm=idsRealm:add-identity-attribute(identity=123456,name=Roles,value=[Users])
/subsystem=elytron/filesystem-realm=idsRealm:add-identity(identity=456789)  

 

Finally, we’ll configure a security domain that references our security realm and makes use of our chained principal transformer:

 

/subsystem=elytron/security-domain=clientCertDomain:add(realms=[{realm=idsRealm}], default-realm=idsRealm, pre-realm-principal-transformer=myChainedTransformer, permission-mapper=default-permission-mapper)

 

Two-way SSL configuration

 

We’re now going to enable two-way SSL for web applications deployed to the server.

 

First, configure a key-store using our server.truststore file. Remember this only contains the example certificate authority’s certificate, it doesn’t contain the individual client certificates:

 

/subsystem=elytron/key-store=serverTS:add(path=server.truststore,relative-to=jboss.server.config.dir,credential-reference={clear-text=truststorepass},type=JKS)

 

Next, enable two-way SSL:

 

security enable-ssl-http-server --key-store-path=server.keystore --key-store-path-relative-to=jboss.server.config.dir --key-store-password=keystorepass --trust-store-name=serverTS

 

CLIENT_CERT configuration

 

We’re now going to configure the CLIENT_CERT HTTP authentication mechanism so we can secure our web application using this mechanism.

 

The CLIENT_CERT HTTP authentication mechanism makes use of the verified X.509 client certificate chain that is established on the SSL connection. By default, this authentication mechanism will attempt to use the configured security realm to validate this certificate chain using client certificates that are stored in the configured security realm. Since our security realm does not contain client certificates and only contains role information, there won’t be a way for the realm itself to verify the client certificate. This is fine since we know the certificate chain was already verified when establishing the SSL connection so we’re going to set the org.wildfly.security.http.skip-certificate-verification property to true for the CLIENT_CERT mechanism, as shown below. Our security realm will still be used for the authorization check.

 

/subsystem=elytron/configurable-http-server-mechanism-factory=configuredCert:add(http-server-mechanism-factory=global, properties={org.wildfly.security.http.skip-certificate-verification=true})

 

Now let’s finish configuring the CLIENT_CERT mechanism:

 

/subsystem=elytron/http-authentication-factory=clientCertAuth:add(http-server-mechanism-factory=configuredCert, security-domain=clientCertDomain, mechanism-configurations=[{mechanism-name=CLIENT_CERT}])
/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=clientCertAuth,override-deployment-config=true)

 

Finally, reload the server:

 

reload

 

Deploying and accessing the application

 

We’re going to make use of the simple-webapp project in the elytron-examples repository. It can be deployed using the following commands:

 

cd /PATH/TO/ELYTRON/EXAMPLES/simple-webapp
mvn clean install wildfly:deploy

 

Then try accessing the application using https://localhost:8443/simple-webapp

 

Note that since the server's certificate won't be trusted by your browser, you'll need to manually confirm that this certificate is trusted or configure your browser to trust it.

 

First, select the certificate for "Alice.Smith.456789". Then try clicking on “Access Secured Servlet”. Notice that a Forbidden message occurs. This is because accessing the secured servlet requires “Users” role but the “456789” identity that we configured has no roles.

 

Now, try accessing the application again. This time, select the certificate for "Bob.Smith.123456" and then click on “Access Secured Servlet”. This time, this succeeds since the “123456” identity that we configured has “Users” role.

 

Summary

 

This example has shown to secure a web application deployed to WildFly using the CLIENT_CERT HTTP authentication mechanism with two-way SSL with authorization. It has also demonstrated that individual client certificates do not need to be stored in either the server’s truststore or in its security realm.

Since the feature development phase for WildFly 18 has now started, we wanted to highlight the security features that we are planning to work on for this release.

 

Planned Features

 

The features in this first set are ones that were actively developed during the WildFly 17 feature development phase. Many of these are now close to being merged:

 

  • WFCORE-4362 Make the certificate authority used by a certificate-authority-account configurable

Currently, WildFly only supports integration with the Let’s Encrypt certificate authority. Once the certificate authority is configurable, it will be possible to use this integration with other certificate authorities that implement the ACME protocol.

 

  • WFCORE-4227 Add the ability for the CLI SSL security commands to be able to obtain a server certificate from Let's Encrypt

The ssl enable-ssl-management and enable-ssl-http-server already make it possible to enable one-way and two-way SSL easily. These commands will soon be able to obtain certificates from Let’s Encrypt.

 

  • WFCORE-4361 Enhanced mapping of X509Certificate to the underlying identity

Currently, the mapping of an X.509 certificate chain to an identity is done by taking the subject distinguished name from the first certificate in the X.509 certificate chain as an X.500 principal. It will soon be possible to map an X.509 certificate chain to an identity by using a subject alternative name from the first certificate in the X.509 certificate chain.

 

  • WFCORE-3947 Support SSL Certificate revocation using OCSP

 

 

  • WFCORE-4447 Ability to load the attributes of an identity from multiple sources

Elytron already provides a very flexible approach to assigning roles and permissions to an identity based on arbitrary attributes loaded by a security realm. This enhancement will add support for loading the attributes from multiple security realms and aggregating the results together.

 

This feature request looks at adding support for RFC 5424 and RFC 3164 as well as some other enhancements related to reliability vs. speed.

 

The features in this second set are new ones that we are planning to work on:

 

  • ELY-1778 Enhance Audit Logging by Feature Parity with Legacy Security Auditing

This feature request looks at adding support for additional audit events as well as support for a new @Audit annotation.

 

  • WFCORE-4360 Support encrypted expression resolution using a CredentialStore

The previous Vault implementation was basically a repository of encrypted clear text strings that could be referenced using expressions in the management model. The new CredentialStore is a repository of credentials. This feature request will look into how the CredentialStore could be used to support encrypted values within the overall model.

 

  • WFCORE-629 Enable automatic encryption of passwords stored in configuration

 

  • ELY-816 Support for masked passwords in client XML config

 

  • WFLY-11697 Web Services Integration with WildFly Elytron

The WildFly Elytron integration added a new API and configuration file to configure the client side security for outgoing calls. This feature request is to increase the integration for web services clients.

 

  • WFCORE-4483 Add support for missing MP-JWT requirements

This one will look at adding adding some remaining items needed to support the MicroProfile JWT RBAC security specification.

 

This feature request looks at allowing users to configure how much output should appear when invoking certain operations on Elytron key-stores.

 

Feedback

 

Please keep in mind that this blog post is a summary of our general plans and not a guarantee that each of these features will be merged. However, this blog post does give an indication of our team’s current priorities. If any of these features are a priority for you, please let us know. Please also let us know if there are any security features that are missing that you would like to see prioritized as we can take this kind of feedback into account for future releases.

A new security feature that we have been working on is enhancing the way an X.509 certificate chain gets mapped to an underlying identity. Although this feature didn’t make it into WildFly 17, the plan is to have it included in WildFly 18. This blog post will give an overview of this new feature.

 

X.509 certificate chain evidence

 

For certificate-based authentication, the client presents its X.509 certificate chain to the server. The server then verifies this certificate chain. Elytron’s security realm APIs support using this type of X.509 certificate chain evidence to locate and load an identity. For example, after verifying a certificate chain, a server may need to check if the corresponding identity is authorized to access a particular resource. This would involve interacting with the security realm to locate and load an identity based on the X.509 certificate chain in order to determine the roles that are associated with that identity.

 

Mapping using the subject distinguished name

 

Currently, the mapping of an X.509 certificate chain to an identity is done by taking the subject distinguished name from the first certificate in the X.509 certificate chain as an X.500 principal. This principal then gets rewritten using any principal decoders or principal transformers that have been configured, as shown below:

 

 

Mapping using a subject alternative name

 

The X.509 v3 Subject Alternative Name extension provides the ability to specify one or more alternative names that can be used in addition to or instead of the subject distinguished name in an X.509 certificate. From WildFly 18, it will also be possible to map an X.509 certificate chain to an identity by using a subject alternative name from the first certificate in the X.509 certificate chain. This principal will then get rewritten using any principal decoders or principal transformers that have been configured, as shown below:

 

 

In particular, to specify that a subject alternative name from a certificate should be used as the principal associated with that certificate, it will be possible to configure an x509-subject-alt-name-evidence-decoder in the mappers configuration in the Elytron subsystem. This element will have two attributes:

 

  • alt-name-type - The subject alternative name type to decode. This required attribute must be one of the subject alternative name types that can be represented as a String:
    • rfc822Name
    • dNSName
    • uniformResourceIdentifier
    • iPAddress
    • registeredID
    • directoryName
  • segment - The 0-based occurrence of the subject alternative name to map. This attribute is optional and only used when there is more than one subject alternative name of the given alt-name-type. The default value will be 0.

 

For example, to associate the X.509 certificate in the diagram above with the principal "bob.smith@example.com", it will be possible to configure the following x509-subject-alt-name-evidence-decoder:

 

/subsystem=elytron/x509-subject-alt-name-evidence-decoder=emailDecoder:add(alt-name-type=rfc822Name)

 

This evidence decoder can then be referenced when creating an Elytron security-domain:

 

/subsystem=elytron/security-domain=exampleSD:add(...,evidence-decoder=emailDecoder)

 

Other types of evidence decoders

 

It will also be possible to configure an x500-subject-evidence-decoder that will just extract the subject from the first certificate in the certificate chain, as an X.500 principal, as shown below:

 

/subsystem=elytron/x500-subject-evidence-decoder=subjectDecoder:add()

 

Finally, it will also be possible to configure an aggregate-evidence-decoder that is made up of two or more evidence decoders, as shown in the following example:

 

/subsystem=elytron/x509-subject-alt-name-evidence-decoder=emailDecoder:add(alt-name-type=rfc822Name)
/subsystem=elytron/x509-subject-alt-name-evidence-decoder=dnsDecoder:add(alt-name-type=dNSName)
/subsystem=elytron/x500-subject-evidence-decoder=subjectDecoder:add()
/subsystem=elytron/aggregate-evidence-decoder=aggregateDecoder:add(evidence-decoders=[emailDecoder,subjectDecoder,dnsDecoder])

 

The evidence decoders that make up an aggregate-evidence-decoder will be attempted in order until one returns a non-null principal or until there are no more evidence decoders left to try.

 

Summary

 

This blog post has given an introduction on how we are enhancing the mapping of an X.509 certificate to an underlying identity.

 

For more details on this feature and updates on the status of this feature, keep an eye on WFCORE-4361.

One of the new security features that we have been working on is adding support for TLS 1.3 to WildFly. Although this feature didn’t make it into WildFly 17, the plan is to have it included in WildFly 18. This blog post will give a quick introduction to this feature.

 

Server Side Configuration

 

Within the Elytron subsystem in WildFly, the server-ssl-context and client-ssl-context are used to configure a server and client SSL context, respectively. Note that a client SSL context is used when a WildFly instance creates an SSL connection as a client. Both the server-ssl-context and client-ssl-context have a protocols attribute which contains a list of the protocols to be supported by WildFly. From WildFly 18, this attribute will also allow TLSv1.3 to be specified.

 

Since the TLS 1.3 specification introduced a few new cipher suites, both the server-ssl-context and client-ssl-context will also have a new cipher-suite-names attribute that will be used to specify the TLS 1.3 cipher suites to enable. The format will be a colon separated list of cipher suite names that will default to:

 

“TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256”

 

It is important to note the new TLS 1.3 cipher suites cannot be used with TLS 1.2 and below. Similarly, the old cipher suites cannot be used with TLS 1.3.

 

Client Side Configuration

 

Within the Elytron authentication client, similar changes will be made to the ssl-context. In particular, from WildFly 18, the ssl-context’s protocol element will also allow TLSv1.3 to be specified in the list of protocol names to be supported.

 

The ssl-context’s cipher-suite element will also have a new names attribute to specify the TLS 1.3 cipher suites to enable. As on the server side, this will default to:

 

“TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256”

 

Summary

 

This blog post has given a quick introduction to the upcoming support for TLS 1.3 in WildFly. Since the TLS 1.3 implementation was introduced in JDK 11, this new feature will only be available when running WildFly against JDK 11 or higher.

 

For more details on this feature and updates on the status of this feature, keep an eye on WFCORE-4172.

WildFly Elytron 1.8.0.Final has been released and is included with WildFly 16 Final, which is now available for download. In this blog post, we’ll take a look at what’s new in Elytron in WildFly 16.

 

Silent mode for HTTP Basic authentication

 

Setting a simple property for the HTTP Basic authentication mechanism in your application’s deployment descriptor enables silent mode. This is useful when paired with HTTP Form authentication, allowing human users to log in using the Form authentication mechanism and programmatic clients to log in using the Basic authentication mechanism. More details can be found in this blog post.

 

Automatic detection of file-based KeyStore types

 

When adding a file-based key-store in the Elytron subsystem, the type attribute no longer needs to be specified. Instead, the Elytron subsystem will now automatically detect the type.

 

Utility to migrate legacy properties files to Elytron

 

It is now easy to migrate legacy properties files to an Elytron filesystem-realm using the WildFly Elytron Tool. Take a look at this blog post for the migration steps.

 

EE Security API integration with Elytron

 

It is possible to make use of the new Security API in Java EE 8 defined under JSR-375 with Elytron. Details on how to get started with EE security can now be found here.

 

Obtain and manage certificates from Let’s Encrypt using the WildFly Management Console

 

Since WildFly 14, it is possible to obtain and manage certificates from Let’s Encrypt using the WildFly CLI. WildFly 16 now adds the ability to also make use of the web-based WildFly Management Console to do this. The details, including screenshots, can be found here.

 

User self-service example

 

Elytron’s APIs allow identities to be modified, making it possible for users to update their credentials and/or other information about themselves. This two-part blog post shows how to create a web application for user self-service.

 

Custom principal transformer example

 

Although there are a few different principal transformers that can be used out of the box with WildFly, it is also possible to implement custom principal transformers that can be registered using the Elytron subsystem. This blog post describes how to create and make use of a custom Elytron principal transformer.

 

Jira release notes

 

The full list of issues resolved is available here.

 

Where to find more information

 

As always, be sure to check out our blog posts page, where we collect references to all our blog posts on Elytron features. If there is an Elytron topic you’d like to see a blog post on, feel free to leave a comment on that page to ask for it. Questions on Elytron are also welcome on WildFly’s user forums.

 

To learn more about the Elytron subsystem, take a look at the Elytron documentation.

An Elytron principal transformer can be used to map a principal from one form to another. Although there are a few different principal transformers that can be used out of the box with WildFly, it is also possible to implement custom principal transformers that can be registered using the Elytron subsystem. In this blog post, we’ll see how to create a custom principal transformer that can be used to convert usernames to all upper case characters. This can be useful if your identity store contains upper case usernames but you want to ignore the case when a username is being specified during the authentication process.

 

This blog post will make use of the custom-principal-transformer project in the elytron-examples repository:

 

https://github.com/wildfly-security-incubator/elytron-examples/tree/master/custom-principal-transformer

 

Prerequisite configuration

 

Let’s first add a management user for the server - this is the user that we’re going to use later on when attempting to connect to the CLI. For this example, we’re going to create a filesystem-based identity store and add a user named “BOBSMITH” with password “bob123+” using the following CLI commands:

 

/subsystem=elytron/filesystem-realm=exampleRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir)
/subsystem=elytron/filesystem-realm=exampleRealm:add-identity(identity=BOBSMITH)
/subsystem=elytron/filesystem-realm=exampleRealm:set-password(identity=BOBSMITH,clear={password=bob123+})  
/subsystem=elytron/filesystem-realm=exampleRealm:add-identity-attribute(identity=BOBSMITH,name=Roles,value=[SuperUser])

 

Next, we’re going to add the filesystem-realm that we just created to the “ManagementDomain” security domain that is already defined in the default Elytron subsystem configuration and we’re going to make this the default security realm for this security domain:

 

/subsystem=elytron/security-domain=ManagementDomain:list-add(name=realms, value={realm=exampleRealm})  
/subsystem=elytron/security-domain=ManagementDomain:write-attribute(name=default-realm, value=exampleRealm) 

 

Now, use the following commands to secure the management interface using Elytron:

 

/core-service=management/management-interface=http-interface:write-attribute(name=http-upgrade,value={enabled=true, sasl-authentication-factory=management-sasl-authentication})  
/core-service=management/management-interface=http-interface:undefine-attribute(name=security-realm)

 

For this example, we’re also going to use RBAC to secure the management interface using our filesystem-based identity store. However, please note that this step is optional:

 

/core-service=management/access=authorization:write-attribute(name=provider, value=rbac)
/core-service=management/access=authorization:write-attribute(name=use-identity-roles,value=true)

 

Finally, reload the server:

 

:reload

 

At this point, let’s try to connect to the CLI specifying “BobSmith” as the username:

 

$WILDFLY_HOME/bin/jboss-cli.sh -c --controller=remote+http://127.0.0.1:9990 --user=BobSmith --password=bob123+

 

Notice that authentication fails since the username in our identity store is “BOBSMITH” not “BobSmith”. In the next few sections, we’ll see how to create and make use of a custom principal transformer that can be used to convert the given principal name to all upper case characters.

 

Custom principal transformer implementation

 

To create a custom principal transformer, we need to create a class that implements the PrincipalTransformer interface. In particular, we need to provide an implementation for the apply method which will be used to map the given principal from one form to another. Our custom implementation, CasePrincipalTransformer, that converts the given principal name to all upper case characters can be found here.

 

Installing our custom principal transformer

 

Now that we’ve created our custom principal transformer, the next thing we need to do is install it in WildFly so we can make use of it in the Elytron subsystem. First, let’s build our custom-principal-transformer project:

 

mvn clean install

 

The above command produces a JAR that contains our custom implementation. Next, we can add a module that contains this JAR to WildFly using the CLI:

 

module add --name=org.wildfly.security.examples.custom-principal-transformer --resources=/PATH_TO_ELYTRON_EXAMPLES/elytron-examples/custom-principal-transformer/target/custom-principal-transformer-1.0.0.Alpha1-SNAPSHOT.jar --dependencies=org.wildfly.security.elytron,org.wildfly.extension.elytron

 

Finally, we can create a custom-principal-transformer in the Elytron subsystem that references both this new module and our custom principal transformer class that is contained in this module:

 

/subsystem=elytron/custom-principal-transformer=myPrincipalTransformer:add(module=org.wildfly.security.examples.custom-principal-transformer, class-name=org.wildfly.security.examples.CasePrincipalTransformer)

 

Making use of our custom principal transformer

 

We’re now ready to make use of our custom-principal-transformer. Let’s update our "ManagementDomain" to specify that our newly created custom-principal-transformer should be used as the pre-realm-principal-transformer - this is a principal transformer that gets applied before the realm is selected:

 

/subsystem=elytron/security-domain=ManagementDomain:write-attribute(name=pre-realm-principal-transformer,value=myPrincipalTransformer)

 

Next, reload the server using the :reload command.

 

Now, try connecting to the CLI again, specifying “BobSmith” as the username:

 

$WILDFLY_HOME/bin/jboss-cli.sh -c --controller=remote+http://127.0.0.1:9990 --user=BobSmith --password=bob123+

 

This time, authentication succeeds since our custom-principal-transformer gets used to convert the given username to all upper case characters, matching the username in our identity store.

 

Summary

 

This blog post has shown how to create and make use of a custom Elytron principal transformer.

Although Elytron was developed for the WildFly application server, it is possible to use Elytron outside of WildFly. This blog post is going to give an overview of how to secure an embedded Jetty server using Elytron. In particular, we’re going to take a look at an example embedded Jetty application that makes use of HTTP Basic authentication and we’re going to modify it so that it’s backed by Elytron for its security.

 

Simple embedded Jetty application

 

Let’s take a look at a simple embedded Jetty server application:

 

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

    // Create the Jetty server instance
    Server server = new Server(8080);

    ConstraintSecurityHandler security = new ConstraintSecurityHandler();
    server.setHandler(security);

    // Create a constraint that specifies that accessing "/secured" requires authentication
    // and the authenticated user must have "admin" role
    Constraint constraint = new Constraint();
    constraint.setName("auth");
    constraint.setAuthenticate(true);
    constraint.setRoles(new String[]{"admin"});
    ConstraintMapping mapping = new ConstraintMapping();
    mapping.setPathSpec("/secured");
    mapping.setConstraint(constraint);
    security.setConstraintMappings(Collections.singletonList(mapping));

    // Security realm configuration
    // alice: alice123+,admin,employee
    // bob: bob123+,employee
    LoginService loginService = new HashLoginService("MyRealm", "src/test/resources/realm.properties");
    server.addBean(loginService);
    security.setLoginService(loginService);

    // Use Jetty's BasicAuthenticator
    security.setAuthenticator(new BasicAuthenticator());

    // Configure the handler we are securing
    ServletHandler servletHandler = new ServletHandler();
    servletHandler.addServletWithMapping(SecuredServlet.class, "/secured");
    security.setHandler(servletHandler);

    server.start();
}

 

The above example first creates a Jetty server instance. It then creates a constraint that specifies that accessing the /secured page requires authentication and that the authenticated user must have admin role. Next, it configures a Jetty security realm that’s backed by a properties file. It then specifies that Jetty’s BasicAuthenticator should be used to handle authentication. It also specifies the ServletHandler that’s being secured. This handler is configured with a simple servlet that just outputs the username of the authenticated user. Finally, the Jetty server instance is started.

 

Switching to Elytron

 

Let’s modify our simple Jetty application so that it’s backed by Elytron for its security. To do this, we're going to focus on the security related parts of the example, namely the security realm configuration, the Authenticator configuration, and the ServletHandler configuration. The code used in this example can be found here.

 

Dependencies

 

There are two key dependencies that are needed:

 

  • org.wildfly.security:wildfly-elytron - This provides the WildFly Elytron security framework.
  • org.wildfly.security.elytron-web:jetty-server - This provides classes for integrating Elytron based HTTP authentication with Jetty.

 

Creating an Elytron SecurityDomain

 

The first thing we’re going to do is create an Elytron security domain. We’re going to use a simple map backed security realm for this example but any type of Elytron security realm could be used here instead:

 

private static SecurityDomain createSecurityDomain() throws Exception {

        // Create an Elytron map-backed security realm
        SimpleMapBackedSecurityRealm simpleRealm = new SimpleMapBackedSecurityRealm(() -> new Provider[] { elytronProvider });
        Map<string, simplerealmentry=""> identityMap = new HashMap<>();

        // Add user alice
        identityMap.put("alice", new SimpleRealmEntry(getCredentialsForClearPassword("alice123+"), getAttributesForRoles("employee", "admin")));

        // Add user bob
        identityMap.put("bob", new SimpleRealmEntry(getCredentialsForClearPassword("bob123+"), getAttributesForRoles("employee")));

        simpleRealm.setIdentityMap(identityMap);

        // Add the map-backed security realm to a new security domain's list of realms
        SecurityDomain.Builder builder = SecurityDomain.builder()
                .addRealm("ExampleRealm", simpleRealm).build()
                .setPermissionMapper((principal, roles) -> PermissionVerifier.from(new LoginPermission()))
                .setDefaultRealmName("ExampleRealm");
        return builder.build();
}

 

Notice that our security domain has two users, alice and bob, with passwords alice123+ and bob123+, respectively. Note that alice has both employee and admin role and bob only has employee role.

 

Creating an ElytronAuthenticator

 

The next thing we’re going to do is create an ElytronAuthenticator instance. ElytronAuthenticator is a class from the elytron-web-jetty project that implements Jetty’s Authenticator interface. It is the class that will be used for validating a request. In particular, ElytronAuthenticator’s validateRequest method uses Elytron APIs to perform authentication for a request by making use of an Elytron SecurityDomain and an Elytron HttpAuthenticationFactory.

 

We’re first going to create an Elytron HttpServerAuthenticationMechanismFactory which provides the Elytron HTTP Basic authentication mechanism:

 

HttpServerAuthenticationMechanismFactory providerFactory = new SecurityProviderServerMechanismFactory(() -> new Provider[] {new WildFlyElytronProvider()});
HttpServerAuthenticationMechanismFactory httpServerMechanismFactory = new FilterServerMechanismFactory(providerFactory, true, "BASIC");

 

We can now create an ElytronAuthenticator instance using our createSecurityDomain() method and our newly created httpServerMechanismFactory:

 

private static ElytronAuthenticator createElytronAuthenticator() {
        SecurityDomain securityDomain = createSecurityDomain();
        return ElytronAuthenticator.builder()
                .setSecurityDomain(securityDomain)
                .setMechanismConfigurationSelector(MechanismConfigurationSelector.constantSelector(MechanismConfiguration.builder()
                        .addMechanismRealm(MechanismRealmConfiguration.builder().setRealmName("Elytron Realm").build())
                        .build()))
                .setFactory(httpServerMechanismFactory)
                .build();
}

 

We can then tweak the example application to specify that authentication should be handled by our ElytronAuthenticator instance:

 

ElytronAuthenticator elytronAuthenticator = createElytronAuthenticator();
security.setAuthenticator(elytronAuthenticator);

 

Wrapping the ServletHandler with an ElytronRunAsHandler

 

Recall that the ServletHandler in the example was configured with a simple secured servlet that just outputs the username of the authenticated user. We’re now going to wrap that handler with an ElytronRunAsHandler, as shown below. This class is also from the elytron-web-jetty project and is used to associate the security identity that was produced after successfully validating a request with the current thread.

 

ServletHandler servletHandler = new ServletHandler();
ElytronRunAsHandler elytronRunAsHandler = new ElytronRunAsHandler(servletHandler);
servletHandler.addServletWithMapping(SecuredServlet.class, "/secured");
security.setHandler(elytronRunAsHandler);

 

Now, in our secured servlet, we can use securityDomain.getCurrentSecurityIdentity() to get the current user and then we can use getPrincipal().getName() to output the username of that authenticated user:

 

public static class SecuredServlet extends HttpServlet {
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            response.setContentType("text/html");
            response.setStatus(HttpServletResponse.SC_OK);
            PrintWriter writer = response.getWriter();
            writer.println("Hello " + securityDomain.getCurrentSecurityIdentity().getPrincipal().getName() + "! You've authenticated successfully using Elytron!");
        }
}

 

These are all the changes that are needed to modify our example embedded Jetty application so that it’s backed by Elytron for its security. The complete code for this example can be found here.

 

Running the modified example application

 

To build and run the modified example application, the following command can be used:

 

mvn clean install exec:exec

 

This will start the embedded Jetty server instance and it will be listening for requests on port 8080.

 

Now try accessing the example application using http://localhost:8080/secured.

 

First, try to log in as bob using password bob123+. Since accessing the /secured page requires admin role and since bob is not an admin, you'll see an HTTP 403 error.

 

Next, try to log in as alice using password alice123+. Since alice has admin role, you'll be able to successfully log in and will see the following message:

Hello alice! You've authenticated successfully using Elytron!

 

Summary

 

This blog post has shown how to secure an embedded Jetty server using Elytron. The classes for integrating Elytron based HTTP authentication with Jetty can be found in the elytron-web-jetty project.

Since WildFly 14, it is possible to obtain and manage certificates from the Let’s Encrypt certificate authority using the WildFly CLI. In particular, it is possible to get a certificate from Let’s Encrypt, revoke it if necessary, and check if it’s due for renewal. This blog post is going to give an overview of these new operations.

 

 

Prerequisite configuration

 

First, configure a key-store in the Elytron subsystem that will be used to hold your server certificates. Note that the path to the keystore file doesn’t need to exist yet.

 

/subsystem=elytron/key-store=serverKS:add(path=server.keystore.jks, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=JKS)

 

Next, make sure your WildFly server instance is publicly accessible using the domain name(s) you will be obtaining a certificate for from Let’s Encrypt. For example, if you are requesting a certificate for the domain name “www.example.org”, then “www.example.org” needs to be publicly accessible. This is because Let’s Encrypt will attempt to access this URL when attempting to validate that you really do own this domain name. WildFly itself will handle proving that you really do own this domain name.

 

Show me the commands first

 

Obtaining and managing certificates from Let’s Encrypt using the CLI requires a couple one-time configuration steps. After that, the new obtain-certificate, revoke-certificate, and should-renew-certificate commands can simply be used. These commands are shown below. We’re going to go through each of these commands in more detail in the rest of this post.

 

### One-time configuration

# Configure a Let’s Encrypt account 
/subsystem=elytron/key-store=accountsKS:add(path=accounts.keystore.jks,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=JKS)
/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:add(alias=letsEncrypt,key-store=accountsKS,contact-urls=[mailto:admin@admin.org])

### Now you’re ready to start obtaining and managing certificates

# Obtain a certificate from Let’s Encrypt
/subsystem=elytron/key-store=serverKS:obtain-certificate(alias=server,domain-names=[www.example.org],certificate-authority-account=myLetsEncryptAccount,agree-to-terms-of-service)

# Revoke a certificate that was issued by Let’s Encrypt
/subsystem=elytron/key-store=serverKS:revoke-certificate(alias=server,reason=keyCompromise,certificate-authority-account=myLetsEncryptAccount)

# Check if a certificate is due for renewal in less than 30 days
/subsystem=elytron/key-store=serverKS:should-renew-certificate(alias=server)

 

Configuring a Let’s Encrypt account

 

Before obtaining your first certificate from Let’s Encrypt, a Let’s Encrypt account needs to be configured.

 

First, configure a key-store in the Elytron subsystem that will be used to hold your Let’s Encrypt account key. The path to the keystore file doesn’t need to exist yet.

 

/subsystem=elytron/key-store=accountsKS:add(path=accounts.keystore.jks,relative-to=jboss.server.config.dir,credential-reference={clear-text=secret},type=JKS)

 

Now we can configure a Let’s Encrypt account that we will reference when obtaining and revoking certificates:

 

/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:add(alias=letsEncrypt,key-store=accountsKS,contact-urls=[mailto:admin@admin.org])

 

The above command results in the following configuration in the Elytron subsystem:

 

<subsystem xmlns="urn:wildfly:elytron:4.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
    <tls>
    ...
        <certificate-authority-accounts>
            <certificate-authority-account name="myLetsEncryptAccount" contact-urls="mailto:admin@admin.org">
                <account-key key-store="accountsKS" alias="letsEncrypt"/>
            </certificate-authority-account>
        </certificate-authority-accounts>
    ...
    </tls>
...
</subsystem>

 

Notice that a certificate-authority-account has the following attributes and element:

 

  • name - The name of the certificate authority account.
  • contact-urls - An optional list of contact URLs that Let’s Encrypt can use to notify you about any issues with your account.
  • account-key - Information about the account key that will be used when communicating with Let’s Encrypt.
    • key-store - A reference to the Elytron key-store that will hold your Let’s Encrypt account key.
    • alias - The alias in the referenced key-store that will contain your Let’s Encrypt account key.

 

Obtaining a certificate from Let’s Encrypt

 

To obtain a certificate from Let’s Encrypt, the key-store obtain-certificate command can be used. Its syntax is as follows:

 

obtain-certificate --alias= --domain-names= --certificate-authority-account= [--agree-to-terms-of-service=<true,false>] [--staging=<true,false>] [--algorithm=] [--key-size=] [--credential-reference=]

 

Let’s take a closer look at the obtain-certificate operation’s parameters:

 

  • alias - The alias in the key-store that will be used to store the certificate obtained from Let’s Encrypt.
  • domain-names - The list of domain names to request a certificate for.
  • certificate-authority-account - A reference to the certificate authority account information that should be used to obtain the certificate.
  • agree-to-terms-of-service - Whether or not you agree to Let’s Encrypt’s terms of service (this only needs to be specified the first time you obtain a certificate from Let’s Encrypt with your certificate-authority-account).
  • staging - Optional. Indicates whether or not Let’s Encrypt’s staging environment should be used to obtain the certificate. The default value is false. This should only be set to true for testing purposes. This should never be set to true in a production environment.
  • algorithm - Optional. Indicates the key algorithm that should be used (RSA or EC). The default value is RSA.
  • key-size - Optional. Indicates the key size that should be used. The default value is 2048.
  • credential-reference - Optional. The credential-reference that should be used to protect the generated private key. The default value is the key-store password.

 

The obtain-certificate command will use the referenced certificate-authority-account to create an account with Let’s Encrypt if one does not already exist. It will then request a certificate from Let’s Encrypt for the specified domain-names. In particular, the obtain-certificate operation will prove ownership of the requested domain names, generate a key pair, generate a certificate signing request (CSR) using the generated key pair and the requested domain names, and submit this CSR to Let’s Encrypt. If successful, the obtain-certificate operation will retrieve the resulting certificate chain from Let’s Encrypt and store it along with the generated PrivateKey under the given alias in the key-store. These changes will also be persisted to the file that backs the key-store.

 

For example, to request a certificate from Let’s Encrypt for the domain name “www.example.org” using the “myLetsEncryptAccount” certificate-authority-account, the following command could be used. The resulting certificate will be stored in the file that backs the "serverKS" key-store under the alias “server”.

 

/subsystem=elytron/key-store=serverKS:obtain-certificate(alias=server,domain-names=[www.example.org],certificate-authority-account=myLetsEncryptAccount,agree-to-terms-of-service)

 

You can now check the alias names in the key-store and confirm the new alias, “server”, is listed:

 

/subsystem=elytron/key-store=serverKS:read-aliases()    
{    
    "outcome" => "success",    
    "result" => ["server"]    
}  

 

To make use of this server certificate that’s been issued by Let’s Encrypt for one-way or two-way SSL, this server key-store can be used to create a key-manager in the Elytron subsystem and an ssl-context that references this key-manager can then be created. More details about setting up one-way and two-way SSL can be found in the Elytron documentation.

 

Revoking a certificate from Let’s Encrypt

 

If you need to revoke a certificate that was issued by Let’s Encrypt, the revoke-certificate command can be used:

 

/subsystem=elytron/key-store=serverKS:revoke-certificate(alias=server,reason=keyCompromise,certificate-authority-account=myLetsEncryptAccount)

 

In the above example, alias identifies the certificate that should be revoked. The certificate-authority-account is a reference to the certificate authority account information that should be used to revoke the certificate. The reason is optional and indicates the reason for revocation. If provided, it must be a valid revocation string.

 

Once the certificate has been successfully revoked, it will be deleted from the key-store. This change will also be persisted to the file that backs the key-store.

 

Checking if a certificate is due for renewal

 

The should-renew-certificate command can be used to check if a certificate is due for renewal. It returns true if the certificate expires in less than the given number of days and false otherwise. Its output also indicates the number of days to expiry. In the following example, should-renew-certificate checks if the certificate stored under the alias “server” expires in less than 15 days.

 

/subsystem=elytron/key-store=serverKS:should-renew-certificate(alias=server,expiration=15)
{
    "outcome" => "success",
    "result" => {
        "should-renew-certificate" => false,
        "days-to-expiry" => 89L
    }
}

 

If the expiration parameter is not provided, it will default to 30 days, i.e., should-renew-certificate will return true if the certificate expires in less than 30 days and false otherwise.

 

Using a CLI script to automate certificate renewal

 

Certificates issued by Let’s Encrypt are valid for 90 days. Let’s Encrypt recommends renewing certificates every 60 days. You can automate renewal by first creating a CLI script that checks if a certificate is due for renewal and if so, uses the obtain-certificate command to renew it. You could then create a cron job that runs say, twice daily, and executes the CLI script. A simple example of such a CLI script can be found below:

 

if (result.should-renew-certificate == true) of /subsystem=elytron/key-store=serverKS:should-renew-certificate(alias=server)

    # certificate is due for renewal in less than 30 days, obtain a new certificate to replace the existing one in the key-store
    /subsystem=elytron/key-store=serverKS:obtain-certificate(alias=server,domain-names=[www.example.org],certificate-authority-account=myLetsEncryptAccount,agree-to-terms-of-service)

    # re-initialize your key-manager to ensure your new certificate will be used without needing to restart the server
    /subsystem=elytron/key-manager=httpsKM:init()

end-if

 

Notice that in the above script, after renewing the certificate, we can simply execute the key-manager init command in order to ensure that the new certificate will be used by the key-manager from now on without needing to restart WildFly.

 

Updating the contact URLs associated with your Let’s Encrypt account

 

To update the contact URLs that are associated with your Let’s Encrypt, the following commands can be used:

 

/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:write-attribute(name=contact-urls,value=[mailto:newadmin@admin.org])
reload
/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:update-account()

 

Changing your Let’s Encrypt account key

 

If you ever want to change the key that is associated with your Let’s Encrypt account (e.g., in the event of a key compromise), the change-account-key command can be used:

 

/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:change-account-key()

 

Deactivating your Let’s Encrypt account

 

If you ever need to deactivate your Let’s Encrypt account, the deactivate-account command can be used:

 

/subsystem=elytron/certificate-authority-account=myLetsEncryptAccount:deactivate-account()

 

Summary

 

This blog post has given an overview on how to obtain and manage certificates from the Let’s Encrypt certificate authority using the WildFly CLI. For more information on the Elytron subsystem, check out the Elytron documentation.

With WildFly 13, there’s a new way to configure permissions in the Elytron subsystem. In particular, it is now possible to configure permissions using a new permission-set resource.

 

Configuring permission sets

 

Adding a permission-set takes the following general form:

 

/subsystem=elytron/permission-set=MyPermissionSetName:add(permissions=[{class-name="...", module="...", target-name="...", action="..."}...])

 

In the above command, permissions consists of a set of permissions, where each permission can have the following attributes:

  • class-name - the fully qualified class name of the permission (this is the only permission attribute that is required)
  • module - the optional module to use to load the permission
  • target-name - the optional target name to pass to the permission as it is constructed
  • action - the optional action to pass to the permission as it is constructed

 

After a permission-set has been created, it can be referenced when creating a permission mapper in order to assign permissions to an identity.

 

Example configuration

 

As an example, the following command can be used to add a new permission-set that contains the org.wildfly.security.auth.permission.RunAsPrincipalPermission:

 

/subsystem=elytron/permission-set=run-as-principal-permission:add(permissions=[{class-name="org.wildfly.security.auth.permission.RunAsPrincipalPermission", target-name="*"}])

 

This results in the following configuration in the Elytron subsystem (note that the login-permission and default-permissions permission sets are already present in the default Elytron subsystem configuration):

 

<subsystem xmlns="urn:wildfly:elytron:3.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
    <permission-sets>
        <permission-set name="login-permission">
            <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
        </permission-set>
        <permission-set name="default-permissions">
            <permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
            <permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
            <permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
        </permission-set>
        <permission-set name="run-as-principal-permission">
            <permission class-name="org.wildfly.security.auth.permission.RunAsPrincipalPermission" target-name="*"/>
        </permission-set>
    </permission-sets>
...
</subsystem>

 

Next, create a simple permission mapper that references the newly created run-as-principal-permission permission set:

 

/subsystem=elytron/simple-permission-mapper=my-simple-permission-mapper:add(permission-mappings=[{principals=["anonymous"]}, \
{principals=["server1"], permission-sets=[{permission-set=login-permission}, {permission-set=default-permissions}, {permission-set=run-as-principal-permission}]}, \
{match-all=true,permission-sets=[{permission-set=login-permission}, {permission-set=default-permissions}]}])

 

This results in the following configuration in the Elytron subsystem:

 

<subsystem xmlns="urn:wildfly:elytron:3.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
    <mappers>
    ...
        <simple-permission-mapper name="my-simple-permission-mapper">
            <permission-mapping>
                <principal name="anonymous"/>
            </permission-mapping>
            <permission-mapping>
                <principal name="server1"/>
                <permission-set name="login-permission"/>
                <permission-set name="default-permissions"/>
                <permission-set name="run-as-principal-permission"/>
            </permission-mapping>
            <permission-mapping match-all="true">
                <permission-set name="login-permission"/>
                <permission-set name="default-permissions"/>
            </permission-mapping>
        </simple-permission-mapper>
    ...
    </mappers>
...
</subsystem>

 

The above command creates a simple permission mapper that:

  • Assigns no permissions to an anonymous user
  • Assigns the permissions referenced in the login-permission, default-permissions, and run-as-principal-permission permission sets to the server1 user
  • Assigns the permissions referenced in the login-permission and default-permissions permission sets to all other users

 

Summary

 

This blog post has given an overview of Elytron permission sets. For more information about the Elytron subsystem, check out the Elytron documentation.

With WildFly 12, it is now possible to perform various KeyStore manipulation operations on a key-store resource in the Elytron subsystem using the JBoss CLI. In particular, the new operations make it possible to:

  • Generate a key pair
  • Generate a certificate signing request (CSR)
  • Import a certificate or a certificate chain from a file
  • Export a certificate to a file
  • Change an existing alias

 

With these new operations, it is now possible to set up one-way and two-way SSL for applications and management interfaces using only the CLI - going back and forth between the CLI and keytool is no longer necessary. This blog post is going to give an overview of these new operations.

 

To start the server, use the following command:

$WILDFLY_HOME/bin/standalone.sh  

 

To connect to the running server to execute CLI commands, use:

$WILDFLY_HOME/bin/jboss-cli.sh --connect

 

Prerequisite configuration

 

First, configure a key-store in the Elytron subsystem. Note that the path to the keystore file doesn’t actually have to exist yet.

 

/subsystem=elytron/key-store=exampleKS:add(path=server.keystore.jks, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=JKS)

 

Generating a key pair

 

The generate-key-pair command generates a key pair and wraps the resulting public key in a self-signed X.509 certificate. The generated private key and self-signed certificate will be added to a new PrivateKeyEntry in the KeyStore.

 

/subsystem=elytron/key-store=exampleKS:generate-key-pair(alias=example, algorithm=RSA, key-size=1024, validity=365, credential-reference={clear-text=secret}, distinguished-name="CN=www.example.com")

 

After performing the above command, you can check the alias names in the KeyStore and confirm the new alias, "example", is listed:

 

/subsystem=elytron/key-store=exampleKS:read-aliases()  
{  
    "outcome" => "success",  
    "result" => ["example"]  
}  

 

Generating a certificate signing request (CSR)

 

The generate-certificate-signing-request command generates a PKCS #10 CSR using a PrivateKeyEntry from the KeyStore. The generated CSR will be output to a file (in the example below, the CSR is output to server.csr).

 

/subsystem=elytron/key-store=exampleKS:generate-certificate-signing-request(alias=example, path=server.csr, relative-to=jboss.server.config.dir, distinguished-name="CN=www.example.com", \
extensions=[{critical=false, name=KeyUsage,value=digitalSignature}], credential-reference={clear-text=secret}) 

 

Notice that in the above command, alias=example refers to the PrivateKeyEntry that was created using the generate-key-pair command.

 

Importing a certificate or certificate chain from a file

 

The import-certificate command imports a certificate or certificate chain from a file into an entry in the KeyStore. This can be used to either import a trusted certificate or to import a certificate reply that’s received after submitting a CSR to a certificate authority.

 

/subsystem=elytron/key-store=exampleKS:import-certificate(alias=example, path=/path/to/certificate/chain/file, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, trust-cacerts=true)

 

Exporting a certificate to a file

 

The export-certificate command exports a certificate from an entry in the KeyStore to a file (in the example below, the certificate is exported to serverCert.cer).

 

/subsystem=elytron/key-store=exampleKS:export-certificate(alias=example, path=serverCert.cer, relative-to=jboss.server.config.dir, pem=true)

 

Changing an existing alias

 

The change-alias command moves an existing KeyStore entry to a new alias.

 

/subsystem=elytron/key-store=exampleKS:change-alias(alias=example, new-alias=new-example, credential-reference={clear-text=secret})

 

After performing the above command, you can check the alias names in the KeyStore and confirm the new alias name, "new-example", is listed:

 

/subsystem=elytron/key-store=exampleKS:read-aliases()
{
    "outcome" => "success",
    "result" => ["new-example"]
}

 

Storing changes

 

The store command persists any changes that you have made using the above commands to the file that backs the KeyStore.

 

/subsystem=elytron/key-store=exampleKS:store()

 

Summary

 

This blog post has given an overview of the new KeyStore manipulation operations that are available via the CLI in WildFly 12. For information on how to set up one-way and two-way SSL for applications and management interfaces, check out the Elytron documentation.

Some SASL mechanisms support channel binding to external secure channels like TLS. The name of a SASL mechanism tells us if channel binding is supported. In particular, SASL mechanisms that support the optional use of channel binding have two SASL mechanism names - one name that includes the “-PLUS” suffix, which implies that channel binding is supported, and one name without the “-PLUS” suffix, which implies that channel binding is not supported (e.g., GS2-KRB5 and GS2-KRB5-PLUS, SCRAM-SHA-256 and SCRAM-SHA-256-PLUS, etc.). Whether or not channel binding is used is determined during SASL mechanism negotiation.

 

This blog post is going to show the server and client configuration needed to connect to the JBoss CLI using the SCRAM-SHA-256-PLUS mechanism, one of the SASL PLUS mechanisms provided by Elytron.

 

Prerequisite configuration

 

First, add a management user for the server - this is the user that we’re going to use later on when attempting to connect to the CLI. For this example, we’re going to create a filesystem-based identity store and add a user named “bob” with password “pAssw0rd” using the following CLI commands:

 

/subsystem=elytron/filesystem-realm=exampleRealm:add(path=fs-realm-users,relative-to=jboss.server.config.dir)
/subsystem=elytron/filesystem-realm=exampleRealm:add-identity(identity=bob)
/subsystem=elytron/filesystem-realm=exampleRealm:set-password(identity=bob,clear={password=pAssw0rd})

 

Now, we’re going to add the filesystem-realm that we just created to the “ManagementDomain” security domain that is already defined in the default Elytron subsystem configuration and we’re going to make this the default security realm for this security domain:

 

/subsystem=elytron/security-domain=ManagementDomain:list-add(name=realms, value={realm=exampleRealm})
/subsystem=elytron/security-domain=ManagementDomain:write-attribute(name=default-realm, value=exampleRealm)

 

Next, use the following commands to secure the management interface using Elytron:

 

/core-service=management/management-interface=http-interface:write-attribute(name=http-upgrade,value={enabled=true, sasl-authentication-factory=management-sasl-authentication})
/core-service=management/management-interface=http-interface:write-attribute(name=http-authentication-factory,value=management-http-authentication)
/core-service=management/management-interface=http-interface:undefine-attribute(name=security-realm)

 

Finally, generate a server keystore and a client truststore using the keytool command, as shown below. We’re going to use these to enable one-way SSL/TLS for the management interface.

 

Generate the server keystore:

keytool -genkeypair -alias localhost -keyalg RSA -keysize 1024 -validity 365 -keystore server.keystore.jks -dname "CN=localhost" -keypass secret -storepass secret

 

Export the server certificate:

keytool -exportcert  -keystore server.keystore.jks -alias localhost -keypass secret -storepass secret -file server.cer

 

Import the server certificate into the client’s truststore:

keytool -importcert -keystore client.truststore.jks -storepass secret -alias localhost -trustcacerts -file server.cer

 

Now we’re ready to proceed with the server and client configuration needed to use the SCRAM-SHA-256-PLUS mechanism.

 

Configuring the server

 

First, configure a key-store, key-manager, and server-ssl-context in the Elytron subsystem using the server keystore that we just created (the following commands assume the server.keystore.jks file is located in the $WILDFLY_HOME/standalone/configuration directory):

 

/subsystem=elytron/key-store=exampleKS:add(path=server.keystore.jks, relative-to=jboss.server.config.dir, credential-reference={clear-text=secret}, type=JKS)
/subsystem=elytron/key-manager=exampleKM:add(key-store=exampleKS, credential-reference={clear-text=secret})  
/subsystem=elytron/server-ssl-context=exampleSSC:add(key-manager=exampleKM, protocols=["TLSv1.2"])

 

Next, enable HTTPS on the management interface using the newly created server-ssl-context:

 

/core-service=management/management-interface=http-interface:write-attribute(name=ssl-context, value=exampleSSC)
/core-service=management/management-interface=http-interface:write-attribute(name=secure-socket-binding, value=management-https)

 

Now, update the “management-sasl-authentication” SASL authentication factory to also offer the SCRAM-SHA-256-PLUS mechanism:

 

/subsystem=elytron/sasl-authentication-factory=management-sasl-authentication:list-add(name=mechanism-configurations, value={mechanism-name=SCRAM-SHA-256-PLUS})

 

Finally, reload the server using the :reload command.

 

Configuring the client

 

We can use a wildfly-config.xml file to provide the information that’s needed to connect to the CLI:

 

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="auth-config"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="auth-config">
                <sasl-mechanism-selector selector="SCRAM-SHA-256-PLUS"/>
                <set-user-name name="bob"/>  
                <credentials>  
                    <clear-password password="pAssw0rd"/>  
                </credentials> 
            </configuration>
        </authentication-configurations>
        <key-stores>
            <key-store name="truststore" type="JKS">
                <file name="/path/to/client.truststore.jks" />
                <key-store-clear-password password="secret" />
            </key-store>
        </key-stores>
        <ssl-contexts>
            <ssl-context name="client-cli-context">
                <trust-store key-store-name="truststore" />
            </ssl-context>
        </ssl-contexts>
        <ssl-context-rules>
            <rule use-ssl-context="client-cli-context" />
        </ssl-context-rules>
    </authentication-client>
</configuration>

 

Notice that the wildfly-config.xml file specifies that the SCRAM-SHA-256-PLUS mechanism should be used and the username and password that should be used when attempting to connect to the CLI. It also configures an ssl-context using the client truststore that we created earlier.

 

Now, we just need to specify this wildfly-config.xml file when connecting to the CLI. The following command connects to the CLI and executes the :whoami command.

 

$WILDFLY_HOME/jboss-cli.sh -c --controller=remote+https://127.0.0.1:9993 -Dwildfly.config.url=/path/to/wildfly-config.xml :whoami

 

You should see the following output, which indicates that we’ve successfully connected to the CLI using the SCRAM-SHA-256-PLUS mechanism.

 

{
    "outcome" => "success",
    "result" => {"identity" => {"username" => "bob"}}
}

 

Summary

 

This blog post has shown how to set up one-way SSL/TLS for the management interface and how to then use a SASL mechanism that supports channel binding to connect to the CLI.

My previous blog post described how to secure EJBs deployed to WildFly 11 using Elytron and how to invoke them from a standalone remote client. This post describes how to invoke EJBs deployed on a WildFly server instance from another WildFly server instance using Elytron. We’ll refer to the server instance on which the EJBs are deployed as the destination server and we’ll refer to the server instance from which the EJB invocation takes place as the client server.

 

Configuring the client server

As in previous WildFly releases, to be able to invoke EJBs deployed on the destination server, you can add configuration to the Remoting subsystem on the client server to specify the information needed for the outbound connection to the destination server. In WildFly 11, a remote outbound connection can now be specified by two things: an Elytron authentication context and an outbound socket binding.

 

Creating an authentication context

The authentication context provides all of the security information that’s needed to connect to the destination server. For example, if you would like to use a user named “ejbUser” with password “secret” when connecting to the destination server, the following CLI commands can be used to create an appropriate authentication context:

 

/subsystem=elytron/authentication-configuration=ejb-auth-config:add(authentication-name=ejbUser, credential-reference={clear-text="secret"})
/subsystem=elytron/authentication-context=ejb-auth-context:add(match-rules=[{authentication-configuration=ejb-auth-config}])

 

The above commands result in the following configuration in the Elytron subsystem on the client server:

 

<subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
    <authentication-client>
        <authentication-configuration name="ejb-auth-config" authentication-name="ejbUser">
            <credential-reference clear-text="secret"/>
        </authentication-configuration>
        <authentication-context name="ejb-auth-context">
            <match-rule authentication-configuration="ejb-auth-config"/>
        </authentication-context>
    </authentication-client>
...
</subsystem>

 

Creating an outbound socket binding

As before, the outbound socket binding points to the destination server’s host and port for the connection. For example, if the destination server’s host is 10.20.30.40 and its port is 8080, the following CLI command can be used to create an outbound socket binding:

 

/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-ejb:add(host=10.20.30.40, port=8080)

 

Creating a remote outbound connection

Finally, you can create a remote-outbound-connection that references your newly created authentication context and outbound socket binding as follows:

 

/subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(authentication-context=ejb-auth-context, outbound-socket-binding-ref=remote-ejb)

 

The above command results in the following configuration in the Remoting subsystem on the client server:

 

<subsystem xmlns="urn:jboss:domain:remoting:4.0">
...
    <outbound-connections>
        <remote-outbound-connection name="remote-ejb-connection" outbound-socket-binding-ref="remote-ejb" authentication-context="ejb-auth-context"/>
    </outbound-connections>
...
</subsystem>

 

Invoking EJBs from a client server

As in my previous post, update any client code that looks up an EJB deployed on the destination server using JNDI to make use of the new Naming Client library. As an example, here’s a simple code snippet that could be used to look up an EJB deployed on the destination server using the new WildFlyInitialContextFactory:

 

Code snippet

// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
InitialContext context = new InitialContext(properties);

// look up an EJB and invoke one of its methods
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
    "ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);

 

Summary

This blog post has shown how to invoke EJBs deployed on a WildFly server instance from another WildFly server instance using Elytron. If you’d like to see a more advanced example application that shows how to propagate the security identity of an EJB deployed on the client server to an EJB deployed on the destination server, take a look at the ejb-security-context-propagation quickstart.

This blog post describes how to secure EJBs deployed to WildFly 11 using Elytron and how to invoke them from a standalone remote client. If you haven’t already, take a look at the Elytron documentation  on how to create security realms, security domains, and authentication factories. In this post, we’ll make use of a security domain (“ApplicationDomain”) and a SASL authentication factory (“application-sasl-authentication”) that are already defined in the default WildFly configuration file in the Elytron subsystem configuration.

 

To start the server, use the following command:

$WILDFLY_HOME/bin/standalone.sh

 

To connect to the running server to execute CLI commands, use:

$WILDFLY_HOME/bin/jboss-cli.sh --connect

 

Securing EJBs with Elytron

By default, security for EJBs deployed to WildFly 11 will continue to be handled by the legacy security subsystem. To indicate that security for an EJB should be handled by the Elytron subsystem instead, you need to add some configuration to the EJB subsystem to map the security domain name that’s configured for an EJB in a deployment to the Elytron security domain that you would like to use. For example, if “other” is the security domain name that is configured for an EJB (e.g., via a @SecurityDomain annotation or in a jboss-ejb3.xml deployment descriptor) and “ApplicationDomain” is the Elytron security domain that you would like to use, the following CLI command can be used to add the required mapping:

 

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

 

The above command results in the following configuration in the EJB subsystem:

 

<subsystem xmlns="urn:jboss:domain:ejb3:5.0">
...
    <application-security-domains>
        <application-security-domain name="other" security-domain="ApplicationDomain"/>
    </application-security-domains>
...
</subsystem>

 

Notice that an application-security-domain mapping has two main attributes:

  • name - the name of the security domain as specified in a deployment
  • security-domain - a reference to the Elytron security domain that should be used

 

Next, update the http-remoting-connector in the Remoting subsystem to reference the SASL authentication factory that is backed by your Elytron security domain:

 

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

 

The latter command above just clears the legacy security-realm attribute since it is no longer needed.

 

Finally, reload the server using the :reload command.

 

Invoking EJBs from a standalone remote client

Prior to WildFly 11, many WildFly client libraries used different configuration strategies. WildFly 11 introduces a new wildfly-config.xml file which unifies all client configuration in a single place. EJBs deployed to WildFly 11 can still be invoked using existing standalone remote clients that make use of the legacy naming and EJB client libraries from previous WildFly releases. This section walks through an example of how to migrate a remote client to make use of the new WildFly Naming Client and EJB Client libraries.

 

Consider a legacy client application that has a jboss-ejb-client.properties configuration file and looks up an EJB deployed on a remote server using JNDI, as follows:

 

jboss-ejb-client.properties

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=10.20.30.40
remote.connection.default.port=8080
remote.connection.default.username=bob
remote.connection.default.password=secret

 

Code snippet

// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext context = new InitialContext(properties);

// look up an EJB and invoke one of its methods
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
    "ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);

 

To migrate this example to the new Naming Client and EJB Client libraries, the first thing you can do is remove the jboss-ejb-client.properties file since it is no longer needed. Instead, all of the information that’s needed to connect to the remote server can be specified in a wildfly-config.xml file in the client application’s META-INF directory, as follows:

 

wildfly-config.xml

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
            <rule use-configuration="default"/>
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
                <set-user-name name="bob"/>
                <credentials>
                    <clear-password password="secret"/>
                </credentials>
            </configuration>
        </authentication-configurations>
    </authentication-client>
    <jboss-ejb-client xmlns="urn:jboss:wildfly-client-ejb:3.0">
        <connections>
            <connection uri="remote+http://10.20.30.40:8080" />
        </connections>
    </jboss-ejb-client>
</configuration>

 

To learn more about the contents of the above file, take a look at the Elytron Client documentation.

 

Next, update the client code that sets up the InitialContext to make use of the new WildFlyInitialContextFactory from the new Naming Client library:

 

Code snippet

// create an InitialContext
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
InitialContext context = new InitialContext(properties);

// look up an EJB and invoke one of its methods (this part is the same as before)
RemoteCalculator statelessRemoteCalculator = (RemoteCalculator) context.lookup(
    "ejb:/ejb-remote-server-side//CalculatorBean!" + RemoteCalculator.class.getName());
int sum = statelessRemoteCalculator.add(101, 202);

 

Additional client migration examples can also be found here.

 

Summary

This blog post has shown how to secure EJBs deployed to WildFly 11 with Elytron and how to invoke them from a standalone remote client using the new Naming Client and EJB Client libraries. If you’d like to see a complete example application that pulls everything described here together, take a look at the ejb-security quickstart. My next post will cover EJB invocations from a remote server using Elytron.

Since WildFly 10 Final was just released, the OpenShift WildFly cartridge has been updated as well. This post will walk you through getting started on OpenShift and deploying a WildFly quickstart. If you already have an existing WildFly OpenShift app, you'll want to check out these tips for migrating your app to WildFly 10 Final.

 

Creating a new app

There are a few options for creating an app on OpenShift:

  • To use the OpenShift Web Console, just click here.
  • To get started directly from Eclipse, you'll first need to install the OpenShift JBoss Tools package. Then, simply use the New OpenShift Application Wizard to create your app.
  • With JBoss Developer Studio, the OpenShift JBoss Tools package is already installed so you can get started right away with the New OpenShift Application Wizard.
  • To use the CLI, make sure you've got the OpenShift Client Tools installed and then execute the following command:
    rhc app create <WILDFLY_APP_NAME> jboss-wildfly-10

 

WildFly management options

Once you've created your app, it will be available at http://<WILDFLY_APP_NAME>-<NAMESPACE>-rhcloud.com. You can then use the WildFly CLI or Admin Console to manage your server.


To connect to the CLI:

rhc ssh <WILDFLY_APP_NAME> # This command allows you to ssh into your OpenShift app
jboss-cli.sh -c --controller=$OPENSHIFT_WILDFLY_IP:$OPENSHIFT_WILDFLY_MANAGEMENT_HTTP_PORT


When logging into the Admin Console, you can use the credentials for the WildFly management user that's automatically created for you by the WildFly cartridge. To obtain the necessary credentials:

rhc ssh <WILDFLY_APP_NAME>
echo $OPENSHIFT_WILDFLY_USERNAME
echo $OPENSHIFT_WILDFLY_PASSWORD


Then, using port forwarding, you're ready to access the Admin Console from your local machine:

rhc port-forward <WILDFLY_APP_NAME>


Deploying a WildFly quickstart

Getting started with a WildFly quickstart on OpenShift is simple. The following steps will demonstrate how to deploy the kitchensink quickstart, an application that demonstrates a few different Java EE 7 technologies, using the OpenShift Client Tools.


If you created your OpenShift app using the CLI, Eclipse, or JBoss Developer Studio, you should already have a clone of your app's OpenShift git repository on your local machine. If you created your app using the OpenShift Web Console, you'll need to first clone your app's git repository as follows:

rhc git-clone <WILDFLY_APP_NAME>

 

Next, navigate to your app's git repository on your machine and remove the source for the default application that's created by the WildFly cartridge:

git rm -r src pom.xml

 

Now, copy over the source for the kitchensink quickstart:

cp -r QUICKSTART_HOME/kitchensink/src .
cp QUICKSTART_HOME/kitchensink/pom.xml .

 

Next, push your changes:

git add src pom.xml
git commit -m "Deploying the kitchensink quickstart on WildFly 10 on OpenShift!"
git push

 

That's it! The kitchensink quickstart will now be available at your application URL (i.e., http://<WILDFLY_APP_NAME>-<NAMESPACE>-rhcloud.com).


Enjoy!


This post provides some tips on how to migrate an existing OpenShift application to WildFly 10 Final.

 

Migrating from WildFly 10 Alpha/Beta/CR

If you already have an existing WildFly 10 Alpha/Beta/CR app, you can simply create a new WildFly 10 app based on the existing app using the following command:

rhc app create <NEW_WILDFLY_APP> --from-app <OLD_WILDFLY_APP>

 

The result will be a new WildFly 10 Final application that's a clone of your existing one. This means that your new app will have the same git repository, gear size, scaling configuration, environment variables, deployments, etc.

 

Migrating from WildFly 8 or WildFly 9

You can create a new WildFly 10 Final app that's based off your existing app's git repository. There are a couple ways to do this. One option is to first create a new WildFly 10 app and then manually copy over the contents of your old git repo to your new git repo once the new app has been created. Another option is to use the following command:

rhc app create <NEW_WILDFLY_APP> jboss-wildfly-10 --from-code http://url/to/git_repo.git

 

With both of these options, you should first replace the standalone.xml file from your git repo's .openshift/config directory with the WildFly 10 standalone.xml file.

 

Migrating from AS 7

You can create a new WildFly 10 Final app that's based off your existing app's git repository, as described above. However, there are some extra caveats that you should be aware of:

  • If your git repo contains any action hook scripts that end with "_jbossas", these scripts will need to be renamed to end with "_wildfly" instead.
  • If your git repo contains any action hook scripts that reference OpenShift environment variables that start with "OPENSHIFT_JBOSSAS_", these scripts will need to be updated to reference "OPENSHIFT_WILDFLY_" instead.

To see if you have any such scripts, have a look in the .openshift/action_hooks directory in your git repo.


Happy migrating!