Although JGroups provides message encryption (ENCRYPT) and cluster admission control (AUTH), it does not prevent malicious attacks.
For example, if a rogue node creates a minimal stack without encryption or authentication, then it is possible for that member to
- Send messages to the cluster
- Capture an existing message from a cluster member P, change it (e.g. the seqno) and resend it spooking P's address
- Install a new view including itself
- Possibly install a new shared key in ENCRYPT, thereby being able to receive cluster messages
1. Prevent cluster members from delivering messages from a non-member (exceptions are join requests from new members and merge requests)
2. Prevent a non-member from receiving any cluster messages
The first goal also prevents rogue views from getting installed, or new shared secrets from being installed into ENCRYPT.
- Use ENCRYPT for encryption of the payload and the headers
- Now every message carries an encrypt header
- (Let's assume for the moment that ENCRYPT is configured to use a shared key)
- When a message is received, it is decrypted using the shared key (this can only be done by members having the shared key)
- If the message doesn't have an encrypt header, it will be dropped
- For some messages that carry their information in the payload rather than the headers, we don't even need to encrypt the entire message (which is faster), e.g.
- Views and MergeViews: they are stored in the payload (GMS)
- A new secret key sent by the key server is also stored in the payload (ENCRYPT)
- ALTERNATIVE I:
- We encrypt a phrase with the secret key. Everyone who has the shared key will be able to decrypt it and thus pass a message up. Messages from a non-member would be dropped.
- Could the above also be done by the sender signing a phrase with its private key and the receiver decrypting it with the sender's public key, to enforce non-repudiation?
- Hmm, this would be prone to replay attacks, so either the phrase would have to be changed every time, or the ENCRYT header would have to be encrypted as well (encrypt_entire_msg==true.
- ALTERNATIVE II: digital signature
- The sender creates a hash from the message (digest) and encrypts the digest with its secret key, and ships it with the message
- The receiver decrypts the digest, computes the digest from the message and drops the message if the digests don't match
- The diff to the first alternative is that a receiver doesn't even need to deliver the message and stop at (failing) de-serialization.
- Downside: additional work to be done and space used when sending the signature with every message
- A rogue member R could catch a message from P by simply joining the same multicast group and port
- R could then increment the last seqno seen, e.g. 23, to 24 and send the message on behalf of P
- However, the decryption won't work because R doesn't have the shared key. Also, modifying the headers and resending the message won't work as R cannot get at the headers because they're encrypted, too
- R could still resend the captured message but that's useless as (a) one of the retransmission protocols (UNICAST3 or NAKACK2) will drop the duplicate message
- This requires ENCRYPT to sit somewhere below GMS
- R sends a new view consisting of the existing view plus R
- If ENCRYPT passes the message up because it doesn't discard message without encrypt header, GMS will install the new view!
- R sends a SECRETKEY msg with a new shared key, encrypted with its public key
- Everyone install the new shared secret and R can now receive encrypted messages from cluster members!
- Cluster members would receive that message and decrypt it with their own (different) shared key, leading to garbage in the payload
- The message would get delivered to the application but de-serialization would fail as the payload is garbage
- See ALTERNATIVE above to prevent cluster members from delivering messages from rogue members in the first place
- Rogue non-member R
- Resending of captures (and unmodified) cluster messages by R: YES (however, those messages will get dropped by either NAKACK2 or UNICAST3 as duplicates)
- Capturing and resending of messages as new messages by R (e.g. by incrementing the seqno): YES if ENCRYPT.encrypt_entire_msg is false, NO if true
- Reception of cluster messages by R: YES, but R won't be able to read the contents as the payload is encrypted
- Cluster members receiving messages from R: YES, but applications will throw an exception trying to read the contents of the payload as it is encrypted, and decryption failed as the secret key was wrong.
- How do we handle (unicast) JOINs from non-members?
- Ditto for merge requests (unicasts)