Merge pull request #36979 from anshulpundir/vndr

Bump swarmkit to bd69f6e8e301645afd344913fa1ede53a0a111fb
This commit is contained in:
Yong Tang 2018-05-06 22:28:46 +02:00 committed by GitHub
commit 5848b9eeb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 113 additions and 70 deletions

View file

@ -119,7 +119,7 @@ github.com/dmcgowan/go-tar go1.10
github.com/stevvooe/ttrpc d4528379866b0ce7e9d71f3eb96f0582fc374577
# cluster
github.com/docker/swarmkit 33d06bf5189881b4d1e371b5571f4d3acf832816
github.com/docker/swarmkit bd69f6e8e301645afd344913fa1ede53a0a111fb
github.com/gogo/protobuf v0.4
github.com/cloudflare/cfssl 7fb22c8cba7ecaf98e4082d22d65800cf45e042a
github.com/fernet/fernet-go 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2

View file

@ -26,10 +26,8 @@ import (
"github.com/cloudflare/cfssl/signer/local"
"github.com/docker/go-events"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/ca/keyutils"
"github.com/docker/swarmkit/ca/pkcs8"
"github.com/docker/swarmkit/connectionbroker"
"github.com/docker/swarmkit/fips"
"github.com/docker/swarmkit/ioutils"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
@ -636,7 +634,7 @@ func newLocalSigner(keyBytes, certBytes []byte, certExpiry time.Duration, rootPo
}
// The key should not be encrypted, but it could be in PKCS8 format rather than PKCS1
priv, err := keyutils.ParsePrivateKeyPEMWithPassword(keyBytes, nil)
priv, err := helpers.ParsePrivateKeyPEM(keyBytes)
if err != nil {
return nil, errors.Wrap(err, "malformed private key")
}
@ -782,14 +780,6 @@ func CreateRootCA(rootCN string) (RootCA, error) {
return RootCA{}, err
}
// Convert key to PKCS#8 in FIPS mode
if fips.Enabled() {
key, err = pkcs8.ConvertECPrivateKeyPEM(key)
if err != nil {
return RootCA{}, err
}
}
rootCA, err := NewRootCA(cert, cert, key, DefaultNodeCertExpiration, nil)
if err != nil {
return RootCA{}, err

View file

@ -73,21 +73,30 @@ func (e ErrInvalidKEK) Error() string {
// KeyReadWriter is an object that knows how to read and write TLS keys and certs to disk,
// optionally encrypted and optionally updating PEM headers.
type KeyReadWriter struct {
mu sync.Mutex
kekData KEKData
paths CertPaths
headersObj PEMKeyHeaders
mu sync.Mutex
kekData KEKData
paths CertPaths
headersObj PEMKeyHeaders
keyFormatter keyutils.Formatter
}
// NewKeyReadWriter creates a new KeyReadWriter
func NewKeyReadWriter(paths CertPaths, kek []byte, headersObj PEMKeyHeaders) *KeyReadWriter {
return &KeyReadWriter{
kekData: KEKData{KEK: kek},
paths: paths,
headersObj: headersObj,
kekData: KEKData{KEK: kek},
paths: paths,
headersObj: headersObj,
keyFormatter: keyutils.Default,
}
}
// SetKeyFormatter sets the keyformatter with which to encrypt and decrypt keys
func (k *KeyReadWriter) SetKeyFormatter(kf keyutils.Formatter) {
k.mu.Lock()
defer k.mu.Unlock()
k.keyFormatter = kf
}
// Migrate checks to see if a temporary key file exists. Older versions of
// swarmkit wrote temporary keys instead of temporary certificates, so
// migrate that temporary key if it exists. We want to write temporary certificates,
@ -324,8 +333,10 @@ func (k *KeyReadWriter) readKey() (*pem.Block, error) {
return nil, ErrInvalidKEK{Wrapped: x509.IncorrectPasswordError}
}
derBytes, err := keyutils.DecryptPEMBlock(keyBlock, k.kekData.KEK)
if err != nil {
derBytes, err := k.keyFormatter.DecryptPEMBlock(keyBlock, k.kekData.KEK)
if err == keyutils.ErrFIPSUnsupportedKeyFormat {
return nil, err
} else if err != nil {
return nil, ErrInvalidKEK{Wrapped: err}
}
@ -349,7 +360,7 @@ func (k *KeyReadWriter) readKey() (*pem.Block, error) {
// writing it to disk. If the kek is nil, writes it to disk unencrypted.
func (k *KeyReadWriter) writeKey(keyBlock *pem.Block, kekData KEKData, pkh PEMKeyHeaders) error {
if kekData.KEK != nil {
encryptedPEMBlock, err := keyutils.EncryptPEMBlock(keyBlock.Bytes, kekData.KEK)
encryptedPEMBlock, err := k.keyFormatter.EncryptPEMBlock(keyBlock.Bytes, kekData.KEK)
if err != nil {
return err
}
@ -404,7 +415,7 @@ func (k *KeyReadWriter) DowngradeKey() error {
}
if k.kekData.KEK != nil {
newBlock, err = keyutils.EncryptPEMBlock(newBlock.Bytes, k.kekData.KEK)
newBlock, err = k.keyFormatter.EncryptPEMBlock(newBlock.Bytes, k.kekData.KEK)
if err != nil {
return err
}

View file

@ -13,10 +13,28 @@ import (
"github.com/cloudflare/cfssl/helpers"
"github.com/docker/swarmkit/ca/pkcs8"
"github.com/docker/swarmkit/fips"
)
var errFIPSUnsupportedKeyFormat = errors.New("unsupported key format due to FIPS compliance")
// Formatter provides an interface for converting keys to the right format, and encrypting and decrypting keys
type Formatter interface {
ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error)
DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error)
EncryptPEMBlock(data, password []byte) (*pem.Block, error)
}
// ErrFIPSUnsupportedKeyFormat is returned when encryption/decryption operations are attempted on a PKCS1 key
// when FIPS mode is enabled.
var ErrFIPSUnsupportedKeyFormat = errors.New("unsupported key format due to FIPS compliance")
// Default is the default key util, where FIPS is not required
var Default Formatter = &utils{fips: false}
// FIPS is the key utility which enforces FIPS compliance
var FIPS Formatter = &utils{fips: true}
type utils struct {
fips bool
}
// IsPKCS8 returns true if the provided der bytes is encrypted/unencrypted PKCS#8 key
func IsPKCS8(derBytes []byte) bool {
@ -31,9 +49,14 @@ func IsPKCS8(derBytes []byte) bool {
})
}
// IsEncryptedPEMBlock checks if a PKCS#1 or PKCS#8 PEM-block is encrypted or not
func IsEncryptedPEMBlock(block *pem.Block) bool {
return pkcs8.IsEncryptedPEMBlock(block) || x509.IsEncryptedPEMBlock(block)
}
// ParsePrivateKeyPEMWithPassword parses an encrypted or a decrypted PKCS#1 or PKCS#8 PEM to crypto.Signer.
// It returns an error in FIPS mode if PKCS#1 PEM bytes are passed.
func ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) {
func (u *utils) ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("Could not parse PEM")
@ -41,26 +64,20 @@ func ParsePrivateKeyPEMWithPassword(pemBytes, password []byte) (crypto.Signer, e
if IsPKCS8(block.Bytes) {
return pkcs8.ParsePrivateKeyPEMWithPassword(pemBytes, password)
} else if fips.Enabled() {
return nil, errFIPSUnsupportedKeyFormat
} else if u.fips {
return nil, ErrFIPSUnsupportedKeyFormat
}
return helpers.ParsePrivateKeyPEMWithPassword(pemBytes, password)
}
// IsEncryptedPEMBlock checks if a PKCS#1 or PKCS#8 PEM-block is encrypted or not
// It returns false in FIPS mode even if PKCS#1 is encrypted
func IsEncryptedPEMBlock(block *pem.Block) bool {
return pkcs8.IsEncryptedPEMBlock(block) || (!fips.Enabled() && x509.IsEncryptedPEMBlock(block))
}
// DecryptPEMBlock requires PKCS#1 or PKCS#8 PEM Block and password to decrypt and return unencrypted der []byte
// It returns an error in FIPS mode when PKCS#1 PEM Block is passed.
func DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) {
func (u *utils) DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) {
if IsPKCS8(block.Bytes) {
return pkcs8.DecryptPEMBlock(block, password)
} else if fips.Enabled() {
return nil, errFIPSUnsupportedKeyFormat
} else if u.fips {
return nil, ErrFIPSUnsupportedKeyFormat
}
return x509.DecryptPEMBlock(block, password)
@ -68,11 +85,11 @@ func DecryptPEMBlock(block *pem.Block, password []byte) ([]byte, error) {
// EncryptPEMBlock takes DER-format bytes and password to return an encrypted PKCS#1 or PKCS#8 PEM-block
// It returns an error in FIPS mode when PKCS#1 PEM bytes are passed.
func EncryptPEMBlock(data, password []byte) (*pem.Block, error) {
func (u *utils) EncryptPEMBlock(data, password []byte) (*pem.Block, error) {
if IsPKCS8(data) {
return pkcs8.EncryptPEMBlock(data, password)
} else if fips.Enabled() {
return nil, errFIPSUnsupportedKeyFormat
} else if u.fips {
return nil, ErrFIPSUnsupportedKeyFormat
}
cipherType := x509.PEMCipherAES256

View file

@ -1,11 +0,0 @@
package fips
import "os"
// EnvVar is the environment variable which stores FIPS mode state
const EnvVar = "GOFIPS"
// Enabled returns true when FIPS mode is enabled
func Enabled() bool {
return os.Getenv(EnvVar) != ""
}

View file

@ -632,7 +632,9 @@ func (a *Allocator) allocateServices(ctx context.Context, existingAddressesOnly
}
return nil
}); err != nil {
log.G(ctx).WithError(err).Error("failed committing allocation of services during init")
for _, s := range allocatedServices {
log.G(ctx).WithError(err).Errorf("failed committing allocation of service %v during init", s.GetID())
}
}
return nil
@ -653,8 +655,11 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
return errors.Wrap(err, "error listing all tasks in store while trying to allocate during init")
}
logger := log.G(ctx).WithField("method", "(*Allocator).allocateTasks")
for _, t := range tasks {
if t.Status.State > api.TaskStateRunning {
logger.Debugf("task %v is in allocated state: %v", t.GetID(), t.Status.State)
continue
}
@ -667,6 +672,7 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
}
}
if !hasAddresses {
logger.Debugf("task %v has no attached addresses", t.GetID())
continue
}
}
@ -684,6 +690,7 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
if taskReadyForNetworkVote(t, s, nc) {
if t.Status.State >= api.TaskStatePending {
logger.Debugf("task %v is in allocated state: %v", t.GetID(), t.Status.State)
continue
}
@ -694,6 +701,7 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
// soon as possible.
updateTaskStatus(t, api.TaskStatePending, allocatedStatusMessage)
allocatedTasks = append(allocatedTasks, t)
logger.Debugf("allocated task %v, state update %v", t.GetID(), api.TaskStatePending)
}
continue
}
@ -702,7 +710,7 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
if err == nil {
allocatedTasks = append(allocatedTasks, t)
} else if err != errNoChanges {
log.G(ctx).WithError(err).Errorf("failed allocating task %s during init", t.ID)
logger.WithError(err).Errorf("failed allocating task %s during init", t.ID)
nc.unallocatedTasks[t.ID] = t
}
}
@ -710,13 +718,15 @@ func (a *Allocator) allocateTasks(ctx context.Context, existingAddressesOnly boo
if err := a.store.Batch(func(batch *store.Batch) error {
for _, t := range allocatedTasks {
if err := a.commitAllocatedTask(ctx, batch, t); err != nil {
log.G(ctx).WithError(err).Errorf("failed committing allocation of task %s during init", t.ID)
logger.WithError(err).Errorf("failed committing allocation of task %s during init", t.ID)
}
}
return nil
}); err != nil {
log.G(ctx).WithError(err).Error("failed committing allocation of tasks during init")
for _, t := range allocatedTasks {
logger.WithError(err).Errorf("failed committing allocation of task %v during init", t.GetID())
}
}
return nil
@ -795,6 +805,8 @@ func (a *Allocator) doTaskAlloc(ctx context.Context, ev events.Event) {
t *api.Task
)
logger := log.G(ctx).WithField("method", "(*Allocator).doTaskAlloc")
// We may have already allocated this task. If a create or update
// event is older than the current version in the store, we run the
// risk of allocating the task a second time. Only operate on the
@ -824,7 +836,7 @@ func (a *Allocator) doTaskAlloc(ctx context.Context, ev events.Event) {
if t.Status.State > api.TaskStateRunning || isDelete {
if nc.nwkAllocator.IsTaskAllocated(t) {
if err := nc.nwkAllocator.DeallocateTask(t); err != nil {
log.G(ctx).WithError(err).Errorf("Failed freeing network resources for task %s", t.ID)
logger.WithError(err).Errorf("Failed freeing network resources for task %s", t.ID)
} else {
nc.somethingWasDeallocated = true
}
@ -840,6 +852,7 @@ func (a *Allocator) doTaskAlloc(ctx context.Context, ev events.Event) {
// If we are already in allocated state, there is
// absolutely nothing else to do.
if t.Status.State >= api.TaskStatePending {
logger.Debugf("Task %s is already in allocated state %v", t.ID, t.Status.State)
delete(nc.pendingTasks, t.ID)
delete(nc.unallocatedTasks, t.ID)
return
@ -871,6 +884,7 @@ func (a *Allocator) doTaskAlloc(ctx context.Context, ev events.Event) {
a.taskCreateNetworkAttachments(t, s)
nc.pendingTasks[t.ID] = t
log.G(ctx).Debugf("task %v was marked pending allocation", t.ID)
}
func (a *Allocator) allocateNode(ctx context.Context, node *api.Node, existingAddressesOnly bool, networks []*api.Network) bool {
@ -1093,6 +1107,8 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
taskUpdated := false
nc := a.netCtx
logger := log.G(ctx).WithField("method", "(*Allocator).allocateTask")
// We might be here even if a task allocation has already
// happened but wasn't successfully committed to store. In such
// cases skip allocation and go straight ahead to updating the
@ -1102,12 +1118,12 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
if t.ServiceID != "" {
s := store.GetService(tx, t.ServiceID)
if s == nil {
err = fmt.Errorf("could not find service %s", t.ServiceID)
err = fmt.Errorf("could not find service %s for task %s", t.ServiceID, t.GetID())
return
}
if !nc.nwkAllocator.IsServiceAllocated(s) {
err = fmt.Errorf("service %s to which this task %s belongs has pending allocations", s.ID, t.ID)
err = fmt.Errorf("service %s to which task %s belongs has pending allocations", s.ID, t.ID)
return
}
@ -1150,7 +1166,10 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
if a.taskAllocateVote(networkVoter, t.ID) {
if t.Status.State < api.TaskStatePending {
updateTaskStatus(t, api.TaskStatePending, allocatedStatusMessage)
logger.Debugf("allocated task %v, state update %v", t.GetID(), api.TaskStatePending)
taskUpdated = true
} else {
logger.Debugf("task %v, already in allocated state %v", t.GetID(), t.Status.State)
}
}
@ -1162,7 +1181,7 @@ func (a *Allocator) allocateTask(ctx context.Context, t *api.Task) (err error) {
}
func (a *Allocator) commitAllocatedTask(ctx context.Context, batch *store.Batch, t *api.Task) error {
return batch.Update(func(tx store.Tx) error {
retError := batch.Update(func(tx store.Tx) error {
err := store.UpdateTask(tx, t)
if err == store.ErrSequenceConflict {
@ -1177,6 +1196,12 @@ func (a *Allocator) commitAllocatedTask(ctx context.Context, batch *store.Batch,
return errors.Wrapf(err, "failed updating state in store transaction for task %s", t.ID)
})
if retError == nil {
log.G(ctx).Debugf("committed allocated task %v, state update %v", t.GetID(), t.Status)
}
return retError
}
func (a *Allocator) procUnallocatedNetworks(ctx context.Context) {
@ -1268,6 +1293,7 @@ func (a *Allocator) procTasksNetwork(ctx context.Context, onRetry bool) {
allocatedTasks := make([]*api.Task, 0, len(toAllocate))
for _, t := range toAllocate {
if err := a.allocateTask(ctx, t); err == nil {
allocatedTasks = append(allocatedTasks, t)
} else if err != errNoChanges {
@ -1287,7 +1313,7 @@ func (a *Allocator) procTasksNetwork(ctx context.Context, onRetry bool) {
for _, t := range allocatedTasks {
err := a.commitAllocatedTask(ctx, batch, t)
if err != nil {
log.G(ctx).WithError(err).Error("task allocation commit failure")
log.G(ctx).WithField("method", "(*Allocator).procTasksNetwork").WithError(err).Errorf("allocation commit failure for task %s", t.GetID())
continue
}
delete(toAllocate, t.ID)

View file

@ -243,7 +243,7 @@ func (r *RaftDEKManager) MaybeUpdateKEK(candidateKEK ca.KEKData) (bool, bool, er
func decodePEMHeaderValue(headerValue string, kek []byte) ([]byte, error) {
var decrypter encryption.Decrypter = encryption.NoopCrypter
if kek != nil {
_, decrypter = encryption.Defaults(kek)
_, decrypter = encryption.Defaults(kek, false)
}
valueBytes, err := base64.StdEncoding.DecodeString(headerValue)
if err != nil {
@ -259,7 +259,7 @@ func decodePEMHeaderValue(headerValue string, kek []byte) ([]byte, error) {
func encodePEMHeaderValue(headerValue []byte, kek []byte) (string, error) {
var encrypter encryption.Encrypter = encryption.NoopCrypter
if kek != nil {
encrypter, _ = encryption.Defaults(kek)
encrypter, _ = encryption.Defaults(kek, false)
}
encrypted, err := encryption.Encrypt(headerValue, encrypter)
if err != nil {

View file

@ -699,6 +699,7 @@ func (d *Dispatcher) processUpdates(ctx context.Context) {
task.Status = *status
task.Status.AppliedBy = d.securityConfig.ClientTLSCreds.NodeID()
task.Status.AppliedAt = ptypes.MustTimestampProto(time.Now())
logger.Debugf("state for task %v updated to %v", task.GetID(), task.Status.State)
if err := store.UpdateTask(tx, task); err != nil {
logger.WithError(err).Error("failed to update task status")
return nil

View file

@ -8,7 +8,6 @@ import (
"strings"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/fips"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
)
@ -150,10 +149,11 @@ func Encrypt(plaintext []byte, encrypter Encrypter) ([]byte, error) {
return data, nil
}
// Defaults returns a default encrypter and decrypter
func Defaults(key []byte) (Encrypter, Decrypter) {
// Defaults returns a default encrypter and decrypter. If the FIPS parameter is set to
// true, the only algorithm supported on both the encrypter and decrypter will be fernet.
func Defaults(key []byte, fips bool) (Encrypter, Decrypter) {
f := NewFernet(key)
if fips.Enabled() {
if fips {
return f, f
}
n := NewNACLSecretbox(key)

View file

@ -38,6 +38,9 @@ type EncryptedRaftLogger struct {
StateDir string
EncryptionKey []byte
// FIPS specifies whether the encryption should be FIPS-compliant
FIPS bool
// mutex is locked for writing only when we need to replace the wal object and snapshotter
// object, not when we're writing snapshots or wals (in which case it's locked for reading)
encoderMu sync.RWMutex
@ -53,11 +56,11 @@ func (e *EncryptedRaftLogger) BootstrapFromDisk(ctx context.Context, oldEncrypti
walDir := e.walDir()
snapDir := e.snapDir()
encrypter, decrypter := encryption.Defaults(e.EncryptionKey)
encrypter, decrypter := encryption.Defaults(e.EncryptionKey, e.FIPS)
if oldEncryptionKeys != nil {
decrypters := []encryption.Decrypter{decrypter}
for _, key := range oldEncryptionKeys {
_, d := encryption.Defaults(key)
_, d := encryption.Defaults(key, e.FIPS)
decrypters = append(decrypters, d)
}
decrypter = encryption.NewMultiDecrypter(decrypters...)
@ -141,7 +144,7 @@ func (e *EncryptedRaftLogger) BootstrapFromDisk(ctx context.Context, oldEncrypti
func (e *EncryptedRaftLogger) BootstrapNew(metadata []byte) error {
e.encoderMu.Lock()
defer e.encoderMu.Unlock()
encrypter, decrypter := encryption.Defaults(e.EncryptionKey)
encrypter, decrypter := encryption.Defaults(e.EncryptionKey, e.FIPS)
walFactory := NewWALFactory(encrypter, decrypter)
for _, dirpath := range []string{filepath.Dir(e.walDir()), e.snapDir()} {
@ -184,7 +187,7 @@ func (e *EncryptedRaftLogger) RotateEncryptionKey(newKey []byte) {
panic(fmt.Errorf("EncryptedRaftLogger's WAL is not a wrappedWAL"))
}
wrapped.encrypter, wrapped.decrypter = encryption.Defaults(newKey)
wrapped.encrypter, wrapped.decrypter = encryption.Defaults(newKey, e.FIPS)
e.snapshotter = NewSnapFactory(wrapped.encrypter, wrapped.decrypter).New(e.snapDir())
}

View file

@ -14,6 +14,8 @@ import (
"sync"
"time"
"github.com/docker/swarmkit/ca/keyutils"
"github.com/boltdb/bolt"
"github.com/docker/docker/pkg/plugingetter"
metrics "github.com/docker/go-metrics"
@ -758,6 +760,10 @@ func (n *Node) loadSecurityConfig(ctx context.Context, paths *ca.SecurityConfigP
)
krw := ca.NewKeyReadWriter(paths.Node, n.unlockKey, &manager.RaftDEKData{})
// if FIPS is required, we want to make sure our key is stored in PKCS8 format
if n.config.FIPS {
krw.SetKeyFormatter(keyutils.FIPS)
}
if err := krw.Migrate(); err != nil {
return nil, nil, err
}