Add settings for upshifting to encrypted gossip (#3079)

This commit is contained in:
Kyle Havlovitz 2017-05-30 08:51:37 -07:00 committed by GitHub
parent 14d5a0dfeb
commit e196576c07
11 changed files with 116 additions and 13 deletions

View File

@ -376,6 +376,14 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
if a.config.ReconnectTimeoutWan != 0 {
base.SerfWANConfig.ReconnectTimeout = a.config.ReconnectTimeoutWan
}
if a.config.EncryptVerifyIncoming != nil {
base.SerfWANConfig.MemberlistConfig.GossipVerifyIncoming = *a.config.EncryptVerifyIncoming
base.SerfLANConfig.MemberlistConfig.GossipVerifyIncoming = *a.config.EncryptVerifyIncoming
}
if a.config.EncryptVerifyOutgoing != nil {
base.SerfWANConfig.MemberlistConfig.GossipVerifyOutgoing = *a.config.EncryptVerifyOutgoing
base.SerfLANConfig.MemberlistConfig.GossipVerifyOutgoing = *a.config.EncryptVerifyOutgoing
}
if a.config.AdvertiseAddrs.RPC != nil {
base.RPCAdvertise = a.config.AdvertiseAddrs.RPC
}

View File

@ -365,6 +365,12 @@ type Config struct {
// Encryption key to use for the Serf communication
EncryptKey string `mapstructure:"encrypt" json:"-"`
// EncryptVerifyIncoming and EncryptVerifyOutgoing are used to enforce
// incoming/outgoing gossip encryption and can be used to upshift to
// encrypted gossip on a running cluster.
EncryptVerifyIncoming *bool `mapstructure:"encrypt_verify_incoming"`
EncryptVerifyOutgoing *bool `mapstructure:"encrypt_verify_outgoing"`
// LogLevel is the level of the logs to putout
LogLevel string `mapstructure:"log_level"`
@ -864,6 +870,9 @@ func DefaultConfig() *Config {
RetryIntervalWan: 30 * time.Second,
TLSMinVersion: "tls10",
EncryptVerifyIncoming: Bool(true),
EncryptVerifyOutgoing: Bool(true),
}
}
@ -1477,6 +1486,12 @@ func MergeConfig(a, b *Config) *Config {
if b.EncryptKey != "" {
result.EncryptKey = b.EncryptKey
}
if b.EncryptVerifyIncoming != nil {
result.EncryptVerifyIncoming = b.EncryptVerifyIncoming
}
if b.EncryptVerifyOutgoing != nil {
result.EncryptVerifyOutgoing = b.EncryptVerifyOutgoing
}
if b.LogLevel != "" {
result.LogLevel = b.LogLevel
}

View File

@ -123,6 +123,18 @@ func TestDecodeConfig(t *testing.T) {
t.Fatalf("bad: %#v", config)
}
input = `{"encrypt_verify_incoming":true, "encrypt_verify_outgoing":true}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
t.Fatalf("err: %s", err)
}
if config.EncryptVerifyIncoming == nil || !*config.EncryptVerifyIncoming {
t.Fatalf("bad: %#v", config)
}
if config.EncryptVerifyOutgoing == nil || !*config.EncryptVerifyOutgoing {
t.Fatalf("bad: %#v", config)
}
// DNS setup
input = `{"ports": {"dns": 8500}, "recursors": ["8.8.8.8","8.8.4.4"], "recursor":"127.0.0.1", "domain": "foobar"}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))

View File

@ -141,6 +141,16 @@ type Config struct {
GossipNodes int
GossipToTheDeadTime time.Duration
// GossipVerifyIncoming controls whether to enforce encryption for incoming
// gossip. It is used for upshifting from unencrypted to encrypted gossip on
// a running cluster.
GossipVerifyIncoming bool
// GossipVerifyOutgoing controls whether to enforce encryption for outgoing
// gossip. It is used for upshifting from unencrypted to encrypted gossip on
// a running cluster.
GossipVerifyOutgoing bool
// EnableCompression is used to control message compression. This can
// be used to reduce bandwidth usage at the cost of slightly more CPU
// utilization. This is only available starting at protocol version 1.
@ -236,6 +246,8 @@ func DefaultLANConfig() *Config {
GossipNodes: 3, // Gossip to 3 nodes
GossipInterval: 200 * time.Millisecond, // Gossip more rapidly
GossipToTheDeadTime: 30 * time.Second, // Same as push/pull
GossipVerifyIncoming: true,
GossipVerifyOutgoing: true,
EnableCompression: true, // Enable compression by default

View File

@ -334,7 +334,7 @@ func (m *Memberlist) setAlive() error {
addr, port, err := m.transport.FinalAdvertiseAddr(
m.config.AdvertiseAddr, m.config.AdvertisePort)
if err != nil {
return fmt.Errorf("Failed to get final advertise address: %v")
return fmt.Errorf("Failed to get final advertise address: %v", err)
}
// Check if this is a public address without encryption

View File

@ -283,9 +283,14 @@ func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time
// Decrypt the payload
plain, err := decryptPayload(m.config.Keyring.GetKeys(), buf, nil)
if err != nil {
if !m.config.GossipVerifyIncoming {
// Treat the message as plaintext
plain = buf
} else {
m.logger.Printf("[ERR] memberlist: Decrypt packet failed: %v %s", err, LogAddress(from))
return
}
}
// Continue processing the plaintext buffer
buf = plain
@ -557,7 +562,7 @@ func (m *Memberlist) encodeAndSendMsg(addr string, msgType messageType, msg inte
func (m *Memberlist) sendMsg(addr string, msg []byte) error {
// Check if we can piggy back any messages
bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead
if m.config.EncryptionEnabled() {
if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
bytesAvail -= encryptOverhead(m.encryptionVersion())
}
extra := m.getBroadcasts(compoundOverhead, bytesAvail)
@ -621,7 +626,7 @@ func (m *Memberlist) rawSendMsgPacket(addr string, node *Node, msg []byte) error
}
// Check if we have encryption enabled
if m.config.EncryptionEnabled() {
if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
// Encrypt the payload
var buf bytes.Buffer
primaryKey := m.config.Keyring.GetPrimaryKey()
@ -652,7 +657,7 @@ func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error {
}
// Check if encryption is enabled
if m.config.EncryptionEnabled() {
if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
crypt, err := m.encryptLocalState(sendBuf)
if err != nil {
m.logger.Printf("[ERROR] memberlist: Failed to encrypt local state: %v", err)
@ -876,7 +881,7 @@ func (m *Memberlist) readStream(conn net.Conn) (messageType, io.Reader, *codec.D
// Reset message type and bufConn
msgType = messageType(plain[0])
bufConn = bytes.NewReader(plain[1:])
} else if m.config.EncryptionEnabled() {
} else if m.config.EncryptionEnabled() && m.config.GossipVerifyIncoming {
return 0, nil, nil,
fmt.Errorf("Encryption is configured but remote state is not encrypted")
}

View File

@ -40,6 +40,11 @@ func (n *Node) Address() string {
return joinHostPort(n.Addr.String(), n.Port)
}
// String returns the node name
func (n *Node) String() string {
return n.Name
}
// NodeState is used to manage our state view of another node
type nodeState struct {
Node

16
vendor/github.com/hashicorp/memberlist/tag.sh generated vendored Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
# The version must be supplied from the environment. Do not include the
# leading "v".
if [ -z $VERSION ]; then
echo "Please specify a version."
exit 1
fi
# Generate the tag.
echo "==> Tagging version $VERSION..."
git commit --allow-empty -a --gpg-sign=348FFC4C -m "Release v$VERSION"
git tag -a -m "Version $VERSION" -s -u 348FFC4C "v${VERSION}" master
exit 0

6
vendor/vendor.json vendored
View File

@ -642,10 +642,10 @@
"revisionTime": "2015-06-09T07:04:31Z"
},
{
"checksumSHA1": "JJsKjmgNTUTaEHEEAQgb9jCGGiM=",
"checksumSHA1": "AoIvQFHycqypYK57ZjiWzlQmdwk=",
"path": "github.com/hashicorp/memberlist",
"revision": "6cc6075ba9fba1915fa0416f00d2b4efa9dc2262",
"revisionTime": "2017-03-17T22:24:04Z"
"revision": "16fe34d996eba2b68f6f46f26c51c617c6bc1bf0",
"revisionTime": "2017-05-26T19:17:51Z"
},
{
"checksumSHA1": "qnlqWJYV81ENr61SZk9c65R1mDo=",

View File

@ -52,6 +52,24 @@ $ consul agent -data-dir=/tmp/consul -config-file=encrypt.json
All nodes within a Consul cluster must share the same encryption key in
order to send and receive cluster information.
## Configuring Gossip Encryption on an existing cluster
As of version 0.8.4, Consul supports upshifting to encrypted gossip on a running cluster
through the following process.
1. Generate an encryption key using [`consul keygen`](/docs/commands/keygen.html)
2. Set the [`encrypt`](/docs/agent/options.html#_encrypt) key in the agent configuration and set
[`encrypt_verify_incoming`](/docs/agent/options.html#encrypt_verify_incoming) and
[`encrypt_verify_outgoing`](/docs/agent/options.html#encrypt_verify_outgoing) to `false`, doing a
rolling update of the cluster with these new values. After this step, the agents will be able to
decrypt gossip but will not yet be sending encrypted traffic.
3. Remove the [`encrypt_verify_outgoing`](/docs/agent/options.html#encrypt_verify_outgoing) setting
to change it back to false (the default) and perform another rolling update of the cluster. The
agents will now be sending encrypted gossip but will still allow incoming unencrypted traffic.
4. Remove the [`encrypt_verify_incoming`](/docs/agent/options.html#encrypt_verify_incoming) setting
to change it back to false (the default) and perform a final rolling update of the cluster. All the
agents will now be strictly enforcing encrypted gossip.
## RPC Encryption with TLS
Consul supports using TLS to verify the authenticity of servers and clients. To enable this,

View File

@ -708,6 +708,18 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* <a name="encrypt"></a><a href="#encrypt">`encrypt`</a> Equivalent to the
[`-encrypt` command-line flag](#_encrypt).
* <a name="encrypt_verify_incoming"></a><a href="#encrypt_verify_incoming">`encrypt_verify_incoming`</a> -
This is an optional parameter that can be used to disable enforcing encryption for incoming gossip in order
to upshift from unencrypted to encrypted gossip on a running cluster. See [this section]
(/docs/agent/encryption.html#configuring-gossip-encryption-on-an-existing-cluster) for more information.
Defaults to true.
* <a name="encrypt_verify_outgoing"></a><a href="#encrypt_verify_outgoing">`encrypt_verify_outgoing`</a> -
This is an optional parameter that can be used to disable enforcing encryption for outgoing gossip in order
to upshift from unencrypted to encrypted gossip on a running cluster. See [this section]
(/docs/agent/encryption.html#configuring-gossip-encryption-on-an-existing-cluster) for more information.
Defaults to true.
* <a name="key_file"></a><a href="#key_file">`key_file`</a> This provides a the file path to a
PEM-encoded private key. The key is used with the certificate to verify the agent's authenticity.
This must be provided along with [`cert_file`](#cert_file).