Browse Source

Merge pull request #1217 from sanimej/cpsec

Add support for encrypting gossip traffic
Jana Radhakrishnan 9 years ago
parent
commit
409431edd3

+ 74 - 1
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)

+ 4 - 0
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)
 }

+ 7 - 0
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
 }

+ 8 - 2
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 {

+ 50 - 0
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)

+ 7 - 0
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

+ 9 - 0
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