| package manager |
| |
| import ( |
| "crypto/subtle" |
| "encoding/base64" |
| "fmt" |
| |
| "github.com/docker/swarmkit/ca" |
| "github.com/docker/swarmkit/manager/encryption" |
| "github.com/docker/swarmkit/manager/state/raft" |
| ) |
| |
| const ( |
| // the raft DEK (data encryption key) is stored in the TLS key as a header |
| // these are the header values |
| pemHeaderRaftDEK = "raft-dek" |
| pemHeaderRaftPendingDEK = "raft-dek-pending" |
| pemHeaderRaftDEKNeedsRotation = "raft-dek-needs-rotation" |
| ) |
| |
| // RaftDEKData contains all the data stored in TLS pem headers |
| type RaftDEKData struct { |
| raft.EncryptionKeys |
| NeedsRotation bool |
| } |
| |
| // UnmarshalHeaders loads the state of the DEK manager given the current TLS headers |
| func (r RaftDEKData) UnmarshalHeaders(headers map[string]string, kekData ca.KEKData) (ca.PEMKeyHeaders, error) { |
| var ( |
| currentDEK, pendingDEK []byte |
| err error |
| ) |
| |
| if currentDEKStr, ok := headers[pemHeaderRaftDEK]; ok { |
| currentDEK, err = decodePEMHeaderValue(currentDEKStr, kekData.KEK) |
| if err != nil { |
| return nil, err |
| } |
| } |
| if pendingDEKStr, ok := headers[pemHeaderRaftPendingDEK]; ok { |
| pendingDEK, err = decodePEMHeaderValue(pendingDEKStr, kekData.KEK) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| if pendingDEK != nil && currentDEK == nil { |
| return nil, fmt.Errorf("there is a pending DEK, but no current DEK") |
| } |
| |
| _, ok := headers[pemHeaderRaftDEKNeedsRotation] |
| return RaftDEKData{ |
| NeedsRotation: ok, |
| EncryptionKeys: raft.EncryptionKeys{ |
| CurrentDEK: currentDEK, |
| PendingDEK: pendingDEK, |
| }, |
| }, nil |
| } |
| |
| // MarshalHeaders returns new headers given the current KEK |
| func (r RaftDEKData) MarshalHeaders(kekData ca.KEKData) (map[string]string, error) { |
| headers := make(map[string]string) |
| for headerKey, contents := range map[string][]byte{ |
| pemHeaderRaftDEK: r.CurrentDEK, |
| pemHeaderRaftPendingDEK: r.PendingDEK, |
| } { |
| if contents != nil { |
| dekStr, err := encodePEMHeaderValue(contents, kekData.KEK) |
| if err != nil { |
| return nil, err |
| } |
| headers[headerKey] = dekStr |
| } |
| } |
| |
| if r.NeedsRotation { |
| headers[pemHeaderRaftDEKNeedsRotation] = "true" |
| } |
| |
| // return a function that updates the dek data on write success |
| return headers, nil |
| } |
| |
| // UpdateKEK optionally sets NeedRotation to true if we go from unlocked to locked |
| func (r RaftDEKData) UpdateKEK(oldKEK, candidateKEK ca.KEKData) ca.PEMKeyHeaders { |
| if _, unlockedToLocked, err := compareKEKs(oldKEK, candidateKEK); err == nil && unlockedToLocked { |
| return RaftDEKData{ |
| EncryptionKeys: r.EncryptionKeys, |
| NeedsRotation: true, |
| } |
| } |
| return r |
| } |
| |
| // Returns whether the old KEK should be replaced with the new KEK, whether we went from |
| // unlocked to locked, and whether there was an error (the versions are the same, but the |
| // keks are different) |
| func compareKEKs(oldKEK, candidateKEK ca.KEKData) (bool, bool, error) { |
| keksEqual := subtle.ConstantTimeCompare(oldKEK.KEK, candidateKEK.KEK) == 1 |
| switch { |
| case oldKEK.Version == candidateKEK.Version && !keksEqual: |
| return false, false, fmt.Errorf("candidate KEK has the same version as the current KEK, but a different KEK value") |
| case oldKEK.Version >= candidateKEK.Version || keksEqual: |
| return false, false, nil |
| default: |
| return true, oldKEK.KEK == nil, nil |
| } |
| } |
| |
| // RaftDEKManager manages the raft DEK keys using TLS headers |
| type RaftDEKManager struct { |
| kw ca.KeyWriter |
| rotationCh chan struct{} |
| } |
| |
| var errNoUpdateNeeded = fmt.Errorf("don't need to rotate or update") |
| |
| // this error is returned if the KeyReadWriter's PEMKeyHeaders object is no longer a RaftDEKData object - |
| // this can happen if the node is no longer a manager, for example |
| var errNotUsingRaftDEKData = fmt.Errorf("RaftDEKManager can no longer store and manage TLS key headers") |
| |
| // NewRaftDEKManager returns a RaftDEKManager that uses the current key writer |
| // and header manager |
| func NewRaftDEKManager(kw ca.KeyWriter) (*RaftDEKManager, error) { |
| // If there is no current DEK, generate one and write it to disk |
| err := kw.ViewAndUpdateHeaders(func(h ca.PEMKeyHeaders) (ca.PEMKeyHeaders, error) { |
| dekData, ok := h.(RaftDEKData) |
| // it wasn't a raft DEK manager before - just replace it |
| if !ok || dekData.CurrentDEK == nil { |
| return RaftDEKData{ |
| EncryptionKeys: raft.EncryptionKeys{ |
| CurrentDEK: encryption.GenerateSecretKey(), |
| }, |
| }, nil |
| } |
| return nil, errNoUpdateNeeded |
| }) |
| if err != nil && err != errNoUpdateNeeded { |
| return nil, err |
| } |
| return &RaftDEKManager{ |
| kw: kw, |
| rotationCh: make(chan struct{}, 1), |
| }, nil |
| } |
| |
| // NeedsRotation returns a boolean about whether we should do a rotation |
| func (r *RaftDEKManager) NeedsRotation() bool { |
| h, _ := r.kw.GetCurrentState() |
| data, ok := h.(RaftDEKData) |
| if !ok { |
| return false |
| } |
| return data.NeedsRotation || data.EncryptionKeys.PendingDEK != nil |
| } |
| |
| // GetKeys returns the current set of DEKs. If NeedsRotation is true, and there |
| // is no existing PendingDEK, it will try to create one. If there are any errors |
| // doing so, just return the original. |
| func (r *RaftDEKManager) GetKeys() raft.EncryptionKeys { |
| var newKeys, originalKeys raft.EncryptionKeys |
| err := r.kw.ViewAndUpdateHeaders(func(h ca.PEMKeyHeaders) (ca.PEMKeyHeaders, error) { |
| data, ok := h.(RaftDEKData) |
| if !ok { |
| return nil, errNotUsingRaftDEKData |
| } |
| originalKeys = data.EncryptionKeys |
| if !data.NeedsRotation || data.PendingDEK != nil { |
| return nil, errNoUpdateNeeded |
| } |
| newKeys = raft.EncryptionKeys{ |
| CurrentDEK: data.CurrentDEK, |
| PendingDEK: encryption.GenerateSecretKey(), |
| } |
| return RaftDEKData{EncryptionKeys: newKeys}, nil |
| }) |
| if err != nil { |
| return originalKeys |
| } |
| return newKeys |
| } |
| |
| // RotationNotify the channel used to notify subscribers as to whether there |
| // should be a rotation done |
| func (r *RaftDEKManager) RotationNotify() chan struct{} { |
| return r.rotationCh |
| } |
| |
| // UpdateKeys will set the updated encryption keys in the headers. This finishes |
| // a rotation, and is expected to set the CurrentDEK to the previous PendingDEK. |
| func (r *RaftDEKManager) UpdateKeys(newKeys raft.EncryptionKeys) error { |
| return r.kw.ViewAndUpdateHeaders(func(h ca.PEMKeyHeaders) (ca.PEMKeyHeaders, error) { |
| data, ok := h.(RaftDEKData) |
| if !ok { |
| return nil, errNotUsingRaftDEKData |
| } |
| // If there is no current DEK, we are basically wiping out all DEKs (no header object) |
| if newKeys.CurrentDEK == nil { |
| return nil, nil |
| } |
| return RaftDEKData{ |
| EncryptionKeys: newKeys, |
| NeedsRotation: data.NeedsRotation, |
| }, nil |
| }) |
| } |
| |
| // MaybeUpdateKEK does a KEK rotation if one is required. Returns whether |
| // the kek was updated, whether it went from unlocked to locked, and any errors. |
| func (r *RaftDEKManager) MaybeUpdateKEK(candidateKEK ca.KEKData) (bool, bool, error) { |
| var updated, unlockedToLocked bool |
| err := r.kw.ViewAndRotateKEK(func(currentKEK ca.KEKData, h ca.PEMKeyHeaders) (ca.KEKData, ca.PEMKeyHeaders, error) { |
| var err error |
| updated, unlockedToLocked, err = compareKEKs(currentKEK, candidateKEK) |
| if err == nil && !updated { // if we don't need to rotate the KEK, don't bother updating |
| err = errNoUpdateNeeded |
| } |
| if err != nil { |
| return ca.KEKData{}, nil, err |
| } |
| |
| data, ok := h.(RaftDEKData) |
| if !ok { |
| return ca.KEKData{}, nil, errNotUsingRaftDEKData |
| } |
| |
| if unlockedToLocked { |
| data.NeedsRotation = true |
| } |
| return candidateKEK, data, nil |
| }) |
| if err == errNoUpdateNeeded { |
| err = nil |
| } |
| |
| if err == nil && unlockedToLocked { |
| r.rotationCh <- struct{}{} |
| } |
| return updated, unlockedToLocked, err |
| } |
| |
| func decodePEMHeaderValue(headerValue string, kek []byte) ([]byte, error) { |
| var decrypter encryption.Decrypter = encryption.NoopCrypter |
| if kek != nil { |
| _, decrypter = encryption.Defaults(kek) |
| } |
| valueBytes, err := base64.StdEncoding.DecodeString(headerValue) |
| if err != nil { |
| return nil, err |
| } |
| result, err := encryption.Decrypt(valueBytes, decrypter) |
| if err != nil { |
| return nil, ca.ErrInvalidKEK{Wrapped: err} |
| } |
| return result, nil |
| } |
| |
| func encodePEMHeaderValue(headerValue []byte, kek []byte) (string, error) { |
| var encrypter encryption.Encrypter = encryption.NoopCrypter |
| if kek != nil { |
| encrypter, _ = encryption.Defaults(kek) |
| } |
| encrypted, err := encryption.Encrypt(headerValue, encrypter) |
| if err != nil { |
| return "", err |
| } |
| return base64.StdEncoding.EncodeToString(encrypted), nil |
| } |