diff --git a/command/agent/agent.go b/command/agent/agent.go
index f43bcac69..827ccd2c4 100644
--- a/command/agent/agent.go
+++ b/command/agent/agent.go
@@ -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
}
diff --git a/command/agent/config.go b/command/agent/config.go
index 16069919d..05fa770e7 100644
--- a/command/agent/config.go
+++ b/command/agent/config.go
@@ -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
}
diff --git a/command/agent/config_test.go b/command/agent/config_test.go
index d0413524b..2d64264f0 100644
--- a/command/agent/config_test.go
+++ b/command/agent/config_test.go
@@ -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)))
diff --git a/vendor/github.com/hashicorp/memberlist/config.go b/vendor/github.com/hashicorp/memberlist/config.go
index 2f43d14cb..5cad4ed54 100644
--- a/vendor/github.com/hashicorp/memberlist/config.go
+++ b/vendor/github.com/hashicorp/memberlist/config.go
@@ -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.
@@ -233,9 +243,11 @@ func DefaultLANConfig() *Config {
DisableTcpPings: false, // TCP pings are safe, even with mixed versions
AwarenessMaxMultiplier: 8, // Probe interval backs off to 8 seconds
- GossipNodes: 3, // Gossip to 3 nodes
- GossipInterval: 200 * time.Millisecond, // Gossip more rapidly
- GossipToTheDeadTime: 30 * time.Second, // Same as push/pull
+ 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
diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go
index 2aba22322..e4b0d7347 100644
--- a/vendor/github.com/hashicorp/memberlist/memberlist.go
+++ b/vendor/github.com/hashicorp/memberlist/memberlist.go
@@ -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
diff --git a/vendor/github.com/hashicorp/memberlist/net.go b/vendor/github.com/hashicorp/memberlist/net.go
index e0036d01d..65a60159d 100644
--- a/vendor/github.com/hashicorp/memberlist/net.go
+++ b/vendor/github.com/hashicorp/memberlist/net.go
@@ -283,8 +283,13 @@ 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 {
- m.logger.Printf("[ERR] memberlist: Decrypt packet failed: %v %s", err, LogAddress(from))
- return
+ 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
@@ -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")
}
diff --git a/vendor/github.com/hashicorp/memberlist/state.go b/vendor/github.com/hashicorp/memberlist/state.go
index 71bf6f34d..8513361b1 100644
--- a/vendor/github.com/hashicorp/memberlist/state.go
+++ b/vendor/github.com/hashicorp/memberlist/state.go
@@ -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
diff --git a/vendor/github.com/hashicorp/memberlist/tag.sh b/vendor/github.com/hashicorp/memberlist/tag.sh
new file mode 100755
index 000000000..cd16623a7
--- /dev/null
+++ b/vendor/github.com/hashicorp/memberlist/tag.sh
@@ -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
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 770afdf7a..8f2c0ce3a 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -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=",
diff --git a/website/source/docs/agent/encryption.html.md b/website/source/docs/agent/encryption.html.md
index 6f182ee32..83e2db0a0 100644
--- a/website/source/docs/agent/encryption.html.md
+++ b/website/source/docs/agent/encryption.html.md
@@ -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,
diff --git a/website/source/docs/agent/options.html.md b/website/source/docs/agent/options.html.md
index 2961c2bd5..48416f9d8 100644
--- a/website/source/docs/agent/options.html.md
+++ b/website/source/docs/agent/options.html.md
@@ -708,6 +708,18 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* `encrypt` Equivalent to the
[`-encrypt` command-line flag](#_encrypt).
+* `encrypt_verify_incoming` -
+ 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.
+
+* `encrypt_verify_outgoing` -
+ 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.
+
* `key_file` 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).