123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- 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
- }
|