Showing posts with label Kerberos. Show all posts
Showing posts with label Kerberos. Show all posts

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.











Thursday, 27 November 2014

WildFly 9 - Kerberos Authentication with Remoting - Part 1

This blog post is in relation to a new feature added to WildFly 9 under WFCORE-106, however it is not currently included in a release so for now you will need to build WildFly yourself or use one of the nightly builds.

If you are not familiar with building WildFly have a look at HackingOnWildFly.

If you want to access a nightly build they are available from WildFly-latest-master.

Introduction

This blog post is the first part of a two part article on how to enable Keberos authentication for Remoting within WildFly 9, in Part 1 I discuss how to enable Keberos authentication for use with the CLI for managing the application server which makes use of JBoss Remoting to communicate with the server.

In Part 2 I will be looking at how to take an existing secured EJB deployment from within the quickstarts and enable Kerberos based authentication.

In a previous blog post WildFly 9 - Kerberos Authentication for Domain Management Over HTTP I have already described a lot of the steps required to enable Kerberos authentication for HTTP, for the steps that are also applicable for authentication with Remoting I will briefly mention them but will not duplicate the descriptions here.

Service Principals

One important aspect of Kerberos based authentication is that the service requiring authentication needs to be represented by it's own service principal, the naming of this principal is in the form '{service_name}/{hostname}' - where HTTP servers are secured using Kerberos the service_name is 'HTTP' - however for Remoting the service_name is expected to be 'remote'.

My Environment

My test environment that I am using for the basis of this blog post is the same as I described previously, the main difference here is that now I have a service principal called remote/web.dal.wildfly.org@DAL.WILDFLY.ORG which is in a keytab called remote.keytab.

As before any example commands will need to be updated to reflect your own environment.

Configuration Updates

As with the previous blog post system properties can be set as an easy way to define the realm name and the address of the KDC, after that similar updates also need to be made to the ManagementRealm: -


  1. Add a server identity for Kerberos.
  2. Add the keytab for the remote service principal.
  3. Add kerberos to the authentication resource of the security domain.
  4. Reload the server.
Steps 1, 3, and 4 are identical to those performed previously but in this example I would use the following command to add my remote keytab.


[standalone@web.dal.wildfly.org:9990 /] ./core-service=management/security-realm=ManagementRealm/server-identity=kerberos/keytab=remote\/web.dal.wildfly.org@DAL.WILDFLY.ORG:add(path=remote.keytab, relative-to=jboss.server.config.dir, debug=true)
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}


Running the CLI

As before I have used the kinit command to obtain my own ticket granting ticket from the KDC: -


[darranl@localhost bin]$ kinit darranl@DAL.WILDFLY.ORG
Password for darranl@DAL.WILDFLY.ORG: 
[darranl@localhost bin]$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: darranl@DAL.WILDFLY.ORG

Valid starting     Expires            Service principal
27/11/14 17:44:50  28/11/14 17:44:47  krbtgt/DAL.WILDFLY.ORG@DAL.WILDFLY.ORG
renew until 04/12/14 17:44:47


The next step is to start the CLI, as I start the CLI I set three system properties: -
  1. -Djava.security.krb5.realm=DAL.WILDFLY.ORG
  2. -Djava.security.krb5.kdc=kdc.dal.wildfly.org
  3. -Djavax.security.auth.useSubjectCredsOnly=false
The first two could be optional depending on local environmental settings but the third one is important as this allows the GSSAPI implementation to make use of the identity managed at the OS level.

In the case of my testing I am running the CLI on the same machine that I am running the application server so it is also important to ensure I disable local authentication otherwise Kerberos authentication will not get a chance to be used so I add the argument --no-local-auth.

This means the resulting command is: -

[darranl@localhost bin]$ ./jboss-cli.sh -c --controller=web.dal.wildfly.org \
    --no-local-auth -Djava.security.krb5.realm=DAL.WILDFLY.ORG \
    -Djava.security.krb5.kdc=kdc.dal.wildfly.org \
    -Djavax.security.auth.useSubjectCredsOnly=false [standalone@web.dal.wildfly.org:9990 /]

I can execute the :whoami command to double check the user I am authenticated as.


[standalone@web.dal.wildfly.org:9990 /] :whoami
{
    "outcome" => "success",
    "result" => {"identity" => {
        "username" => "darranl",
        "realm" => "ManagementRealm"
    }}
}

Additional Config

The example above is making use of a lot of default behaviour within the CLI, if you would prefer to control the Kerberos identity further it is possible to override the JAAS security domain used internally by adding the following system property to point to your own JAAS configuration -Djava.security.auth.login.config={config}, this configuration should contain a security domain called 'com.sun.security.jgss.initiate' that contains a Krb5LoginModule definition for your own environment. One example of a reason you may want to do this is if your clients identity can also be obtained from a keytab.






Friday, 17 October 2014

WildFly 9 - Kerberos Authentication for Domain Management Over HTTP

This blog post is in relation to a new feature added to WildFly 9 under WFCORE-105, however it is not currently included in a release so for now you will need to build WildFly yourself or use one of the nightly builds.

If you are not familiar with building WildFly have a look at HackingOnWildFly.

If you want to access a nightly build they are available from WildFly-latest-master.

Pre-Requisites

This blog post is specifically about the steps and configuration required to enable Kerberos based authentication for domain management over HTTP, this blog post does not describe how to define service principals and obtain the keytab or how to configure the web browser - there is already plenty of information out there covering those topics.

My Environment

The examples described in this blog post are all in the context of my test environment.

My KDC is service requests for the realm DAL.WILDFLY.ORG and the address of my KDC is kdc.dal.wildfly.org.

The HTTP management interface of my server installation is already listening on host web.dal.wildfly.org and I have a keytab called web.keytab which contains the service principal HTTP/web.dal.wildfly.org@DAL.WILDFLY.ORG.

Where you see any of these values in the following example obviously you will need to substitute in your own specific values for your own environment.

General Kerberos Settings

It is possible to rely on your local environments Kerberos configuration for things such as KDC identification and the resolution of the KDC's address, however for my own test environment where I am using a single KDC I prefer to just set a couple of system properties using the following CLI commands.

[standalone@web.dal.wildfly.org:9990 /] 
./system-property=java.security.krb5.realm:add(value=DAL.WILDFLY.ORG)
{"outcome" => "success"}
[standalone@web.dal.wildfly.org:9990 /] 
./system-property=java.security.krb5.kdc:add(value=kdc.dal.wildfly.org)
{"outcome" => "success"}

Enabling Kerberos Authentication

The steps to enable Kerberos authentication are split into two parts, first of all we need to define the servers own identity which is loaded from it's keytab and secondly once that is defined we need to enable the authentication.

Servers Identity

The first step is to now configure how the server will load it's own identity from a keytab, to do this I copy my web.keytab to the standalone/configuration folder of my installation and execute the following CLI commands: -

[standalone@web.dal.wildfly.org:9990 /] ./core-service=management/security-realm=ManagementRealm/server-identity=kerberos:add
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}
[standalone@web.dal.wildfly.org:9990 /] ./core-service=management/security-realm=ManagementRealm/server-identity=kerberos/keytab=HTTP\/web.dal.wildfly.org@DAL.WILDFLY.ORG:add(path=web.keytab, relative-to=jboss.server.config.dir, debug=true)
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

The first command adds a container resource to the security realm to say that we will be adding a Kerberos identity, the subsequent command adds a reference to the keytab.  take note of where the principal name is specified within the address for the keytab and see how it needed to be escaped, also for this keytab I have enabled some additional debug output for when we communicate with the KDC.

Also note that these commands have put the server in the reload-required state.

It is possible that a single server could be accessible using multiple host names, for this reason it is possible to add multiple keytabs to the security realm,  the keytab definitions also have a for-hosts attribute that can be used to specify which keytab should be used for which host, failing that there will be an attempt to match the host name to the principal name.

In general we could proceed to the next section but it worth now executing :reload so that we can test our new configuration before enabling the actual authentication.

[standalone@web.dal.wildfly.org:9990 /] :reload                                  
                                                                                
{            
    "outcome" => "success",
    "result" => undefined
}

The keytab resource then has a test operation that can be invoked to verify the identity can be obtained.

[standalone@web.dal.wildfly.org:9990 /] ./core-service=management/security-realm=ManagementRealm/server-identity=kerberos/keytab=HTTP\/web.dal.wildfly.org@DAL.WILDFLY.ORG:test
{
    "outcome" => "success",
    "result" => {"subject" => "Subject:
Principal: HTTP/web.dal.wildfly.org@DAL.WILDFLY.ORG
Private Credential: /home/darranl/src/wildfly9/wildfly/dist/target/wildfly-9.0.0.Alpha2-SNAPSHOT/standalone/configuration/web.keytab
Private Credential: Kerberos Principal HTTP/web.dal.wildfly.org@DAL.WILDFLY.ORGKey Version 1key EncryptionKey: keyType=17 keyBytes (hex dump)=
0000: 8C D1 9C 24 86 84 D4 25   A3 12 FF DE A9 0D 9B 26  ...$...%.......&

At this point it is absolutely critical that you verify in the above output that one of the Private Credentials is actually a 'Kerberos Principal' and not just the reference to the keytab location.

Switch on Authentication

To switch on authentication a simple addition to the realm is required: -

[standalone@web.dal.wildfly.org:9990 /] ./core-service=management/security-realm=ManagementRealm/authentication=kerberos:add
{
    "outcome" => "success",
    "response-headers" => {
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

At this point :reload needs to be executed again and now I can try and connect using my web browser.

Load the Admin Console

Firstly I make sure I have obtained my own ticket granting ticket from the KDC: -

[darranl@localhost tmp]$ kinit darranl@DAL.WILDFLY.ORG
Password for darranl@DAL.WILDFLY.ORG: 
[darranl@localhost tmp]$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: darranl@DAL.WILDFLY.ORG

Valid starting     Expires            Service principal
17/10/14 13:08:18  18/10/14 13:08:15  krbtgt/DAL.WILDFLY.ORG@DAL.WILDFLY.ORG
renew until 24/10/14 13:08:15

As the security realm is based on the default configuration username/password based authentication is still enabled, for this reason you will need to ensure at least one user is defined by running the add-user script.

Now I load my web browser and attempt to connect using the admin console.


With no additional password prompt I am now authenticated as the user I previously obtained the ticket granting ticket for.

It is worth pointing out that my identity here includes the name of the realm, if desired when I added the authentication=kerberos to the realm I could have added the attribute remove-realm=true to strip the realm name from the username.

Finally if my web browser had not been enabled for Kerberos authentication or if I had not already obtained the ticket granting ticket I would have been prompted for a username and password as normal.

XML Configuration

Finally all of this configuration could have been defined manually in the application server configuration, the following is the resulting realm definition after the above CLI commands have been executed.

<security-realm name="ManagementRealm">
    <server-identities>
        <kerberos>
            <keytab principal="HTTP/web.dal.wildfly.org@DAL.WILDFLY.ORG" path="web.keytab" relative-to="jboss.server.config.dir" debug="true"/>
        </kerberos>
    </server-identities>
    <authentication>
        <local default-user="$local" skip-group-loading="true"/>
        <kerberos/>
        <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
    </authentication>
    <authorization map-groups-to-roles="false">
        <properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
    </authorization>
</security-realm>

Next Steps

The next steps now are to enable Kerberos authentication for our native interface so that clients such as the CLI and JMX Console can also take advantage of Kerberos based authentication.


Thursday, 11 September 2014

Kerberos EncryptedData NULL key / keytype mismatch.

For anyone who has ever been in the position of debugging Kerberos interoperability issues where Java is concerned I am fairly sure you would have come across the situation where you are attempting to search for some meaning in a cryptic error message you have received just to stumble on message posts from other users with the same error but no explanation as to the cause or a solution.

The purpose of this blog post is to describe one such error that I am currently investigating, I will say upfront however I have got to the bottom of why the error is being reported and have a possible workaround for some situations but the quest for a solution is ongoing.

If and when a complete solution is available I will update this blog post with the additional information.

In this case I have a JBoss EAP server handling incoming SPNEGO messages from different clients.

The Errors

Before describing the findings here are a couple of the errors I have been seeing: -

Caused by: KrbException: EncryptedData is encrypted using keytype NULL but decryption key is of type AES256 CTS mode with HMAC SHA1-96
at sun.security.krb5.EncryptedData.decrypt(EncryptedData.java:169) [rt.jar:1.7.0_17]
at sun.security.krb5.KrbCred.<init>(KrbCred.java:131) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.InitialToken$OverloadedChecksum.<init>(InitialToken.java:291) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.InitSecContextToken.<init>(InitSecContextToken.java:130) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:771) [rt.jar:1.7.0_17]

And

Caused by: KrbException: EncryptedData is encrypted using keytype DES3 CBC mode with SHA1-KD but decryption key is of type NULL
at sun.security.krb5.EncryptedData.decrypt(EncryptedData.java:169) [rt.jar:1.7.0_17]
at sun.security.krb5.KrbCred.<init>(KrbCred.java:131) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.InitialToken$OverloadedChecksum.<init>(InitialToken.java:282) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.InitSecContextToken.<init>(InitSecContextToken.java:130) [rt.jar:1.7.0_17]
at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:771) [rt.jar:1.7.0_17]
... 35 more

For the purpose of identifying if your error message is related to this it is absolutely essential that you also compare the stack trace to ensure it is similar, i.e. the line numbers may be different but the classes in the stack should be almost identical.

The first of these error messages was reported when connecting to a server using Firefox from a Mac OS client, the second was reported using Firefox on a Fedora 20 client.

The Trigger

Within the SPNEGO authentication a Kerberos AP_REQ message is sent from the client to the server, within this message it is possible for the client to embed their own credential so that it can be delegated to the server.  For both of the error message above the KrbCred creation, EncryptedData decryption and the resulting validation and error are only applicable if the incoming message does contain a delegated credential.

When it comes to credential delegation and Kerberos this is something that the client decides it is going to do, the server has not actually requested that the client does delegate their credentials.

The Workaround

As this error is only encountered where credentials are delegated to the server the most simple workaround is to disable credential delegation in your client.  That way the AP_REQ message will not contain delegated credentials and the portion of the stack above reporting the error message will not be hit.

Unfortunately this workaround is only suitable if your server side service is not making use of delegated credentials but many services use SPNEGO without requiring delgation.

The Trigger II

So what is actually happening during the parsing that is triggering the error.

Within the Kerberos protocol a session key is established, certain fields can be encrypted using the session key and on parsing the message this session key can be used to decrypt the field.  

The encoded representation of an encrypted attribute contains a field that specifies the encryption type in use, for the above errors we see three types mentioned aes256-cts-hmac-sha1-96, des3-cbc-sha1, and also NULL.  Null is a special case as it specified that an encrypted field is not actually encrypted.

So back to the Java implementation, before parsing and possibly decrypting the delegated credential the Java implementation checks the type of the agreed session key.  If the session key is for aes256-cts-hmac-sha1-96, aes128-cts-hmac-sha1-96, or is arcfour then an assumption is made that the session key should be used to decrypt the credential.  For all other cases it assumes the encryption type will be NULL and that a NULL key should be used for the decryption.

So for the first error I posted above the Mac OS client used NULL for encrypting the delegated credential however as the session key is aes256-cts-hmac-sha1-96 the parsing is expecting this to have been used.

For the second error with the Linux client the session key is now des3-cbc-sha1, the Linux client did encrypt the delegated credential but the Java implementation has made the assumption it should not be doing that.

The Next Steps

As I said at the start at the time of posting this I do not have the solution, only the information that will hopefully make it easier for you to see what the error means and one possible workaround.

Overall this error is as a result of different approaches to message creation and parsing between the different clients and the Java implementation - so now I need to get to the bottom of which side is actually at fault.  My initial suspicion is the Java side as it should be possibly to identify the encryption type that was used before this error is reported but that is going to require some further investigation and discussions.