Wednesday, 26 September 2018

WildFly Elytron - Credential Store - Next Steps

During the development of WildFly 11 where we introduced the WildFly Elytron project to the application server one of the new features we added was the credential store.

https://developers.redhat.com/blog/2017/12/14/new-jboss-eap-7-1-credential-store/

Now that we are planning the next stages of development for WildFly 15 and 16 we are revisiting some of the next steps for the credential store, this blog post explores some of the history of the current decisions made with the credential store and the types of enhancement being requested to develop this further.

Anyone making use of either the CredentialStore or Vault is encouraged to provider your feedback so we can take this into account as the next stages are planned.

Key Differences To Vault

Prior to the credential store the PicketBox vault was the solution used for the encryption of credentials and other fields within the application server's configuration, the credential store approach was dedicated to the secure storage of credentials.

Where the PicketBox vault is used in the application server a single Vault is defined across the whole server and aliases from the vault are referenced via expressions in the server's configuration which allows for the values to be retrieved in clear text.

The credential store on the other hand allows for multiple credential store instances to be defined, resources that make use of the credentials have been updated to a special attribute type where both the name of the credential store can be specified and the alias of the credential within the credential store.

The credential store resources additionally support management operations to allow for entries within the credential store to be manipulated either by adding / removing entries or by updating existing entries.

Uses of the Store

Reviewing where the credential store is used we seem to have two predominant scenarios.

Unlocking Local Resources

In this case a credential is required to unlock a local resource such as a KeyStore, there is no remote authentication to be performed and a credential is generally only required for decrypting the contents of the store.  This is the simplest use of the store and once the resource is unlocked it is not likely to need unlocking again.

Accessing Remote Resources

The second use we see is for services accessing a remote resource, in this case the credentials for the connection are obtained from the credential store.

This scenario has a reasonable amount of history also attached to the current implementation, it tended to be the case that if you were to access a remote resource it would either not be secured or it would be secured and authentication would require a username and password.  Additionally any SSL related configuration would be handled completely independently.

In recent years however there has been a greater demand for alternative authentication mechanisms, there has been a lot of demand for Kerberos both with the server being given it's own account for authentication and also for the propagation of a remote user authenticated against the server using Kerberos.  We haven't seen requests yet for the application server but I suspect OAuth scenarios will be requested soon.

In both of these cases the security has moved from username / password authentication to more advanced scenarios.

In parallel to the credential store WildFly Elytron has also introduced an Authentication Client API, this API can be used for configuring the client side authentication policies for various mechanisms, including scenarios that support propagation of the current identity.  The authentication client configuration also allows SSLContext configurations to be associated with a specific destination.

This now raises the question, should services establishing a remote connection which requires authentication and SSL configuration now reference an authentication client configuration instead of the current assumption that a username and credential reference is sufficient?

This question becomes quite important as it affects the approach we take to a lot of the enhancements requested of us quite significantly.

Next Features

The features currently being requested against the credential store generally cover three broad areas: -

  1. Automation of updates to the store.
  2. Real time updates.
  3. Support for expressions.

Automation of Updates to the Store

The general motivation for this enhancement is to simplify the steps configuring resources which require credentials, using the PicketBox Vault implementation the vault would need to be manipulated offline using a command line tool and then references from the application server configuration.

The CredentialStore has already moved on from this partially as management operations have been exposed to allow the contents of the store to be directly manipulated using management requests so the store can be manipulated directly from the management tools.  However an administrator is still required to operate on the two resources completely independently.

We predominantly have two options to simplify this further.

We could take the decision that further enhancement is now a tooling issue, the admin clients could detect a resource is being added that supports credential store references or the password on an existing resource that supports credential store references is being set and provide guidance / automation to persist the credential in the credential store.

  1. Would you like to store this credential in a credential store?
  2. Which credential store would you like to use? Or would you like to create a new one?
  3. What alias would you like the credential to be stored under?

Alternatively, the credential reference attribute supports specifying a reference to a credential store and an alias in the credential store - this attribute however also supports specifying a clear text password.  We could automate the manipulation in the management tier and if a clear text password is specified on a resource referencing a credential store automatically add it to the credential store and remove it from the model - if no alias is specified automatically generate one.

We could also support a combination of the two approaches as in the management tier although we could support the interception of a new clear password if a store needs to be created that would be more involved than we could automate.

Real Time Updates

Where new credentials are added to a credential store using management operations they are already available for use immediately in the application server process without a restart.  However we still have some areas to consider further real time update support: -
  1. Updates to the store on the filesystem.
  2. Complete replacement of an existing store.
  3. Updates to credentials using management operations.

In these three cases the primary interest is credentials which are already in use in the application server, however in the case of #1 and #2 it could relate to the addition of new credentials to be used immediately.

One point to consider is although our default credential store implementation is file based custom implementations could be in use which are not making use of any local files.

At the credential store level we likely should consider various modes to detect changes when emitting notifications: -
  1. File system monitoring.
  2. Notifications from within the implementation of the store.
  3. Administrator triggered reloads of either the full store or individual aliases.
At the service level where a service is making use of a credential it is likely we would want to decide how to handle updates on a service by service basis.  It is unlikely we would want to automatically restart / replace services as for some services which make use of credentials this could cause a cascade of service restarts potentially leading the redeployment of deployments.

I expect some form of notifications will be required, at the coarsest level notifications could be emitted for all services accessing a specific credential store - this however could trigger a significant overhead as a single store could contain a large number of entries used across a large number of resources.  Instead we could emit notifications just to the resources using the affected aliases, this would be more efficient from the perspective of the notifications but the complexity now is where a coarse update has been updates to a store such as the underlying file being replaced or a full store refresh being triggered by an administrator we now need to identify which credentials were really modified.

Support for Expressions

It was a deliberate decision to move away from using expressions, however there are still some demands for expressions that need to be considered.

Overall the design decisions within WildFly Elytron have always considered a desire to move away from clear text passwords being present in the application server process, where expressions are used the only route available is to obtain the clear text representation of a String and pass it around the related services.

One of the enhancements delivered for WildFly 11 was to support multiple credential stores concurrently within the application server, by moving to the complex attribute we were able to make use of capability references to select which credential store to use with a second attribute selecting the alias in the store.

Another consideration was the desire for automatic updates to be applied via the management tier, by moving from expressions to a complex attribute it opens up the options to intercept these values and persist them in the store.

A further (and possibly the greatest) consideration was the desired support for automatic updates to credentials currently in use in the server, by moving to the capability references aided by the complex attribute definition services can now obtain a direct reference to the store instead of having a credential automatically resolved.  By having a reference to a credential store we can potentially add support for direct notifications of updates applied to that store.  Where a credential is updated different services may want to respond in different ways, this is why a reference is needed.  Within the management tier we can not silently automate the updates, if we were to do so it would likely involve the removal and replacement of the service which could have a side effect of restarting many other services including the deployments.

The biggest restriction of not supporting expressions is attributes for anything other than a credential can no longer be loaded from the credential store - but the missing piece of information is how that is really used and why?

As an example usernames could be loaded using expressions, is this because in some environments the username is being considered as sensitive as the credential?  Or is it the case that where a credential is loaded from a store it is easier to load the username from the same store.

If the answer is co-location of the username and password then a more suitable path to look into may be the externalisation of the authentication client configuration allowing the complete client authentication policy to be handled as a single unit.

If we are still left with attributes in the configuration that need to be stored securely the next question is do they strictly need to be removed from the configuration and looked up from the store?  An alternative option we have to consider is supporting the encryption / decryption of Strings inlined in the management model using a credential from the store.

Other Enhancements

Following on from the main enhancements listed above there is also a set of additional enhancements we could consider.

Injection / Credential Store Access for Deployments

Deployments can already access the authentication client configuration, however if access to raw credentials is required it may help to access the store - this could additionally mean a deployment could manipulate the store.

Permission Checks

Once accessed within deployments, making use of the SecurityIdentity permission checks we could perform permission checks for different read / write operations on the credential store.

Auditing

The credential store could be updated to emit security events as it is accessed, by emitting security events these can be output to audit logs or sent to other event analysing frameworks.













Thursday, 8 February 2018

WildFly Elytron - Implementing a Custom HTTP Authentication Mechanism

When WildFly Elytron is used to secure a web application is is possible to implement custom HTTP authentication mechanisms that can be registered using the Elytron subsystem, it is then possible to override the configuration within the deployment to make use of this mechanism without requiring modifications to the deployment.

This blog post explores a custom authentication mechanism that can make use of custom HTTP headers to receive a clear text username and password and use these for authentication.  Generally passing clear text passwords is something that should be voided, they are only used here to avoid over complicating the example.

This blog will be making use of two projects within the elytron-examples respository that can be found at: -

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


  • simple-webapp - A very simple secured web application that can be deployed to test the mechanism.
  • simple-http-mechanism - A small project containing the minimal pieces to develop a custom mechanism.

Mechanism Implementation

The major piece of any custom HTTP mechanism is going to be the actual implementation of the mechanism, all custom mechanisms are required to implement the interface HttpServerAuthenticationMechanism.html.

In general for a mechanism the evaluateRequest method is called to handle the request passing in a HttpServerRequest object, the mechanism processes the requests and uses one of the following callback methods on the request to indicate the outcome: -
For each of these callback methods it is possible to pass in an instance of a HttpServerMechanismResponder which can be used to send challenge information to the calling client, which responders are called will depend very much on the outcome of the other mechanisms in use concurrently.


The custom mechanism implemented for this blog can be found at CustomHeaderHttpAuthenticationMechanism.java

Where this custom mechanism sends a challenge the challenge is always the same so we can use a static instance of the responder to avoid creating a new one each time we need to challenge: -

    private static final HttpServerMechanismsResponder RESPONDER = new      
            HttpServerMechanismsResponder() {       
        public void sendResponse(HttpServerResponse response) throws 
                HttpAuthenticationException {
            response.addResponseHeader(MESSAGE_HEADER, "Please resubit the request with a username specified using the X-USERNAME and a password specified using the X-PASSWORD header.");
            response.setStatusCode(401);
        }
    };


The evaluateRequest method then follows a path that will be common to many mechanisms.


final String username = request.getFirstRequestHeaderValue(USERNAME_HEADER);
final String password = request.getFirstRequestHeaderValue(PASSWORD_HEADER);

if (username == null || username.length() == 0 || password == null || 
      password.length() == 0) {    
    request.noAuthenticationInProgress(RESPONDER);
    return;
}


In this first block the mechanism tests if it has authentication headers appropriate for this mechanism, if not 'noAuthenticationInProgress' is called to notify the framework this mechanism is not doing anything yet and passes the responder in case a response is needed to challenge the client.


NameCallback nameCallback = new NameCallback("Remote Authentication Name", 
                                              username);
nameCallback.setName(username);
final PasswordGuessEvidence evidence = new 
                PasswordGuessEvidence(password.toCharArray());
EvidenceVerifyCallback evidenceVerifyCallback = new 
                EvidenceVerifyCallback(evidence);
try {
    callbackHandler.handle(new Callback[] { nameCallback, 
                                            evidenceVerifyCallback });
} catch (IOException | UnsupportedCallbackException e) {
    throw new HttpAuthenticationException(e);
}

if (evidenceVerifyCallback.isVerified() == false) {
    request.authenticationFailed("Username / Password Validation Failed", 
                                 RESPONDER);
}


In this second block the mechanism takes the headers received from the client and uses them to perform authentication by passing Callbacks to the provided CallbackHandler.

After a successful authentication it is possible to associate any credentials received from the client with the resulting identity, this step is optional but can be useful.


try {
    callbackHandler.handle(new Callback[] {new IdentityCredentialCallback(
        new PasswordCredential(ClearPassword.createRaw(
                ClearPassword.ALGORITHM_CLEAR, 
                password.toCharArray())), true)});
} catch (IOException | UnsupportedCallbackException e) {
    throw new HttpAuthenticationException(e);
}

At this point the identity has been authenticated and the credential associated but no check has been performed to ensure the identity is allowed to login so the next step is an authorization.

try {
    callbackHandler.handle(new Callback[] {authorizeCallback});

    if (authorizeCallback.isAuthorized()) {
        callbackHandler.handle(new Callback[] { 
            AuthenticationCompleteCallback.SUCCEEDED });
        request.authenticationComplete();
    } else {
        callbackHandler.handle(new Callback[] { 
            AuthenticationCompleteCallback.FAILED });
        request.authenticationFailed("Authorization check failed.", RESPONDER);
    }
    return;
} catch (IOException | UnsupportedCallbackException e) {
    throw new HttpAuthenticationException(e);
}

In this last block finally an AuthenticationCompleteCallback is needed to indicate the overall outcome of the authentication as decided by the mechanism, the reason the mechanism makes this decision is because a mechanism could take into account additional information in addition beyond the outcomes from the callbacks - an an example the HTTP Digest information will asses the validity of the nonce late in the authentication process.

Mechanism Factory Implementation

After the mechanism implementation the next class required is a factory to return instances of the mechanism, factories implement the HttpAuthenticationFactory interface, in this example the factory only returns a single mechanism however a single factory could support multiple mechanisms.

Within the test project this is implemented in CustomMechanismFactory.java

The most important step within the factory is to double check the name of the mechanism requested, it is important for the factory to return null if it can not create the required mechanism.  The mechanism factory can also take into account properties in the Map passed in to decide if it can create the requested mechanism.

Advertising Availability

There are two different approaches that can be used to advertise the availability of a mechanism factory, the first is to implement a java.security.Provider with the HttpAuthenticationFactory registered as an available service once for each mechanism it supports.

This example however is very simple so instead of implementing a provider we use a java.util.ServiceLoader to discover the factory instead, to achieve this we add the descriptor org.wildfly.security.http.HttpServerAuthenticationMechanismFactory under META-INF/services the only contents required in this file are the fully qualified class name of the factory.

Mechanism Installation

At this stage the mechanism project can be built and installed in the application server as a module ready to be used, the project is a simple maven project so can be built with: -

    mvn clean install

For the next stage the application server does not need to be running, the following command can be executed within the jboss-cli to add the module to the application server: -

module add --name=org.wildfly.security.examples.custom-http \
 --resources=/path/elytron-examples/simple-http-mechanism/target/simple-http-mechanism-1.0.0.Alpha1-SNAPSHOT.jar \
 --dependencies=org.wildfly.security.elytron,javax.api

The required dependencies are very simple, the installed module just requires a dependency on the public Elytron API and the javax API for access to some of the common callbacks and related exceptions.

Testing - BASIC authentication

When testing changes within the application server it is often better to start with small changes and verify those are working before moving onto the next set of changes, often users that make a large number of changes at once find it difficult to track down at which stage an error was introduced.

The next step within this blog is to deploy the simple-webapp also in the elytron-examples repository and verify it works with HTTP BASIC authentication, once verified we will switch the configuration to use the new authentication mechanism.

Before starting the application server a new test account can be added using the add-user utility.

./add-user.sh -a -u testuser -p password -g Users

Now the application server can be started and the CLI connected to the application server.

When the sample application is deployed it will by default use the 'other' security domain so a mapping needs to be added to map this to an Elytron HTTP authentication factory: -

./subsystem=undertow/application-security-domain=other:add(
http-authentication-factory=application-http-authentication)

The simple-webapp can now be deployed directly using maven.

mvn wildfly:deploy

This application could be tested using a web browser as it is using a standard mechanism the browser would understand, however once we switch to using a custom mechanism the browser will not understand so it is better to test the call using a client that will allow us to manipulate the headers ourselves.

curl -v http://localhost:8080/simple-webapp/secured -u testuser:password

If everything is working output similar to the following should be seen (Some headers have been removed to make the output easier to read)

< HTTP/1.1 200 OK
...<
<html>
 <head><title>Secured Servlet</title></head>
 <body>
   <h1>Secured Servlet</h1>
   <p>
Current Principal 'testuser'    </p>
 </body>
</html>

At this stage authentication is being successfully applied to the web application backed by WildFly Elytron using the Elytron implementation of the HTTP BASIC authentication mechanism, the next step is to switch to using the custom mechanism.

Testing - Custom Mechanism

The first resource to add in the CLI is to discover the factory implementation.

./subsystem=elytron/service-loader-http-server-mechanism-factory=
custom-factory:add(module=org.wildfly.security.examples.custom-http)

After this is added a CLI command can be executed to immediately check which mechanisms the factory can create.

./subsystem=elytron/service-loader-http-server-mechanism-factory=
custom-factory:read-resource(include-runtime=true)
{
   "outcome" => "success",
   "result" => {
       "available-mechanisms" => ["CUSTOM_MECHANISM"],
       "module" => "org.wildfly.security.examples.custom-http"
   }
}
The next resource to add is a http-authentication-factory to tie the mechanism factory to a security-domain that will be used for the actual authentication.

./subsystem=elytron/http-authentication-factory=custom-mechanism:
add(http-server-mechanism-factory=custom-factory,
security-domain=ApplicationDomain,
mechanism-configurations=[{mechanism-name=CUSTOM_MECHANISM}])

The application-security-domain resource we added previously can now be updated to use this new http-authentication-factory.

./subsystem=undertow/application-security-domain=other:
write-attribute(name=http-authentication-factory, value=custom-mechanism)
./subsystem=undertow/application-security-domain=other:
write-attribute(name=override-deployment-config, value=true)

The second of those commands is important, the application is defined to use the BASIC authentication mechanism only, by overriding the deployment config the mechanisms from the http-authentication-factory will be used instead.

The server can now be reloaded.

:reload

The same curl command can be executed again but this time it is expected it will fail with output similar to the following.

curl -v http://localhost:8080/simple-webapp/secured -u testuser:password
< HTTP/1.1 401 Unauthorized
...
< X-MESSAGE: Please resubit the request with a username specified using the X-USERNAME and a password specified using the X-PASSWORD header.
...

Here the authentication mechanism has rejected the call and subsequently added a header describing how to authentication.

The curl command can now be modified to: -

curl -v http://localhost:8080/simple-webapp/secured -H "X-USERNAME:testuser" -H "X-PASSWORD:password"
< HTTP/1.1 200 OK
...
<
<html>
 <head><title>Secured Servlet</title></head>
 <body>
   <h1>Secured Servlet</h1>
   <p>
Current Principal 'testuser'    </p>
 </body>
</html>

The resulting output now shows authentication was successful again and the authenticated principal is 'testuser'.







Tuesday, 26 September 2017

WildFly Elytron - Add Kerberos Authentication To Existing Web Application

When using WildFly Elytron to secure a web application it is possible to customise the authentication policy without modifying the web application, this is something that could be very useful as an application transitions though different environments such as development, testing, staging, and production - in each of these environments a custom authentication policy can be used without requiring a custom build of the web application.

This blog post is going to take a simple web application secured by HTTP BASIC authentication and convert it to use WildFly Elytron using SPNEGO authentication with fallback to BASIC without repackaging the application.

Example Application

This blog post have been written using the following example web application: -

https://github.com/wildfly-security-incubator/elytron-examples/tree/master/simple-webapp

This application contains a single welcome page with a link to a secured servlet that requires the caller have the role 'Users', the resulting deployment contains a web.xml that specifies authentication should use HTTP BASIC authentication.

The example application contains no JBoss or WidFly specific descriptors or annotations and instead depends on the default configuration within the Undertow subsystem using the 'other' security domain which pulls the user information from the application-users.properties and application-roles.properties.

Test User

Within my test Kerberos environment the main user I test with is 'testuser', before proceeding to configure the server I will ensure there is an entry for this user in the properties files with the following command: -

  ./add-user.sh -a -u testuser -p password -g Users

Configuring the Server

These instructions are written using a recent build of WildFly 11 however it should be possible to use them against either the CR1 release or once available the Final release.

Although they are not in use by default the default server configuration of WildFly 11 contains many Elytron components already configured to work with the legacy security properties, the steps here will make use of those as much as is possible and just define new components to achieve SPNEGO authentication as well.

The next set of commands are all executed within the JBoss CLI.

When working with Kerberos in a test environment I tend to find it easier to define the realm and KDC information as system properties: -


./system-property=sun.security.krb5.debug:add(value=true)
./system-property=java.security.krb5.realm:add(value=ELYTRON.ORG)
./system-property=java.security.krb5.kdc:add(value=kdc.elytron.org)


The next command is to define the server's Kerberos identity.

./subsystem=elytron/kerberos-security-factory=test-server:add( \
    path=/home/darranl/src/kerberos/test-server.keytab, \
    principal=HTTP/test-server.elytron.org@ELYTRON.ORG, \
    debug=true) 


The existing 'ApplicationDomain' and the realm it already references are going to be used.

SPNEGO authentication by default results in an identities name containing the Kerberos realm as a suffix so will now define a principal transformer to strip the realm.

 ./subsystem=elytron/regex-principal-transformer= \
    realm-stripper:add( \
    pattern="@ELYTRON.ORG", replacement="")

The final step within the Elytron subsystem is to define a HTTP authentication factory that supports both SPNEGO and BASIC authentication.

./subsystem=elytron/http-authentication-factory=spnego-http-authentication:add(security-domain=ApplicationDomain, \
    http-server-mechanism-factory=global, \
    mechanism-configurations=[ \
    {mechanism-name=SPNEGO, \
    credential-security-factory=test-server, \
    pre-realm-principal-transformer=realm-stripper}, \
    {mechanism-name=BASIC} \
    ])

The ordering of the mechanism names was important here as we want SPNEGO to be the preferred authentication mechanism with fallback to BASIC if the web browser can not respond. 

For this specific example we will be overriding the mechanisms defined within the web.xml so it is important to only list the mechanisms we want to actually use.

The final configuration step is within the Undertow susbystem to configure any deployments that use the 'other' security domain to instead use the HTTP authentication factory defined here and override any defined authentication mechanisms.

/subsystem=undertow/application-security-domain=other:add( \
    http-authentication-factory=spnego-http-authentication, \
    override-deployment-config=true)

Deploy Example Application

If the web application has already been deployed the server should be reloaded / restarted for the new security policy to apply to it, alternatively it can be deployed using the WildFly Maven plug-in.

mvn wildfly:deploy

Access Web Application

For my Kerberos test environment the server is test-server.elytron.org so I can access the web application through the following URL: -

http://test-server.elytron.org:8080/simple-webapp

At this point if I access the web application after obtaining a Kerberos ticket for 'testuser' and subsequently click on the 'Access Secured Servlet' link SPNEGO authentication will silently take place for me as I have already configured my web browser to enable SPNEGO authentication.

If I don't have a local Kerberos ticket or if my web browser does not have SPNEGO authentication enabled I will receive the BASIC authentication prompt and will be able to use the username and password I defined at the start of this blog post.

One point to keep in mind, SPNEGO authentication is cached by default against the HTTP session - if switching from having a ticket to not having a ticket restart either the browser or the server.











Friday, 8 September 2017

Using WildFly Elytron with Undertow Standalone

The WildFly Elytron project has been developed to provide the security requirements of the WildFly application server, however the development of WildFly Elytron has produced a security framework which does not depend on the application and can be used outside of the application server.

This means that it is possible to use WildFly Elytron in situations outside of the application server, this blog post demonstrates how WildFly Elytron can be used to secure an embedded Undertow server.

If you visit the main page for Undertow towards the bottom of the page is a code example showing how to start a simple HelloWorld server using Async IO.

http://undertow.io/

This blog post goes one step further and illustrates how WildFly Elytron can be added to the example to secure access to the HttpHandler using HTTP Basic authentication.

The components demonstrated in this example are the same components we use within the application server, the difference being that they are programatically configured and wired together rather than using the subsystem for configuration.

The code for the example can be found in the project called 'undertow-standalone' in the Git repository https://github.com/wildfly-security-incubator/elytron-examples.

Dependencies

The example project has the following key dependencies although these will pull in some additional dependencies.

io.undertow:undertow-core
This is the main dependency on Undertow, this example is only demonstrating with the core Undertow APIs so the servlet dependency is not required.

org.wildfly.security:wildfly-elytron
This is the dependency on the WildFly Elytron security framework.

org.wildfly.security.elytron-web:undertow-server
The Undertow project does not have a dependency on WildFly Elytron and the Elytron project does not have a dependency on Undertow, the Elytron Web project acts as an intermediate project to join the two together.

In future releases of Elytron Web we may also be able to look into integration with other servers following a similar pattern to how we have integrated with Undertow.

Example

The initial example is very similar to the example on undertow.io, the main difference being the call to a method 'wrap' to wrap the HttpHandler.

    public static void main(String[] args) throws Exception {
        final SecurityDomain securityDomain = createSecurityDomain();

        Undertow server = Undertow.builder()
                .addHttpListener(8080, "localhost")
                .setHandler(wrap(new HttpHandler() {

          public void handleRequest(HttpServerExchange exchange) 
               throws Exception {
                                            
exchange.getResponseHeaders().
  put(Headers.CONTENT_TYPE, "text/plain");                       
exchange.getResponseSender().
  send("Hello " + securityDomain.getCurrentSecurityIdentity().getPrincipal().getName());
                    }
                 }, securityDomain)).build();
        server.start();
    }

The first thing to happen during wrapping is an Elytron security domain is assembled, this domain contains a single user 'elytron' with a password of 'Coleoptera'.

    private static SecurityDomain createSecurityDomain() throws Exception {
        PasswordFactory passwordFactory = PasswordFactory.getInstance(ALGORITHM_CLEAR, elytronProvider);

        Map<String, SimpleRealmEntry> passwordMap = new HashMap<>();
        passwordMap.put("elytron", new SimpleRealmEntry(Collections.singletonList(new PasswordCredential(passwordFactory.generatePassword(new ClearPasswordSpec("Coleoptera".toCharArray()))))));

        SimpleMapBackedSecurityRealm simpleRealm = new SimpleMapBackedSecurityRealm(() -> new Provider[] { elytronProvider });
        simpleRealm.setPasswordMap(passwordMap);

        SecurityDomain.Builder builder = SecurityDomain.builder()
                .setDefaultRealmName("TestRealm");

        builder.addRealm("TestRealm", simpleRealm).build();
        builder.setPermissionMapper((principal, roles) -> PermissionVerifier.from(new LoginPermission()));

        return builder.build();
    }

In this example a simple in memory security realm backed by a Map is used, however any of the other Elytron security realms could be used or even a custom security realm implementation if desired.

After assembling the security domain the next step is creating a HttpAuthenticationFactory, the HttpAuthenticationFactory is the overall authentication policy that makes authentication mechanisms backed by the security domain available.

    private static HttpAuthenticationFactory createHttpAuthenticationFactory(final SecurityDomain securityDomain) {
        HttpServerAuthenticationMechanismFactory providerFactory = new SecurityProviderServerMechanismFactory(() -> new Provider[] {elytronProvider});
        HttpServerAuthenticationMechanismFactory httpServerMechanismFactory = new FilterServerMechanismFactory(providerFactory, true, "BASIC");

        return HttpAuthenticationFactory.builder()
                .setSecurityDomain(securityDomain)
                .setMechanismConfigurationSelector(MechanismConfigurationSelector.constantSelector(
                        MechanismConfiguration.builder()
                                .addMechanismRealm(MechanismRealmConfiguration.builder().setRealmName("Elytron Realm").build())
                                .build()))
                .setFactory(httpServerMechanismFactory)
                .build();
    }

At this stage we can see how the original HttpHandler is wrapped using the resulting HttpAuthenticationFactory and some additional Elytron and Undertow APIs to enable security.

    private static HttpHandler wrap(final HttpHandler toWrap, final SecurityDomain securityDomain) {
        HttpAuthenticationFactory httpAuthenticationFactory = createHttpAuthenticationFactory(securityDomain);

        HttpHandler rootHandler = new ElytronRunAsHandler(toWrap);

        rootHandler = new AuthenticationCallHandler(rootHandler);
        rootHandler = new AuthenticationConstraintHandler(rootHandler);

        return ElytronContextAssociationHandler.builder()
                .setNext(rootHandler)
                .setMechanismSupplier(() -> {
                    try {
                        return Collections.singletonList(httpAuthenticationFactory.createMechanism("BASIC"));
                    } catch (HttpAuthenticationException e) {
                        throw new RuntimeException(e);
                    }
        }).build();
    }

Build and Run

The example project is a standard Maven project so provided Maven is installed along with Java 8 the project can be built using 'mvn install'.

Once built the server can be started using the exec plug-in 'mvn exec:exec'.  This should result in the server starting and listening on port 8080 for incoming requests.

Note: As an example project there is not a lot of output from the project to the console, feel free to add more output if desired to see each stage as it occurs.

After starting the project you should be able to access the server using curl.

undertow-standalone]$ curl -v http://127.0.0.1:8080/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.53.1
> Accept: */*
< HTTP/1.1 401 Unauthorized
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Elytron Realm"
< Content-Length: 0
< Date: Fri, 08 Sep 2017 15:16:30 GMT
* Connection #0 to host 127.0.0.1 left intact

At this stage curl was not supplied with any user details so we can see the HTTP Basic authentication challenge and the request ends.

If we now provide a username and enter the password when prompted we see a full HTTP exchange.

undertow-standalone]$ curl -v http://127.0.0.1:8080/ --user elytron
Enter host password for user 'elytron':
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
* Server auth using Basic with user 'elytron'
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> Authorization: Basic ZWx5dHJvbjpDb2xlb3B0ZXJh
> User-Agent: curl/7.53.1
> Accept: */*
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: text/plain
< Content-Length: 13
< Date: Fri, 08 Sep 2017 15:17:45 GMT
* Connection #0 to host 127.0.0.1 left intact
Hello elytron

The 'Hello elytron' returned at the end is the message from the HttpHandler where 'elytron' is the name of the authenticated principal calling the HttpHandler.




Friday, 14 July 2017

WildFly Elytron - Principal Transformers, Realm Mappings, and Principal Decoders

Within the WildFly Elytron configuration it is possible to specify multiple principal transformers, realm mappers, and a principal decoder - this blog post is to describe how they all fit together during the authentication process.

During the authentication process the principal transformers and principal decoders form a very similar function in that they are both used to map principals from one form to another, principal transformers can also be used to validate a principal as an example double check the formatting so authentication can be terminated early if an invalid principal is detected.

At the appropriate state in the process that is being described in this blog post the realm mappers will then operate on the mapped principal to identify which security realm should be used to load the identity.

Here are a couple of diagrams to illustrate how these concepts fit together, the remainder of this blog post will describe the steps in more detail.

The first diagram illustrates the general states the authentication process undergoes to map the Principal used for authentication.

Identity Assignment States 
The next diagram illustrates how the various transformers, decoders, and mappers can be configured for Elytron authentication.

Configuration Relationships

Resolve Mechanism Configuration

When the authentication processes commences for a single authentication mechanism the first step is to resolve the MechanismConfiguration that should be used, this is resolved by taking into account the name of the selected mechanism, the host name, and the protocol.

During this stage the mechanism realm configuration will also be resolved, the authentication mechanism will either request this by name or if the mechanism does not request this then the first one in the list is used.

Note: The mechanism realm is specifically in relation to the realm name negotiated by the authentication mechanism if applicable and is independent of the security realm representing the identity store.

Pre Realm Mapping

The purpose of this state is to take the Principal from the form that was provided by the authentication mechanism and map it to the form that can be used to identify which security realm to use to load the identity.

At the very end of the authentication process the identity is represented by a SecurityIdentity which contains a single Principal, the Principal will be the one created by this mapping stage.

The principal transformers and principal decoder will be called in the following order: -

1. Mechanism Realm - pre-realm principal-transformer
2. Mechanism Configuration - pre-realm principal transformer
3. Security Domain - principal-decoder
4. Security Domain - pre-realm-principal-transformer

If the end result is a null principal and error will be reported and authentication will terminate.

Realm Mapping

The next stage is to take the mapping principal and map it to a realm name to identify the name of the Security Realm to use to load the identity.

Note:  At this stage the realm name is the name of the SecurityRealm as referenced by the SecurityDomain and is not the mechanism realm name.

The configuration will be inspected for the first realm mapper that can be found in the following locations: -

A. Mechanism Realm - realm-mapper
B. Mechanism Configuration - realm-mapper
C. Security Domain - realm-mapper

If a RealmMapper is identified but that mapper returns null when mapping the Principal then the default-realm specified on the Security Domain will be used instead.

If no RealmMapper is available then the default-realm on the SecurityDomain will be used.

Post Realm Mapping

After the realm has been identified a further round of principal transformation happens, this time the following transformers are called: -

5. Mechanism Realm - post-realm principal-transformer
6. Mechanism Configuration - post-realm principal-transformer
7. Security Domain - post-realm principal-transformer

As before if the result is a null principal an error will be reported and authentication will be terminated.

Final Principal Transformation

After the post realm mapping stage one final round of principal transforming takes place, this time the following transformers are called in order.

8. Mechanism Realm - final principal-transformer
9. Mechanism Configuration - final principal-transformer
10. Realm Mapping - principal-transformer

Once again a null principal will result in an error being reported and authentication being terminated.

Having to transformations after the realm has been identified allows for mechanism specific transformations to be applied both before and after domain specific transformations, if this is not required then either the post-realm principal transformers or the final principal-transformers can be used to obtain the same result.

The End

It is only now at the very end of principal transformation that the security realm previously identified will be call to obtain the RealmIdentity that is now used for authentication to continue.

The key points to keep in mind are: -

  • The Principal created by the pre-realm-principal-transformers is: -
    • The Principal used to map the SecurityRealm
    • The Principal that will be associated with the resulting SecurityIdentity.
  • The Principal created after the final principal transformers is: -
    • The Principal that will be passed to the SecurityRealm to obtain the RealmIdentity.