[Remoting 3] SASL and Kerberos

Working on how Kerberos can be supported with Remoting for REM3-29, the first stage is understanding the unederlying mechanism. 

 

Please Note, this article is not a user guide, it is a reference guide to hold the information needed so that the issue REM3-129 can be implemented.

 

Whilst there is some JDK documentation this does not go into the finer details, the implementation makes quite a few assumptions so this article is to capture the additional detail.

 

Client

OS local Kerberos configuration can be used to specify the Ream and KDC, alternatively JVM wide system properties can be used: -

 

System.setProperty("java.security.krb5.kdc", "ec2-?-?-?-?.compute-1.amazonaws.com");
System.setProperty("java.security.krb5.realm", "DARRANL.JBOSS.ORG");

 

 

On the client side unless you fallback to default JAAS configurations the SASL negotiation needs to be within a PrivilegedAction where a Subject for the local clients identity is passed in: -

 

Subject.doAs(clientSubject, new PrivilegedAction<Void>() {
            @Override
            public Void run() {
                try {
                    client.haveAChat();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
});

 

 

To obtain the subject a JAAS LoginContext is used with the 'com.sun.security.auth.module.Krb5LoginModule' LoginModule, although JAAS is used we don't need the configuration within a descriptor - we can provide a simplified configuration for the client and dynamically configure the LoginContext.

 

No special configuration of the LoginModule is required on the client side.  There are three configuration options for the client: -

  1. Use a supplied CallbackHandler to prompt the client for the username and password, the LoginModule will then obtain their Kerberos tickets.
  2. Configure to use a keytab without prompting - useful for unattended processes e.g. server instances.
  3. Configure to use local OS cache i.e. if user obtained Kerberos ticket on OS log on use this identity.

 

The client side does not require the 'storeKey' option being set to true although if a process is both a client and a server this can be set to true.

 

Inside the PriviledgedAction the SaslClient can be obtained as: -

 

Sasl.createSaslClient(MECHS, null, "remoting", "test2", Collections.EMPTY_MAP,
                new NoCallbackHanlder());

 

 

The parameters are: -

  1. Array of mechanisms, in this case just "GSSAPI"
  2. The authorization ID, if not supplied it will default to the username@REALM of the user.
  3. Protocol
  4. Server host name
  5. Configuration options
  6. Callback Handler

 

The values for protocol and server host name are criticial to the negotiation process.

 

In this example before the communication with the server commences the client side will automatically send a TGS-REQ to the KDC for the server name 'remoting/test2' - this is an integral part of the process an if this server is not known by the KDC the process will terminate.

 

Do also note that the host name may undergo the following conversion, the comment in the code openly admints that this is against the RFC but goes ahead and does it anyway.  The end result is that the name could end up being the fully qualified host name of the remote service.  If the server name does not match then the original supplied name is used.

// RFC4120 does not recommend canonicalizing a hostname.
// However, for compatibility reason, we will try
// canonicalize it and see if the output looks better.

String canonicalized = (InetAddress.getByName(hostName)).
                            getCanonicalHostName();

 

 

For this TGS-REQ a service principal mapping on the KDC is sufficient, however there are also issues on the server side of this to consider.

 

There are a couple of options which could be passed in, one is to enable server_auth, this setting means that in addition to the server verifying the identity of the client the client will also verify the identity of the server.

 

At the stage of the SASL message exchange the CallbackHandler on the client is not required to handle any callbacks, the client side idenity has already been established when the Subject was obtained.

 

If this was SPNEGO the web browser would be verifying the DNS name of the server it is connecting to, we should consider if our client should allow any flexibility in the setting of server host name or should we mandate that it does match the host name we are connecting to - at the same point also consider banning connection to IP addresses as DNS is also fundamental to this.

 

Server

As for the client side the Realm / KDC configuration is required, again this can either be configured using OS specific configuration or the same system properties can be set.

 

On the server a Subject needs to be obtained similar to how one was obtained for the client, this time the Subject is for the identity of the server - all the Sasl calls will then be within a PriviledgedAction.

 

When the Subject for the server is obtained here it is essential that 'storeKey' is set to true for the LoginModule, this will store the KerberosKeys for the server in the PrivateCredentials of the Subject.

 

On the server side the SaslServer can be obtained with: -

 

SaslServer saslServer = Sasl.createSaslServer(GSSAPI, "remoting", "test2", Collections.EMPTY_MAP,
                                              new AuthorizeCallbackHandler());

 

 

Here the parameters are: -

  1. Chosen mechanism "GSSAPI"
  2. Protocol
  3. Server Host Name
  4. Configuration options
  5. Callback Handler

 

On the server side the protocol and server host name are again critical to the process but this time they affect how the PrivateCredentials of the Subject are searched.

 

In this example the protocol is 'remoting', the server host name is 'test2' and from the system properties the realm is 'DARRANL.JBOSS.ORG', the private credentials of the Subject will be searched for KerberosKeys with the prinicipal name "remoting/test2@DARRANL.JBOSS.ORG"

 

On the server side an error indicating "Mechanism level: Failed to find any Kerberos Key" most likely translates to mean that no key has been found with the expected principal name.

 

The server identity does need to match the identity the client received in the TGS-REP from the TGS-REQ that the client sent in, however I need to verify if the names within the Subject also need to match or if we can just allign the createSaslServer identity with what is provided in the Subject.  The reason this needs to be considered is that it may be desirable to just have a single Kerberos identity on the server for both Remoting and HTTP, however for HTTP the identity may already be mapped to HTTP/test2

 

The only Callback that needs to be supported is the AuthorizeCallback, by default the authentication ID will be in the form username@REALM - there may be some desire to drop the @REALM part.

 

In the exchange between the client and the server we do need to be able to support 0 length SASL messages.

 

There is also an option that may be possible to set on the server side if the identity is only being used for incomming requests and that is to set isInitiator to false, this removes one round trip with the KDC.