Working on how Kerberos can be supported with Remoting for REM3-29, the first stage is understanding the unederlying mechanism.
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: -
- Use a supplied CallbackHandler to prompt the client for the username and password, the LoginModule will then obtain their Kerberos tickets.
- Configure to use a keytab without prompting - useful for unattended processes e.g. server instances.
- 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: -
- Array of mechanisms, in this case just "GSSAPI"
- The authorization ID, if not supplied it will default to the username@REALM of the user.
- Protocol
- Server host name
- Configuration options
- 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.
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: -
- Chosen mechanism "GSSAPI"
- Protocol
- Server Host Name
- Configuration options
- 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"
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.