123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- package keymanager
- // keymanager does the allocation, rotation and distribution of symmetric
- // keys to the agents. This is to securely bootstrap network communication
- // between agents. It can be used for encrypting gossip between the agents
- // which is used to exchange service discovery and overlay network control
- // plane information. It can also be used to encrypt overlay data traffic.
- import (
- cryptorand "crypto/rand"
- "encoding/binary"
- "sync"
- "time"
- "github.com/docker/swarmkit/api"
- "github.com/docker/swarmkit/log"
- "github.com/docker/swarmkit/manager/state/store"
- "github.com/pkg/errors"
- "golang.org/x/net/context"
- )
- const (
- // DefaultKeyLen is the default length (in bytes) of the key allocated
- DefaultKeyLen = 16
- // DefaultKeyRotationInterval used by key manager
- DefaultKeyRotationInterval = 12 * time.Hour
- // SubsystemGossip handles gossip protocol between the agents
- SubsystemGossip = "networking:gossip"
- // SubsystemIPSec is overlay network data encryption subsystem
- SubsystemIPSec = "networking:ipsec"
- // DefaultSubsystem is gossip
- DefaultSubsystem = SubsystemGossip
- // number of keys to mainrain in the key ring.
- keyringSize = 3
- )
- // map of subsystems and corresponding encryption algorithm. Initially only
- // AES_128 in GCM mode is supported.
- var subsysToAlgo = map[string]api.EncryptionKey_Algorithm{
- SubsystemGossip: api.AES_128_GCM,
- SubsystemIPSec: api.AES_128_GCM,
- }
- type keyRing struct {
- lClock uint64
- keys []*api.EncryptionKey
- }
- // Config for the keymanager that can be modified
- type Config struct {
- ClusterName string
- Keylen int
- RotationInterval time.Duration
- Subsystems []string
- }
- // KeyManager handles key allocation, rotation & distribution
- type KeyManager struct {
- config *Config
- store *store.MemoryStore
- keyRing *keyRing
- ctx context.Context
- cancel context.CancelFunc
- mu sync.Mutex
- }
- // DefaultConfig provides the default config for keymanager
- func DefaultConfig() *Config {
- return &Config{
- ClusterName: store.DefaultClusterName,
- Keylen: DefaultKeyLen,
- RotationInterval: DefaultKeyRotationInterval,
- Subsystems: []string{SubsystemGossip, SubsystemIPSec},
- }
- }
- // New creates an instance of keymanager with the given config
- func New(store *store.MemoryStore, config *Config) *KeyManager {
- for _, subsys := range config.Subsystems {
- if subsys != SubsystemGossip && subsys != SubsystemIPSec {
- return nil
- }
- }
- return &KeyManager{
- config: config,
- store: store,
- keyRing: &keyRing{lClock: genSkew()},
- }
- }
- func (k *KeyManager) allocateKey(ctx context.Context, subsys string) *api.EncryptionKey {
- key := make([]byte, k.config.Keylen)
- _, err := cryptorand.Read(key)
- if err != nil {
- panic(errors.Wrap(err, "key generated failed"))
- }
- k.keyRing.lClock++
- return &api.EncryptionKey{
- Subsystem: subsys,
- Algorithm: subsysToAlgo[subsys],
- Key: key,
- LamportTime: k.keyRing.lClock,
- }
- }
- func (k *KeyManager) updateKey(cluster *api.Cluster) error {
- return k.store.Update(func(tx store.Tx) error {
- cluster = store.GetCluster(tx, cluster.ID)
- if cluster == nil {
- return nil
- }
- cluster.EncryptionKeyLamportClock = k.keyRing.lClock
- cluster.NetworkBootstrapKeys = k.keyRing.keys
- return store.UpdateCluster(tx, cluster)
- })
- }
- func (k *KeyManager) rotateKey(ctx context.Context) error {
- var (
- clusters []*api.Cluster
- err error
- )
- k.store.View(func(readTx store.ReadTx) {
- clusters, err = store.FindClusters(readTx, store.ByName(k.config.ClusterName))
- })
- if err != nil {
- log.G(ctx).Errorf("reading cluster config failed, %v", err)
- return err
- }
- cluster := clusters[0]
- if len(cluster.NetworkBootstrapKeys) == 0 {
- panic(errors.New("no key in the cluster config"))
- }
- subsysKeys := map[string][]*api.EncryptionKey{}
- for _, key := range k.keyRing.keys {
- subsysKeys[key.Subsystem] = append(subsysKeys[key.Subsystem], key)
- }
- k.keyRing.keys = []*api.EncryptionKey{}
- // We maintain the latest key and the one before in the key ring to allow
- // agents to communicate without disruption on key change.
- for subsys, keys := range subsysKeys {
- if len(keys) == keyringSize {
- min := 0
- for i, key := range keys[1:] {
- if key.LamportTime < keys[min].LamportTime {
- min = i
- }
- }
- keys = append(keys[0:min], keys[min+1:]...)
- }
- keys = append(keys, k.allocateKey(ctx, subsys))
- subsysKeys[subsys] = keys
- }
- for _, keys := range subsysKeys {
- k.keyRing.keys = append(k.keyRing.keys, keys...)
- }
- return k.updateKey(cluster)
- }
- // Run starts the keymanager, it doesn't return
- func (k *KeyManager) Run(ctx context.Context) error {
- k.mu.Lock()
- ctx = log.WithModule(ctx, "keymanager")
- var (
- clusters []*api.Cluster
- err error
- )
- k.store.View(func(readTx store.ReadTx) {
- clusters, err = store.FindClusters(readTx, store.ByName(k.config.ClusterName))
- })
- if err != nil {
- log.G(ctx).Errorf("reading cluster config failed, %v", err)
- k.mu.Unlock()
- return err
- }
- cluster := clusters[0]
- if len(cluster.NetworkBootstrapKeys) == 0 {
- for _, subsys := range k.config.Subsystems {
- for i := 0; i < keyringSize; i++ {
- k.keyRing.keys = append(k.keyRing.keys, k.allocateKey(ctx, subsys))
- }
- }
- if err := k.updateKey(cluster); err != nil {
- log.G(ctx).Errorf("store update failed %v", err)
- }
- } else {
- k.keyRing.lClock = cluster.EncryptionKeyLamportClock
- k.keyRing.keys = cluster.NetworkBootstrapKeys
- }
- ticker := time.NewTicker(k.config.RotationInterval)
- defer ticker.Stop()
- k.ctx, k.cancel = context.WithCancel(ctx)
- k.mu.Unlock()
- for {
- select {
- case <-ticker.C:
- k.rotateKey(ctx)
- case <-k.ctx.Done():
- return nil
- }
- }
- }
- // Stop stops the running instance of key manager
- func (k *KeyManager) Stop() error {
- k.mu.Lock()
- defer k.mu.Unlock()
- if k.cancel == nil {
- return errors.New("keymanager is not started")
- }
- k.cancel()
- return nil
- }
- // genSkew generates a random uint64 number between 0 and 65535
- func genSkew() uint64 {
- b := make([]byte, 2)
- if _, err := cryptorand.Read(b); err != nil {
- panic(err)
- }
- return uint64(binary.BigEndian.Uint16(b))
- }
|