diff --git a/libnetwork/agent.go b/libnetwork/agent.go index b3790fd93c..cbe6f21cb1 100644 --- a/libnetwork/agent.go +++ b/libnetwork/agent.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "os" + "sort" "github.com/Sirupsen/logrus" "github.com/docker/go-events" @@ -13,14 +14,24 @@ import ( "github.com/docker/libnetwork/discoverapi" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/networkdb" + "github.com/docker/libnetwork/types" "github.com/gogo/protobuf/proto" ) +// ByTime implements sort.Interface for []*types.EncryptionKey based on +// the LamportTime field. +type ByTime []*types.EncryptionKey + +func (b ByTime) Len() int { return len(b) } +func (b ByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] } +func (b ByTime) Less(i, j int) bool { return b[i].LamportTime < b[j].LamportTime } + type agent struct { networkDB *networkdb.NetworkDB bindAddr string epTblCancel func() driverCancelFuncs map[string][]func() + keys []*types.EncryptionKey } func getBindAddr(ifaceName string) (string, error) { @@ -61,11 +72,71 @@ func resolveAddr(addrOrInterface string) (string, error) { return getBindAddr(addrOrInterface) } -func (c *controller) agentInit(bindAddrOrInterface string) error { +func (c *controller) agentHandleKeys(keys []*types.EncryptionKey) error { + // Find the new key and add it to the key ring + a := c.agent + for _, key := range keys { + same := false + for _, aKey := range a.keys { + if same = aKey.LamportTime == key.LamportTime; same { + break + } + } + if !same { + a.keys = append(a.keys, key) + if key.Subsystem == "networking:gossip" { + a.networkDB.SetKey(key.Key) + } + break + } + } + // Find the deleted key. If the deleted key was the primary key, + // a new primary key should be set before removing if from keyring. + deleted := []byte{} + for i, aKey := range a.keys { + same := false + for _, key := range keys { + if same = key.LamportTime == aKey.LamportTime; same { + break + } + } + if !same { + if aKey.Subsystem == "networking:gossip" { + deleted = aKey.Key + } + a.keys = append(a.keys[:i], a.keys[i+1:]...) + break + } + } + + sort.Sort(ByTime(a.keys)) + for _, key := range a.keys { + if key.Subsystem == "networking:gossip" { + a.networkDB.SetPrimaryKey(key.Key) + break + } + } + if len(deleted) > 0 { + a.networkDB.RemoveKey(deleted) + } + return nil +} + +func (c *controller) agentInit(bindAddrOrInterface string, keys []*types.EncryptionKey) error { if !c.isAgent() { return nil } + // sort the keys by lamport time + sort.Sort(ByTime(keys)) + + gossipkey := [][]byte{} + for _, key := range keys { + if key.Subsystem == "networking:gossip" { + gossipkey = append(gossipkey, key.Key) + } + } + bindAddr, err := resolveAddr(bindAddrOrInterface) if err != nil { return err @@ -75,6 +146,7 @@ func (c *controller) agentInit(bindAddrOrInterface string) error { nDB, err := networkdb.New(&networkdb.Config{ BindAddr: bindAddr, NodeName: hostname, + Keys: gossipkey, }) if err != nil { @@ -88,6 +160,7 @@ func (c *controller) agentInit(bindAddrOrInterface string) error { bindAddr: bindAddr, epTblCancel: cancel, driverCancelFuncs: make(map[string][]func()), + keys: keys, } go c.handleTableEvents(ch, c.handleEpTableEvent) diff --git a/libnetwork/cluster/provider.go b/libnetwork/cluster/provider.go index 3b91a41ff8..3689a1a267 100644 --- a/libnetwork/cluster/provider.go +++ b/libnetwork/cluster/provider.go @@ -1,5 +1,7 @@ package cluster +import "github.com/docker/libnetwork/types" + // Provider provides clustering config details type Provider interface { IsManager() bool @@ -7,4 +9,6 @@ type Provider interface { GetListenAddress() string GetRemoteAddress() string ListenClusterEvents() <-chan struct{} + GetNetworkKeys() []*types.EncryptionKey + SetNetworkKeys([]*types.EncryptionKey) } diff --git a/libnetwork/cmd/dnet/dnet.go b/libnetwork/cmd/dnet/dnet.go index 7c827b8209..4d15c92c44 100644 --- a/libnetwork/cmd/dnet/dnet.go +++ b/libnetwork/cmd/dnet/dnet.go @@ -314,6 +314,13 @@ func (d *dnetConnection) GetRemoteAddress() string { return d.Orchestration.Peer } +func (d *dnetConnection) GetNetworkKeys() []*types.EncryptionKey { + return nil +} + +func (d *dnetConnection) SetNetworkKeys([]*types.EncryptionKey) { +} + func (d *dnetConnection) ListenClusterEvents() <-chan struct{} { return d.configEvent } diff --git a/libnetwork/controller.go b/libnetwork/controller.go index e0c1ce7d8b..6f139ac315 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -226,6 +226,12 @@ func (c *controller) clusterAgentInit() { select { case <-clusterProvider.ListenClusterEvents(): if !c.isDistributedControl() { + keys := clusterProvider.GetNetworkKeys() + // If the agent is already setup this could be a key change notificaiton + if c.agent != nil { + c.agentHandleKeys(keys) + } + bindAddr, _, _ := net.SplitHostPort(clusterProvider.GetListenAddress()) remote := clusterProvider.GetRemoteAddress() remoteAddr, _, _ := net.SplitHostPort(remote) @@ -243,8 +249,8 @@ func (c *controller) clusterAgentInit() { } } - if bindAddr != "" && c.agent == nil { - if err := c.agentInit(bindAddr); err != nil { + if bindAddr != "" && len(keys) > 0 && c.agent == nil { + if err := c.agentInit(bindAddr, keys); err != nil { log.Errorf("Error in agentInit : %v", err) } else { c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool { diff --git a/libnetwork/networkdb/cluster.go b/libnetwork/networkdb/cluster.go index 9646e114bb..cab55bf069 100644 --- a/libnetwork/networkdb/cluster.go +++ b/libnetwork/networkdb/cluster.go @@ -1,6 +1,7 @@ package networkdb import ( + "bytes" "crypto/rand" "fmt" "math/big" @@ -33,6 +34,46 @@ func (l *logWriter) Write(p []byte) (int, error) { return len(p), nil } +// SetKey adds a new key to the key ring +func (nDB *NetworkDB) SetKey(key []byte) { + for _, dbKey := range nDB.config.Keys { + if bytes.Equal(key, dbKey) { + return + } + } + nDB.config.Keys = append(nDB.config.Keys, key) + if nDB.keyring != nil { + nDB.keyring.AddKey(key) + } +} + +// SetPrimaryKey sets the given key as the primary key. This should have +// been added apriori through SetKey +func (nDB *NetworkDB) SetPrimaryKey(key []byte) { + for _, dbKey := range nDB.config.Keys { + if bytes.Equal(key, dbKey) { + if nDB.keyring != nil { + nDB.keyring.UseKey(dbKey) + } + break + } + } +} + +// RemoveKey removes a key from the key ring. The key being removed +// can't be the primary key +func (nDB *NetworkDB) RemoveKey(key []byte) { + for i, dbKey := range nDB.config.Keys { + if bytes.Equal(key, dbKey) { + nDB.config.Keys = append(nDB.config.Keys[:i], nDB.config.Keys[i+1:]...) + if nDB.keyring != nil { + nDB.keyring.RemoveKey(dbKey) + } + break + } + } +} + func (nDB *NetworkDB) clusterInit() error { config := memberlist.DefaultLANConfig() config.Name = nDB.config.NodeName @@ -47,6 +88,15 @@ func (nDB *NetworkDB) clusterInit() error { config.Events = &eventDelegate{nDB: nDB} config.LogOutput = &logWriter{} + var err error + if len(nDB.config.Keys) > 0 { + nDB.keyring, err = memberlist.NewKeyring(nDB.config.Keys, nDB.config.Keys[0]) + if err != nil { + return err + } + config.Keyring = nDB.keyring + } + nDB.networkBroadcasts = &memberlist.TransmitLimitedQueue{ NumNodes: func() int { return len(nDB.nodes) diff --git a/libnetwork/networkdb/networkdb.go b/libnetwork/networkdb/networkdb.go index 293c17ad01..e02fe794af 100644 --- a/libnetwork/networkdb/networkdb.go +++ b/libnetwork/networkdb/networkdb.go @@ -77,6 +77,9 @@ type NetworkDB struct { // List of all tickers which needed to be stopped when // cleaning up. tickers []*time.Ticker + + // Reference to the memberlist's keyring to add & remove keys + keyring *memberlist.Keyring } // network describes the node/network attachment. @@ -111,6 +114,10 @@ type Config struct { // BindPort is the local node's port to which we bind to for // cluster communication. BindPort int + + // Keys to be added to the Keyring of the memberlist. Key at index + // 0 is the primary key + Keys [][]byte } // entry defines a table entry diff --git a/libnetwork/types/types.go b/libnetwork/types/types.go index c249d4b5bb..fb686de0b9 100644 --- a/libnetwork/types/types.go +++ b/libnetwork/types/types.go @@ -16,6 +16,15 @@ const ( IPv6 ) +// EncryptionKey is the libnetwork representation of the key distributed by the lead +// manager. +type EncryptionKey struct { + Subsystem string + Algorithm int32 + Key []byte + LamportTime uint64 +} + // UUID represents a globally unique ID of various resources like network and endpoint type UUID string