Saturday 19 January 2019

Using WildFly Elytron with the Netty HttpServerCodec

The WildFly Elytron project was developed to meet the needs if the WildFly application server, however the APIs and SPIs within this project also allow us to use the project in other environments.

A couple of previous blogs from Farah Juma and myself have highlighted a couple of these environments already: -
 Using WildFly Elytron with Undertow Standalone
 Securing an embedded Jetty server using Elytron

Also I have previously published a blog describing how to implement a custom HTTP authentication mechanism using the WildFly Elytron SPIs.
WildFly Elytron - Implementing a Custom HTTP Authentication Mechanism

Being able to take a single security project and use it in multiple environments has numerous benefits, some of which are: -

  • Only needing to learn one framework instead of one framework per server type.
  • Portability of custom implementation such as authentication mechanisms or security realms which can be used in all environments.
  • The ability to combine multiple servers into a single process whilst using common security SPIs.
This blog post is to introduce the integration of WildFly Elytron with the Netty HttpServerCodec for HTTP authentication.

It is worth noting this integration is specifically making use of Netty's HttpServerCodec for integration, if an alternative HTTP server was developed on Netty then an alternative integration with WildFly Elytron would also be required.

The project containing the Netty integration code can be found on GitHub at elytron-web-netty

As an integration project this project only exposes once class as public API 'org.wildfly.elytron.web.netty.server.ElytronHandlers'  - the purpose this class is to take configured WildFly Elytron components and use them to insert a set of handlers into a Netty ChannelPipeline, once inserted they will perform authentication kaing use of Wildfly Elytron.

The example project can be found on GitHub at netty-standalone.

After checking out the example project it can be built using maven: -

    mvn clean instal

And then once build can also be executed using maven: -

    mvn exec:exec

Once running you can navigate to http://localhost:7776/ and access the application using the username 'alice' with the password 'alice123+', if successful you should see the following response: -

Current identity 'alice'

Now that the example project is running we can look into the details as to how WildFly Elytron was activated.

Activation

Within Netty a ChannelInitializer is required to add the handlers to the ChannelPipeline, within the example project this is within the class 'org.wildfly.security.examples.TestInitialiser' and is implemented as: -


protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new HttpServerCodec());
    pipeline.addLast(new HttpServerExpectContinueHandler());
    securityHandler.apply(pipeline);
    pipeline.addLast(new TestContentHandler());
}

The 'securityHandler' within the example block of code is actually an instance of the ElytronHandlers class, it is worth noting that it can be cached and re-used potentially performing many initialisations concurrently.

As the example projects starts up the ElytronHandlers is initialised as follows: -

ElytronHandlers securityHandlers = ElytronHandlers.newInstance()
        .setSecurityDomain(securityDomain)
        .setFactory(createHttpAuthenticationFactory())
        .setMechanismConfigurationSelector(MechanismConfigurationSelector.constantSelector(
                MechanismConfiguration.builder()
                        .addMechanismRealm(MechanismRealmConfiguration.builder().setRealmName("Elytron Realm").build())
                        .build()));

This initialisation takes a pre-configured SecurityDomain and HttpAuthenticationFactory and adds a MechanismConfigurationSelector, these have been covered on prior blogs so I am not going to cover the details of these again here although they can all be seen within the org.wildfly.security.examples.HelloWorld class to see how these are initialised.

One further method not used in this example is: -

public ElytronHandlers setAuthenticationRequired(final Predicate<HttpRequest> authenticationRequired)

This can be used to add a Predicate to decide on a request by request basis if authentication is required after inspecting the HttpRequest.

Outcome

After the initialisation described above the channel end up with the following handlers defined on it's pipeline: -

For a request after being handled by the HttpServerCodec handler it will pass so the ElytronInboundHandler, this is where the authentication by WildFly Elytron takes place and a SecurityIdentity is established.

If authentication fails the request can be turned around at this stage and sent back to the client.

Our next inbound handler is the ElytronRunAsHandler, this handler is responsible for taking any established SecurityIdentity and ensuring it is associated with the current thread.

In this set up we have one outbound handler which is the ElytronOutboundHandler, this handler is called for all messages being sent back to the client is responsible for setting any security related HTTP headers that need to be set on the respone message.


Contribution

The Netty integration project is currently tagged as a Beta as a number of areas still need to be developed, if anyone is interested in contributing their contributions will be welcome.

Completing the implementation of the ElytronHttpExchange class: -
  • Support for all Scope types.
  • Parsing of request parameters.
  • Cookie support.
Request InputStream handling.

Improved response OutputStream handling.

Adding support for authorization, either role based checks or Java permission checks.

Further enhancement of the test cases including adding more mechanisms to be tested.

Outside of the Netty integration there are other projects out there still possible options for integration, some of these are: -
  • Tomcat
  • Pure servlet integration.
  • EE Security integration.

For all integrations some form of common testsuite to verify the full permutation of authentication mechanisms available within WildFly Elytron.