Merge pull request #1217 from sanimej/cpsec

Add support for encrypting gossip traffic
This commit is contained in:
Jana Radhakrishnan 2016-06-06 15:56:37 -07:00
commit 409431edd3
7 changed files with 159 additions and 3 deletions

View File

@ -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)

View File

@ -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)
}

View File

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

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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