Design of authentication protocol (AUTH)
========================================
Author: Roland Raez, Bela Ban, Chris Mills
Goal: to prevent random members from joining a group. Members have to pass authentication
to join a group, otherwise they will be rejected
[pasted from JGroupsAUTH wiki]
Definition
AUTH is used to provide a layer of authentication to JGroups. This allows you to define pluggable security
that defines if a node should be allowed to join a group. AUTH sits below the GMS protocol and listens for
JOIN REQUEST messages. When a JOIN REQUEST is received it tries to find an AuthHeader? object, inside of which
should be an implementation of the AuthToken? object.
AuthToken? is an abstract class, implementations of which are responsible for providing the actual authentication
mechanism. Some basic implementations of AuthToken? are provide in the
org.jgroups.auth package (SimpleToken?, MD5Token and X509Token).
Effectivly all these implementations do is encrypt a string (found in the jgroups config) and pass that on
the JOIN REQUEST.
When authentication is successful, the message is simply passed up the stack to the GMS protocol.
When it fails, the AUTH protocol creates a JOIN RESPONSE message with a failure string and passes
it back down the stack. This failure string informs the client of the reason for failure. Clients will
then fail to join the group and will throw a SecurityException?. If this error string is null then
authentication is considered to have passed.
Example Configuration
In the above example the AUTH protocol delegates authentication to an instance of the
org.jgroups.auth.X509Token1_5 class. The only parameter that AUTH requires is the auth_class
attribute which defines the authentication mechanism. All other parameters defined in the configuration are
passed in to the instance of the auth_class.
This allows pluggable authentication mechanisms, abstracted from the core of JGroups, to be configured to
secure and lock down who can join a group.
Creating an AUTH module
1. Create a class that extends org.jgroups.auth.AuthToken
2. You must have an empty constructor
3. Implement the public void setValue(Properties properties) method to recieve properties from the JGroups config.
4. Implement the public String getName() method to return the package and class name
5. Implement the public boolean authenticate(AuthToken token) method to provide the actual authentication
mechanism of clients.
6. In the jgroups config XML for AUTH set the auth_class attribute to your new authentication class. Remember
to include anyother properties your class may require.
Example Failure
When authentication fails a SecurityException? is thrown on the client trying to join the group. Below is
an example stack trace:
org.jboss.jgroups.fileshare.exception.FileShareException: org.jgroups.ChannelException: connect() failed
at org.jboss.jgroups.fileshare.FileShare.(FileShare.java:28)
at org.jboss.jgroups.fileshare.FileShare.main(FileShare.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:78)
Caused by: org.jgroups.ChannelException: connect() failed
at org.jgroups.JChannel.connect(JChannel.java:425)
at org.jboss.jgroups.fileshare.FileShare.(FileShare.java:21)
... 6 more
Caused by: java.lang.SecurityException: Authentication failed
at org.jgroups.protocols.pbcast.ClientGmsImpl.join(ClientGmsImpl.java:132)
at org.jgroups.protocols.pbcast.GMS.down(GMS.java:738)
at org.jgroups.stack.DownHandler.run(Protocol.java:120)
On the coordinator the following is displayed for every failed membership join event:
21125 [WARN] X509Token.authenticate(): - X509 authentication failed
21125 [WARN] AUTH.up(): - AUTH failed to validate AuthHeader token
[pasted from Roland's email]
Recently I discussed with Bela Ban about a new Protocol called AUTH to
authenticate the joining of members.
I would like to implement this protocol. Here are my thoughts.
AUTH is the next layer under pbcast.GMS. When pbcast.GMS sends a join
request (a GMS.GmsHeader.JOIN_REQ header) AUTH just places the credential
for the authentication in its own header. The join message will be sent to
the coordinator and before the join will reach the GMS protocol the AUTH
protocol checks the auth header. When the header authenticated successfully,
it just passes up the join request and pbcast.GMS will accept the join and
sends the header GMS.GmsHeader.JOIN_RSP containing a pbcast.JoinRsp back.
When the authentication is not successful the AUTH protocol answers the
GMS.GmsHeader.JOIN_REQ itself and sends back a GMS.GmsHeader.JOIN_RSP
containing a pbcast.JoinRsp object with an error message.
The pbcast.ClientGmsImpl receives in both cases the GMS.GmsHeader.JOIN_RSP.
In the case where the join request could not be successfully authenticated
the message in pbcast.JoinRsp can be used to throw a RuntimeException.
Requirements for other Protocols:
- pbcast.JoinRsp needs a new attribute (String), e.g. errorMsg
- pbcast.ClientGmsImpl should use the attribute defined above and throw an
exception when there was an error.
- Optionally ENCRYPT can be extended so that AUTH provides the symmetric
key
I think it would be fine when AUTH supports two authentication methods:
Token:
A simple token (String) based authentication using a configured String or a
String read from an external file which can be protected so that only
certain proceses can read the credential. Each member in the group needs to
have the same token else the authentication will not succeed:
1. AUTH sends along with the GMS.GmsHeader.JOIN_REQ the hash of the
credential including its address (each address will produce another hash).
2. The coordinator compares it's own hash with the one from the client.
When they match the JOIN_REQ is passed up, else negative JOIN_RSP (with
errorMsg set) is sent back (--> no further steps).
3. Along with the positive response JOIN_RSP from the GMS protocol the
coordinator sends the hash of the credential including its address.
4. The client verifies the hash of the coordinator. When the verification
is ok, he passes up the JOIN_RSP else he sends down a LEAVE_REQ.
Should the following LEAVE_RSP be discarded by the AUTH protocol layer?
Certificate:
Implementing Certificate based authentication is more secure because
spoofing IP addresses doesn't impact the security when additional message
encryption is used. I think that it is a requirement that each member in the
group can have the same certificate.
The following algorithm allows the usage of the same certificate for each
member in the group.
1. The joiner (GMS client) sends with the JOIN_REQ message its certificate
and a nonce.
2. The coordinator verifies if he trusts the certificate of the client by
checking the certificate chain. The authentication of the joiner is
performed in a later step! When he does not trust, he sends back a negative
GMS.GmsHeader.JOIN_RSP (--> no further steps).
3. The coordinator create a AUTH.REQ message containing its certificate,
the nonce from the joiner encrypted with the private key of its certificate
and a new nonce
4. The joiner verifies if he trusts the server by verifying the message
(decrypts the nonce with the public key from the coordinator (retrieved from
the certificate)) and optionally by checking the certificate chain. When the
verification is ok, he creates an AUTH.RSP message containing the nonce from
the coordinator encrypted with the private key of the joiner. When the
verification is not successful AUTH.RSP message containing an error message.
5. The coordinator verifies the AUTH.RSP message. When it contains an error
message a negative JOIN_RSP is sent down else the joiner is authenticated by
decrypting the nonce with the public key of the joiner. When this is ok a
JOIN_REQ is sent up to GMS else a negative JOIN_RSP is sent down.
6. The GMS protocols answers with a JOIN_RSP. When ENCRYPT and AUTH work
together the AUTH protocol or the ENCRYPT protocol sends along this JOIN_RSP
the new symmetric encryption key encrypted with the public key of the
joiner.
This protocol uses a lot of public private key encryptions (4 or five when
AUTH provides the key for ENCRYP) but I think this is ok (members usually
join not very often).
The session key created by the coordinator could be used in the ENCRPT
protocol for the message encryption.
For me there are the following open questions:
- Are the two AUTH methods reasonable?
- Should ENCRYTP be extended so that the AUTH protocol distributes the new
symmetric encryption key?
- How would AUTH "send" the key to the ENCRYPT protocol? Using a header
which is cleared in the ENCRYPT layer would need that the ENCRYPT must be
below AUTH because of fragmentation. This would be ok for me.
- Would it be ok that when a member leaves the group that still the same
symmetric key is used?