blob: 4813a67d53a475c3b1ff0845a5a1de0a39b1f073 [file] [log] [blame]
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
}