mirror of https://github.com/status-im/consul.git
commit
19ce2e920d
|
@ -23,7 +23,6 @@ _testmain.go
|
|||
*.test
|
||||
bin/
|
||||
.vagrant/
|
||||
|
||||
|
||||
website/npm-debug.log
|
||||
|
||||
*.old
|
||||
*.attr
|
||||
|
|
|
@ -169,6 +169,13 @@ func (a *Agent) consulConfig() *consul.Config {
|
|||
base.ProtocolVersion = uint8(a.config.Protocol)
|
||||
}
|
||||
|
||||
// Copy the TLS configuration
|
||||
base.VerifyIncoming = a.config.VerifyIncoming
|
||||
base.VerifyOutgoing = a.config.VerifyOutgoing
|
||||
base.CAFile = a.config.CAFile
|
||||
base.CertFile = a.config.CertFile
|
||||
base.KeyFile = a.config.KeyFile
|
||||
|
||||
// Setup the ServerUp callback
|
||||
base.ServerUp = a.state.ConsulServerUp
|
||||
|
||||
|
|
|
@ -103,6 +103,28 @@ type Config struct {
|
|||
// EnableDebug is used to enable various debugging features
|
||||
EnableDebug bool `mapstructure:"enable_debug"`
|
||||
|
||||
// VerifyIncoming is used to verify the authenticity of incoming connections.
|
||||
// This means that TCP requests are forbidden, only allowing for TLS. TLS connections
|
||||
// must match a provided certificate authority. This can be used to force client auth.
|
||||
VerifyIncoming bool `mapstructure:"verify_incoming"`
|
||||
|
||||
// VerifyOutgoing is used to verify the authenticity of outgoing connections.
|
||||
// This means that TLS requests are used. TLS connections must match a provided
|
||||
// certificate authority. This is used to verify authenticity of server nodes.
|
||||
VerifyOutgoing bool `mapstructure:"verify_outgoing"`
|
||||
|
||||
// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
|
||||
// or VerifyOutgoing to verify the TLS connection.
|
||||
CAFile string `mapstructure:"ca_file"`
|
||||
|
||||
// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
|
||||
// Must be provided to serve TLS connections.
|
||||
CertFile string `mapstructure:"cert_file"`
|
||||
|
||||
// KeyFile is used to provide a TLS key that is used for serving TLS connections.
|
||||
// Must be provided to serve TLS connections.
|
||||
KeyFile string `mapstructure:"key_file"`
|
||||
|
||||
// Checks holds the provided check definitions
|
||||
Checks []*CheckDefinition `mapstructure:"-"`
|
||||
|
||||
|
@ -335,6 +357,21 @@ func MergeConfig(a, b *Config) *Config {
|
|||
if b.EnableDebug {
|
||||
result.EnableDebug = true
|
||||
}
|
||||
if b.VerifyIncoming {
|
||||
result.VerifyIncoming = true
|
||||
}
|
||||
if b.VerifyOutgoing {
|
||||
result.VerifyOutgoing = true
|
||||
}
|
||||
if b.CAFile != "" {
|
||||
result.CAFile = b.CAFile
|
||||
}
|
||||
if b.CertFile != "" {
|
||||
result.CertFile = b.CertFile
|
||||
}
|
||||
if b.KeyFile != "" {
|
||||
result.KeyFile = b.KeyFile
|
||||
}
|
||||
if b.Checks != nil {
|
||||
result.Checks = append(result.Checks, b.Checks...)
|
||||
}
|
||||
|
|
|
@ -193,6 +193,38 @@ func TestDecodeConfig(t *testing.T) {
|
|||
if config.EnableDebug != true {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
|
||||
// TLS
|
||||
input = `{"verify_incoming": true, "verify_outgoing": true}`
|
||||
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if config.VerifyIncoming != true {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
|
||||
if config.VerifyOutgoing != true {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
|
||||
// TLS keys
|
||||
input = `{"ca_file": "my/ca/file", "cert_file": "my.cert", "key_file": "key.pem"}`
|
||||
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if config.CAFile != "my/ca/file" {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
if config.CertFile != "my.cert" {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
if config.KeyFile != "key.pem" {
|
||||
t.Fatalf("bad: %#v", config)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeConfig_Service(t *testing.T) {
|
||||
|
@ -318,6 +350,11 @@ func TestMergeConfig(t *testing.T) {
|
|||
LeaveOnTerm: true,
|
||||
SkipLeaveOnInt: true,
|
||||
EnableDebug: true,
|
||||
VerifyIncoming: true,
|
||||
VerifyOutgoing: true,
|
||||
CAFile: "test/ca.pem",
|
||||
CertFile: "test/cert.pem",
|
||||
KeyFile: "test/key.pem",
|
||||
Checks: []*CheckDefinition{nil},
|
||||
Services: []*ServiceDefinition{nil},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/hashicorp/consul/consul/structs"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
|
@ -78,13 +79,22 @@ func NewClient(config *Config) (*Client, error) {
|
|||
config.LogOutput = os.Stderr
|
||||
}
|
||||
|
||||
// Create the tlsConfig
|
||||
var tlsConfig *tls.Config
|
||||
var err error
|
||||
if config.VerifyOutgoing {
|
||||
if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Create a logger
|
||||
logger := log.New(config.LogOutput, "", log.LstdFlags)
|
||||
|
||||
// Create server
|
||||
c := &Client{
|
||||
config: config,
|
||||
connPool: NewPool(clientRPCCache),
|
||||
connPool: NewPool(clientRPCCache, tlsConfig),
|
||||
eventCh: make(chan serf.Event, 256),
|
||||
logger: logger,
|
||||
shutdownCh: make(chan struct{}),
|
||||
|
@ -94,7 +104,6 @@ func NewClient(config *Config) (*Client, error) {
|
|||
go c.lanEventHandler()
|
||||
|
||||
// Initialize the lan Serf
|
||||
var err error
|
||||
c.serf, err = c.setupSerf(config.SerfLANConfig,
|
||||
c.eventCh, serfLANSnapshot)
|
||||
if err != nil {
|
||||
|
|
|
@ -9,14 +9,10 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
func testClient(t *testing.T) (string, *Client) {
|
||||
return testClientDC(t, "dc1")
|
||||
}
|
||||
|
||||
func testClientDC(t *testing.T, dc string) (string, *Client) {
|
||||
func testClientConfig(t *testing.T) (string, *Config) {
|
||||
dir := tmpDir(t)
|
||||
config := DefaultConfig()
|
||||
config.Datacenter = dc
|
||||
config.Datacenter = "dc1"
|
||||
config.DataDir = dir
|
||||
|
||||
// Adjust the ports
|
||||
|
@ -32,6 +28,17 @@ func testClientDC(t *testing.T, dc string) (string, *Client) {
|
|||
config.SerfLANConfig.MemberlistConfig.ProbeInterval = time.Second
|
||||
config.SerfLANConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond
|
||||
|
||||
return dir, config
|
||||
}
|
||||
|
||||
func testClient(t *testing.T) (string, *Client) {
|
||||
return testClientDC(t, "dc1")
|
||||
}
|
||||
|
||||
func testClientDC(t *testing.T, dc string) (string, *Client) {
|
||||
dir, config := testClientConfig(t)
|
||||
config.Datacenter = dc
|
||||
|
||||
client, err := NewClient(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
@ -119,3 +126,55 @@ func TestClient_RPC(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_RPC_TLS(t *testing.T) {
|
||||
dir1, conf1 := testServerConfig(t)
|
||||
conf1.VerifyIncoming = true
|
||||
conf1.VerifyOutgoing = true
|
||||
configureTLS(conf1)
|
||||
s1, err := NewServer(conf1)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
|
||||
dir2, conf2 := testClientConfig(t)
|
||||
conf2.VerifyOutgoing = true
|
||||
configureTLS(conf2)
|
||||
c1, err := NewClient(conf2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir2)
|
||||
defer c1.Shutdown()
|
||||
|
||||
// Try an RPC
|
||||
var out struct{}
|
||||
if err := c1.RPC("Status.Ping", struct{}{}, &out); err != structs.ErrNoServers {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Try to join
|
||||
addr := fmt.Sprintf("127.0.0.1:%d",
|
||||
s1.config.SerfLANConfig.MemberlistConfig.BindPort)
|
||||
if _, err := c1.JoinLAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check the members
|
||||
if len(s1.LANMembers()) != 2 {
|
||||
t.Fatalf("bad len")
|
||||
}
|
||||
|
||||
if len(c1.LANMembers()) != 2 {
|
||||
t.Fatalf("bad len")
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// RPC shoudl succeed
|
||||
if err := c1.RPC("Status.Ping", struct{}{}, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
134
consul/config.go
134
consul/config.go
|
@ -1,11 +1,15 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/hashicorp/raft"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
@ -81,6 +85,29 @@ type Config struct {
|
|||
// ProtocolVersionMin and ProtocolVersionMax.
|
||||
ProtocolVersion uint8
|
||||
|
||||
// VerifyIncoming is used to verify the authenticity of incoming connections.
|
||||
// This means that TCP requests are forbidden, only allowing for TLS. TLS connections
|
||||
// must match a provided certificate authority. This can be used to force client auth.
|
||||
VerifyIncoming bool
|
||||
|
||||
// VerifyOutgoing is used to verify the authenticity of outgoing connections.
|
||||
// This means that TLS requests are used, and TCP requests are not made. TLS connections
|
||||
// must match a provided certificate authority. This is used to verify authenticity of
|
||||
// server nodes.
|
||||
VerifyOutgoing bool
|
||||
|
||||
// CAFile is a path to a certificate authority file. This is used with VerifyIncoming
|
||||
// or VerifyOutgoing to verify the TLS connection.
|
||||
CAFile string
|
||||
|
||||
// CertFile is used to provide a TLS certificate that is used for serving TLS connections.
|
||||
// Must be provided to serve TLS connections.
|
||||
CertFile string
|
||||
|
||||
// KeyFile is used to provide a TLS key that is used for serving TLS connections.
|
||||
// Must be provided to serve TLS connections.
|
||||
KeyFile string
|
||||
|
||||
// ServerUp callback can be used to trigger a notification that
|
||||
// a Consul server is now up and known about.
|
||||
ServerUp func()
|
||||
|
@ -98,6 +125,113 @@ func (c *Config) CheckVersion() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// CACertificate is used to open and parse a CA file
|
||||
func (c *Config) CACertificate() (*x509.Certificate, error) {
|
||||
if c.CAFile == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Read the file
|
||||
data, err := ioutil.ReadFile(c.CAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to read CA file: %v", err)
|
||||
}
|
||||
|
||||
// Decode from the PEM format
|
||||
block, _ := pem.Decode(data)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("Failed to decode CA PEM!")
|
||||
}
|
||||
|
||||
// Parse the certificate
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse CA file: %v", err)
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// KeyPair is used to open and parse a certificate and key file
|
||||
func (c *Config) KeyPair() (*tls.Certificate, error) {
|
||||
if c.CertFile == "" || c.KeyFile == "" {
|
||||
return nil, nil
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair(c.CertFile, c.KeyFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to load cert/key pair: %v", err)
|
||||
}
|
||||
return &cert, err
|
||||
}
|
||||
|
||||
// OutgoingTLSConfig generates a TLS configuration for outgoing requests
|
||||
func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
|
||||
// Create the tlsConfig
|
||||
tlsConfig := &tls.Config{
|
||||
RootCAs: x509.NewCertPool(),
|
||||
InsecureSkipVerify: !c.VerifyOutgoing,
|
||||
}
|
||||
|
||||
// Parse the CA cert if any
|
||||
ca, err := c.CACertificate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if ca != nil {
|
||||
tlsConfig.RootCAs.AddCert(ca)
|
||||
}
|
||||
|
||||
// Ensure we have a CA if VerifyOutgoing is set
|
||||
if c.VerifyOutgoing && ca == nil {
|
||||
return nil, fmt.Errorf("VerifyOutgoing set, and no CA certificate provided!")
|
||||
}
|
||||
|
||||
// Add cert/key
|
||||
cert, err := c.KeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if cert != nil {
|
||||
tlsConfig.Certificates = []tls.Certificate{*cert}
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// IncomingTLSConfig generates a TLS configuration for incoming requests
|
||||
func (c *Config) IncomingTLSConfig() (*tls.Config, error) {
|
||||
// Create the tlsConfig
|
||||
tlsConfig := &tls.Config{
|
||||
ClientCAs: x509.NewCertPool(),
|
||||
ClientAuth: tls.NoClientCert,
|
||||
}
|
||||
|
||||
// Parse the CA cert if any
|
||||
ca, err := c.CACertificate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if ca != nil {
|
||||
tlsConfig.ClientCAs.AddCert(ca)
|
||||
}
|
||||
|
||||
// Add cert/key
|
||||
cert, err := c.KeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if cert != nil {
|
||||
tlsConfig.Certificates = []tls.Certificate{*cert}
|
||||
}
|
||||
|
||||
// Check if we require verification
|
||||
if c.VerifyIncoming {
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
if ca == nil {
|
||||
return nil, fmt.Errorf("VerifyIncoming set, and no CA certificate provided!")
|
||||
}
|
||||
if cert == nil {
|
||||
return nil, fmt.Errorf("VerifyIncoming set, and no Cert/Key pair provided!")
|
||||
}
|
||||
}
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// DefaultConfig is used to return a sane default configuration
|
||||
func DefaultConfig() *Config {
|
||||
hostname, err := os.Hostname()
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfig_CACertificate_None(t *testing.T) {
|
||||
conf := &Config{}
|
||||
cert, err := conf.CACertificate()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if cert != nil {
|
||||
t.Fatalf("bad: %v", cert)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_CACertificate_Valid(t *testing.T) {
|
||||
conf := &Config{
|
||||
CAFile: "../test/ca/root.cer",
|
||||
}
|
||||
cert, err := conf.CACertificate()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if cert == nil {
|
||||
t.Fatalf("expected cert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_KeyPair_None(t *testing.T) {
|
||||
conf := &Config{}
|
||||
cert, err := conf.KeyPair()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if cert != nil {
|
||||
t.Fatalf("bad: %v", cert)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_KeyPair_Valid(t *testing.T) {
|
||||
conf := &Config{
|
||||
CertFile: "../test/key/ourdomain.cer",
|
||||
KeyFile: "../test/key/ourdomain.key",
|
||||
}
|
||||
cert, err := conf.KeyPair()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if cert == nil {
|
||||
t.Fatalf("expected cert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_OutgoingTLS_MissingCA(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyOutgoing: true,
|
||||
}
|
||||
tls, err := conf.OutgoingTLSConfig()
|
||||
if err == nil {
|
||||
t.Fatalf("expected err")
|
||||
}
|
||||
if tls != nil {
|
||||
t.Fatalf("bad: %v", tls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_OutgoingTLS_OnlyCA(t *testing.T) {
|
||||
conf := &Config{
|
||||
CAFile: "../test/ca/root.cer",
|
||||
}
|
||||
tls, err := conf.OutgoingTLSConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if tls == nil {
|
||||
t.Fatalf("expected config")
|
||||
}
|
||||
if len(tls.RootCAs.Subjects()) != 1 {
|
||||
t.Fatalf("expect root cert")
|
||||
}
|
||||
if !tls.InsecureSkipVerify {
|
||||
t.Fatalf("expect to skip verification")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_OutgoingTLS_VerifyOutgoing(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyOutgoing: true,
|
||||
CAFile: "../test/ca/root.cer",
|
||||
}
|
||||
tls, err := conf.OutgoingTLSConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if tls == nil {
|
||||
t.Fatalf("expected config")
|
||||
}
|
||||
if len(tls.RootCAs.Subjects()) != 1 {
|
||||
t.Fatalf("expect root cert")
|
||||
}
|
||||
if tls.InsecureSkipVerify {
|
||||
t.Fatalf("should not skip verification")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_OutgoingTLS_WithKeyPair(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyOutgoing: true,
|
||||
CAFile: "../test/ca/root.cer",
|
||||
CertFile: "../test/key/ourdomain.cer",
|
||||
KeyFile: "../test/key/ourdomain.key",
|
||||
}
|
||||
tls, err := conf.OutgoingTLSConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if tls == nil {
|
||||
t.Fatalf("expected config")
|
||||
}
|
||||
if len(tls.RootCAs.Subjects()) != 1 {
|
||||
t.Fatalf("expect root cert")
|
||||
}
|
||||
if tls.InsecureSkipVerify {
|
||||
t.Fatalf("should not skip verification")
|
||||
}
|
||||
if len(tls.Certificates) != 1 {
|
||||
t.Fatalf("expected client cert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_IncomingTLS(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyIncoming: true,
|
||||
CAFile: "../test/ca/root.cer",
|
||||
CertFile: "../test/key/ourdomain.cer",
|
||||
KeyFile: "../test/key/ourdomain.key",
|
||||
}
|
||||
tlsC, err := conf.IncomingTLSConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if tlsC == nil {
|
||||
t.Fatalf("expected config")
|
||||
}
|
||||
if len(tlsC.ClientCAs.Subjects()) != 1 {
|
||||
t.Fatalf("expect client cert")
|
||||
}
|
||||
if tlsC.ClientAuth != tls.RequireAndVerifyClientCert {
|
||||
t.Fatalf("should not skip verification")
|
||||
}
|
||||
if len(tlsC.Certificates) != 1 {
|
||||
t.Fatalf("expected client cert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_IncomingTLS_MissingCA(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyIncoming: true,
|
||||
CertFile: "../test/key/ourdomain.cer",
|
||||
KeyFile: "../test/key/ourdomain.key",
|
||||
}
|
||||
_, err := conf.IncomingTLSConfig()
|
||||
if err == nil {
|
||||
t.Fatalf("expected err")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_IncomingTLS_MissingKey(t *testing.T) {
|
||||
conf := &Config{
|
||||
VerifyIncoming: true,
|
||||
CAFile: "../test/ca/root.cer",
|
||||
}
|
||||
_, err := conf.IncomingTLSConfig()
|
||||
if err == nil {
|
||||
t.Fatalf("expected err")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_IncomingTLS_NoVerify(t *testing.T) {
|
||||
conf := &Config{}
|
||||
tlsC, err := conf.IncomingTLSConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if tlsC == nil {
|
||||
t.Fatalf("expected config")
|
||||
}
|
||||
if len(tlsC.ClientCAs.Subjects()) != 0 {
|
||||
t.Fatalf("do not expect client cert")
|
||||
}
|
||||
if tlsC.ClientAuth != tls.NoClientCert {
|
||||
t.Fatalf("should skip verification")
|
||||
}
|
||||
if len(tlsC.Certificates) != 0 {
|
||||
t.Fatalf("unexpected client cert")
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/inconshreveable/muxado"
|
||||
"github.com/ugorji/go/codec"
|
||||
|
@ -37,6 +38,9 @@ type ConnPool struct {
|
|||
// Pool maps an address to a open connection
|
||||
pool map[string]*Conn
|
||||
|
||||
// TLS settings
|
||||
tlsConfig *tls.Config
|
||||
|
||||
// Used to indicate the pool is shutdown
|
||||
shutdown bool
|
||||
shutdownCh chan struct{}
|
||||
|
@ -44,11 +48,13 @@ type ConnPool struct {
|
|||
|
||||
// NewPool is used to make a new connection pool
|
||||
// Maintain at most one connection per host, for up to maxTime.
|
||||
// Set maxTime to 0 to disable reaping.
|
||||
func NewPool(maxTime time.Duration) *ConnPool {
|
||||
// Set maxTime to 0 to disable reaping. If TLS settings are provided
|
||||
// outgoing connections use TLS.
|
||||
func NewPool(maxTime time.Duration, tlsConfig *tls.Config) *ConnPool {
|
||||
pool := &ConnPool{
|
||||
maxTime: maxTime,
|
||||
pool: make(map[string]*Conn),
|
||||
tlsConfig: tlsConfig,
|
||||
shutdownCh: make(chan struct{}),
|
||||
}
|
||||
if maxTime > 0 {
|
||||
|
@ -104,20 +110,34 @@ func (p *ConnPool) getPooled(addr net.Addr) *Conn {
|
|||
// getNewConn is used to return a new connection
|
||||
func (p *ConnPool) getNewConn(addr net.Addr) (*Conn, error) {
|
||||
// Try to dial the conn
|
||||
rawConn, err := net.DialTimeout("tcp", addr.String(), 10*time.Second)
|
||||
conn, err := net.DialTimeout("tcp", addr.String(), 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Cast to TCPConn
|
||||
conn := rawConn.(*net.TCPConn)
|
||||
if tcp, ok := conn.(*net.TCPConn); ok {
|
||||
tcp.SetKeepAlive(true)
|
||||
tcp.SetNoDelay(true)
|
||||
}
|
||||
|
||||
// Enable keep alives
|
||||
conn.SetKeepAlive(true)
|
||||
conn.SetNoDelay(true)
|
||||
// Check if TLS is enabled
|
||||
if p.tlsConfig != nil {
|
||||
// Switch the connection into TLS mode
|
||||
if _, err := conn.Write([]byte{byte(rpcTLS)}); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the connection in a TLS client
|
||||
conn = tls.Client(conn, p.tlsConfig)
|
||||
}
|
||||
|
||||
// Write the Consul multiplex byte to set the mode
|
||||
conn.Write([]byte{byte(rpcMultiplex)})
|
||||
if _, err := conn.Write([]byte{byte(rpcMultiplex)}); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a multiplexed session
|
||||
session := muxado.Client(conn)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
@ -16,6 +17,9 @@ type RaftLayer struct {
|
|||
// connCh is used to accept connections
|
||||
connCh chan net.Conn
|
||||
|
||||
// TLS configuration
|
||||
tlsConfig *tls.Config
|
||||
|
||||
// Tracks if we are closed
|
||||
closed bool
|
||||
closeCh chan struct{}
|
||||
|
@ -23,12 +27,14 @@ type RaftLayer struct {
|
|||
}
|
||||
|
||||
// NewRaftLayer is used to initialize a new RaftLayer which can
|
||||
// be used as a StreamLayer for Raft
|
||||
func NewRaftLayer(addr net.Addr) *RaftLayer {
|
||||
// be used as a StreamLayer for Raft. If a tlsConfig is provided,
|
||||
// then the connection will use TLS.
|
||||
func NewRaftLayer(addr net.Addr, tlsConfig *tls.Config) *RaftLayer {
|
||||
layer := &RaftLayer{
|
||||
addr: addr,
|
||||
connCh: make(chan net.Conn),
|
||||
closeCh: make(chan struct{}),
|
||||
addr: addr,
|
||||
connCh: make(chan net.Conn),
|
||||
tlsConfig: tlsConfig,
|
||||
closeCh: make(chan struct{}),
|
||||
}
|
||||
return layer
|
||||
}
|
||||
|
@ -79,6 +85,18 @@ func (l *RaftLayer) Dial(address string, timeout time.Duration) (net.Conn, error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Check for tls mode
|
||||
if l.tlsConfig != nil {
|
||||
// Switch the connection into TLS mode
|
||||
if _, err := conn.Write([]byte{byte(rpcTLS)}); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the connection in a TLS client
|
||||
conn = tls.Client(conn, l.tlsConfig)
|
||||
}
|
||||
|
||||
// Write the Raft byte to set the mode
|
||||
_, err = conn.Write([]byte{byte(rpcRaft)})
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/consul/consul/structs"
|
||||
|
@ -19,6 +20,7 @@ const (
|
|||
rpcConsul RPCType = iota
|
||||
rpcRaft
|
||||
rpcMultiplex
|
||||
rpcTLS
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -43,14 +45,14 @@ func (s *Server) listen() {
|
|||
s.rpcClients[conn] = struct{}{}
|
||||
s.rpcClientLock.Unlock()
|
||||
|
||||
go s.handleConn(conn)
|
||||
go s.handleConn(conn, false)
|
||||
metrics.IncrCounter([]string{"consul", "rpc", "accept_conn"}, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// handleConn is used to determine if this is a Raft or
|
||||
// Consul type RPC connection and invoke the correct handler
|
||||
func (s *Server) handleConn(conn net.Conn) {
|
||||
func (s *Server) handleConn(conn net.Conn, isTLS bool) {
|
||||
// Read a single byte
|
||||
buf := make([]byte, 1)
|
||||
if _, err := conn.Read(buf); err != nil {
|
||||
|
@ -59,6 +61,13 @@ func (s *Server) handleConn(conn net.Conn) {
|
|||
return
|
||||
}
|
||||
|
||||
// Enforce TLS if VerifyIncoming is set
|
||||
if s.config.VerifyIncoming && !isTLS && RPCType(buf[0]) != rpcTLS {
|
||||
s.logger.Printf("[WARN] consul.rpc: Non-TLS connection attempted with VerifyIncoming set")
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Switch on the byte
|
||||
switch RPCType(buf[0]) {
|
||||
case rpcConsul:
|
||||
|
@ -71,6 +80,15 @@ func (s *Server) handleConn(conn net.Conn) {
|
|||
case rpcMultiplex:
|
||||
s.handleMultiplex(conn)
|
||||
|
||||
case rpcTLS:
|
||||
if s.rpcTLS == nil {
|
||||
s.logger.Printf("[WARN] consul.rpc: TLS connection attempted, server not configured for TLS")
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
conn = tls.Server(conn, s.rpcTLS)
|
||||
s.handleConn(conn, true)
|
||||
|
||||
default:
|
||||
s.logger.Printf("[ERR] consul.rpc: unrecognized RPC byte: %v", buf[0])
|
||||
conn.Close()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package consul
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/hashicorp/raft"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
|
@ -82,6 +83,9 @@ type Server struct {
|
|||
rpcListener net.Listener
|
||||
rpcServer *rpc.Server
|
||||
|
||||
// rpcTLS is the TLS config for incoming TLS requests
|
||||
rpcTLS *tls.Config
|
||||
|
||||
// serfLAN is the Serf cluster maintained inside the DC
|
||||
// which contains all the DC nodes
|
||||
serfLAN *serf.Serf
|
||||
|
@ -122,13 +126,28 @@ func NewServer(config *Config) (*Server, error) {
|
|||
config.LogOutput = os.Stderr
|
||||
}
|
||||
|
||||
// Create the tlsConfig for outgoing connections
|
||||
var tlsConfig *tls.Config
|
||||
var err error
|
||||
if config.VerifyOutgoing {
|
||||
if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the incoming tls config
|
||||
incomingTLS, err := config.IncomingTLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a logger
|
||||
logger := log.New(config.LogOutput, "", log.LstdFlags)
|
||||
|
||||
// Create server
|
||||
s := &Server{
|
||||
config: config,
|
||||
connPool: NewPool(time.Minute),
|
||||
connPool: NewPool(time.Minute, tlsConfig),
|
||||
eventChLAN: make(chan serf.Event, 256),
|
||||
eventChWAN: make(chan serf.Event, 256),
|
||||
logger: logger,
|
||||
|
@ -136,11 +155,12 @@ func NewServer(config *Config) (*Server, error) {
|
|||
remoteConsuls: make(map[string][]net.Addr),
|
||||
rpcClients: make(map[net.Conn]struct{}),
|
||||
rpcServer: rpc.NewServer(),
|
||||
rpcTLS: incomingTLS,
|
||||
shutdownCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Initialize the RPC layer
|
||||
if err := s.setupRPC(); err != nil {
|
||||
if err := s.setupRPC(tlsConfig); err != nil {
|
||||
s.Shutdown()
|
||||
return nil, fmt.Errorf("Failed to start RPC layer: %v", err)
|
||||
}
|
||||
|
@ -156,7 +176,6 @@ func NewServer(config *Config) (*Server, error) {
|
|||
go s.wanEventHandler()
|
||||
|
||||
// Initialize the lan Serf
|
||||
var err error
|
||||
s.serfLAN, err = s.setupSerf(config.SerfLANConfig,
|
||||
s.eventChLAN, serfLANSnapshot)
|
||||
if err != nil {
|
||||
|
@ -271,7 +290,7 @@ func (s *Server) setupRaft() error {
|
|||
}
|
||||
|
||||
// setupRPC is used to setup the RPC listener
|
||||
func (s *Server) setupRPC() error {
|
||||
func (s *Server) setupRPC(tlsConfig *tls.Config) error {
|
||||
// Create endpoints
|
||||
s.endpoints.Status = &Status{s}
|
||||
s.endpoints.Raft = &Raft{s}
|
||||
|
@ -310,7 +329,7 @@ func (s *Server) setupRPC() error {
|
|||
return fmt.Errorf("RPC advertise address is not advertisable: %v", addr)
|
||||
}
|
||||
|
||||
s.raftLayer = NewRaftLayer(advertise)
|
||||
s.raftLayer = NewRaftLayer(advertise, tlsConfig)
|
||||
go s.listen()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -25,19 +25,17 @@ func tmpDir(t *testing.T) string {
|
|||
return dir
|
||||
}
|
||||
|
||||
func testServer(t *testing.T) (string, *Server) {
|
||||
return testServerDC(t, "dc1")
|
||||
func configureTLS(config *Config) {
|
||||
config.CAFile = "../test/ca/root.cer"
|
||||
config.CertFile = "../test/key/ourdomain.cer"
|
||||
config.KeyFile = "../test/key/ourdomain.key"
|
||||
}
|
||||
|
||||
func testServerDC(t *testing.T, dc string) (string, *Server) {
|
||||
return testServerDCBootstrap(t, dc, true)
|
||||
}
|
||||
|
||||
func testServerDCBootstrap(t *testing.T, dc string, bootstrap bool) (string, *Server) {
|
||||
func testServerConfig(t *testing.T) (string, *Config) {
|
||||
dir := tmpDir(t)
|
||||
config := DefaultConfig()
|
||||
config.Bootstrap = bootstrap
|
||||
config.Datacenter = dc
|
||||
config.Bootstrap = true
|
||||
config.Datacenter = "dc1"
|
||||
config.DataDir = dir
|
||||
|
||||
// Adjust the ports
|
||||
|
@ -65,7 +63,21 @@ func testServerDCBootstrap(t *testing.T, dc string, bootstrap bool) (string, *Se
|
|||
config.RaftConfig.ElectionTimeout = 40 * time.Millisecond
|
||||
|
||||
config.ReconcileInterval = 100 * time.Millisecond
|
||||
return dir, config
|
||||
}
|
||||
|
||||
func testServer(t *testing.T) (string, *Server) {
|
||||
return testServerDC(t, "dc1")
|
||||
}
|
||||
|
||||
func testServerDC(t *testing.T, dc string) (string, *Server) {
|
||||
return testServerDCBootstrap(t, dc, true)
|
||||
}
|
||||
|
||||
func testServerDCBootstrap(t *testing.T, dc string, bootstrap bool) (string, *Server) {
|
||||
dir, config := testServerConfig(t)
|
||||
config.Datacenter = dc
|
||||
config.Bootstrap = bootstrap
|
||||
server, err := NewServer(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
@ -219,3 +231,55 @@ func TestServer_RPC(t *testing.T) {
|
|||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_JoinLAN_TLS(t *testing.T) {
|
||||
dir1, conf1 := testServerConfig(t)
|
||||
conf1.VerifyIncoming = true
|
||||
conf1.VerifyOutgoing = true
|
||||
configureTLS(conf1)
|
||||
s1, err := NewServer(conf1)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir1)
|
||||
defer s1.Shutdown()
|
||||
|
||||
dir2, conf2 := testServerConfig(t)
|
||||
conf2.Bootstrap = false
|
||||
conf2.VerifyIncoming = true
|
||||
conf2.VerifyOutgoing = true
|
||||
configureTLS(conf2)
|
||||
s2, err := NewServer(conf2)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir2)
|
||||
defer s2.Shutdown()
|
||||
|
||||
// Try to join
|
||||
addr := fmt.Sprintf("127.0.0.1:%d",
|
||||
s1.config.SerfLANConfig.MemberlistConfig.BindPort)
|
||||
if _, err := s2.JoinLAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check the members
|
||||
if len(s1.LANMembers()) != 2 {
|
||||
t.Fatalf("bad len")
|
||||
}
|
||||
|
||||
if len(s2.LANMembers()) != 2 {
|
||||
t.Fatalf("bad len")
|
||||
}
|
||||
|
||||
// Wait a while
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Verify Raft has established a peer
|
||||
if s1.Stats()["raft"]["num_peers"] != "1" {
|
||||
t.Fatalf("bad: %v", s1.Stats()["raft"])
|
||||
}
|
||||
if s2.Stats()["raft"]["num_peers"] != "1" {
|
||||
t.Fatalf("bad: %v", s2.Stats()["raft"])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
V 150407190456Z 0A unknown /CN=testco.internal/ST=California/C=US/emailAddress=test@testco.com/O=TestCo/OU=Beta
|
||||
V 150407194146Z 0B unknown /CN=testco.internal/ST=California/C=US/emailAddress=test@testco.com/O=TestCo/OU=Beta
|
|
@ -0,0 +1,34 @@
|
|||
[ ca ]
|
||||
default_ca = myca
|
||||
|
||||
[ crl_ext ]
|
||||
# issuerAltName=issuer:copy #this would copy the issuer name to altname
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ myca ]
|
||||
new_certs_dir = /tmp
|
||||
unique_subject = no
|
||||
certificate = root.cer
|
||||
database = certindex
|
||||
private_key = privkey.pem
|
||||
serial = serialfile
|
||||
default_days = 365
|
||||
default_md = sha1
|
||||
policy = myca_policy
|
||||
x509_extensions = myca_extensions
|
||||
|
||||
[ myca_policy ]
|
||||
commonName = supplied
|
||||
stateOrProvinceName = supplied
|
||||
countryName = supplied
|
||||
emailAddress = optional
|
||||
organizationName = supplied
|
||||
organizationalUnitName = optional
|
||||
|
||||
[ myca_extensions ]
|
||||
basicConstraints = CA:false
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
keyUsage = digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = serverAuth,clientAuth
|
||||
crlDistributionPoints = URI:http://path.to.crl/myca.crl
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAxrs6JK4NpiOItxrpNR/1ppUUmH7p2BgLCBZ6eHdclle9J56i
|
||||
68adt8J85zaqphCfz6VDP58DsFx+N50PZyjQaDsUd0HejRqfHRMtg2O+UQkv4Z66
|
||||
+Vo+gc6uGuANi2xMtSYDVTAqqzF48OOPQDgYkzcGxcFZzTRFFZt2vPnyHj8cHcaF
|
||||
o/NMNVh7C3yTXevRGNm9u2mrbxCEeiHzFC2WUnvgU2jQuC7Fhnl33Zd3B6d3mQH6
|
||||
O23ncmwxTcPUJe6xZaIRrDuzwUcyhLj5Z3faag/fpFIIcHSiHRfoqHLGsGg+3swI
|
||||
d/zVJSSDHr7pJUu7Cre+vZa63FqDaooqvnisrQIDAQABAoIBABreo6zj76p/8XM4
|
||||
a0GokZE1ZPR9bGawUYWFbIevM9CMCmI5+7M/RoHbBQJKDOapJsJviNkoSdpllxcz
|
||||
4CpFhXAiVNEPEeUoLU1EE4pJSSkxwcySppsiTYNFi5rMomgwe2qeuiKhgZNl/AEt
|
||||
82dubjwxW3QPgXHSWGjkfTht3wOhrczA8xyEjc9Bsad2ooA9IQk+VXYlPZXyXjs1
|
||||
WwLYHmcSfveauLliLXeVU2Ux5PPwyreKMhyAfSHVQCycxK008u8WPy8nkAlpxKMC
|
||||
UwCN+JKl69WCCA3CxXgM83zz4pXvB4EyMr8aTiqmOID8RIIrPcjCmVJki6KbJ9WG
|
||||
S2CQVG0CgYEA5kVACrnjLtov426ZNifF2zUXu9x//7D6GkbJxzZLwXP/BJFcEOdQ
|
||||
Fnjcs3s7wYh/wdTnEcQVWSJSAqnRt98c9yAXVnG5z1M0DYpAsY8xrdhEitxOf2oB
|
||||
2cbvi4+cvUuUxk1hgva18UCT23aLP+iY2+t/ydBXAZ9kq1zz5CcpEBMCgYEA3O/R
|
||||
g1Y9O36XxBmSYnkoCF5yGrPunnKKNBJc/WA7pTkQFYHr64Y/h5EKubzHD/VEd1Li
|
||||
nDuGYxVMewf+5pHUhqSdpZtTxv25hjOsqLf5o5wm18JThGifs2zEVCTJOPti5n2M
|
||||
RHakxuq1I625/QHidLBTQYuEBS/vywhapfaSaD8CgYEAhd1OPK4R30PiQRIjqXL3
|
||||
t9ampISsOKXWz33FgbUT1zOq1in23rDKQzYh/4ktlPXYZ4NwjUhzrKyiBoBYtc7T
|
||||
1OpoBs34Wgmhohl0QIThOZIXTq6CR9oFl2fqDDUBxp3wsFN905e+77A+BIBmtVFv
|
||||
w7GlSVp/qibSbDiOZF1LptcCgYB8sJBi+jnmqOSIVRJLpysTxhHJxkDmhahACRkY
|
||||
Gsau0cylBsUaEJMsNIyEFOmXtQml+k5QdDu9EdkvGm0evbDfKGqce1RF2w5okiNg
|
||||
uSwXzVoSrOartMxk2/7VqkkycpX3lWWjgf4vEWmXsEVmaDjhOF5UgKPKtao0wQs/
|
||||
3S/1ywKBgAIGgOuvL/GBcGqLikHLC+cputMvBAuE/tJnFHPxFoobskocVsMKbDTy
|
||||
NYF7uPlzSGGClZsjE6DQyyGf5E9/U+EdwDKZwHYGCkzVjplUBo0BT3EN0vcc9jB/
|
||||
ML9Ta4ETPyf66BhSVcD+eeNipPFAul0Q7uZhErH1zr1evTy8XXyI
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEtzCCA5+gAwIBAgIJAIewRMI8OnvTMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYD
|
||||
VQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xHDAa
|
||||
BgNVBAoTE0hhc2hpQ29ycCBUZXN0IENlcnQxDDAKBgNVBAsTA0RldjEWMBQGA1UE
|
||||
AxMNdGVzdC5pbnRlcm5hbDEgMB4GCSqGSIb3DQEJARYRdGVzdEBpbnRlcm5hbC5j
|
||||
b20wHhcNMTQwNDA3MTkwMTA4WhcNMjQwNDA0MTkwMTA4WjCBmDELMAkGA1UEBhMC
|
||||
VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRwwGgYDVQQK
|
||||
ExNIYXNoaUNvcnAgVGVzdCBDZXJ0MQwwCgYDVQQLEwNEZXYxFjAUBgNVBAMTDXRl
|
||||
c3QuaW50ZXJuYWwxIDAeBgkqhkiG9w0BCQEWEXRlc3RAaW50ZXJuYWwuY29tMIIB
|
||||
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrs6JK4NpiOItxrpNR/1ppUU
|
||||
mH7p2BgLCBZ6eHdclle9J56i68adt8J85zaqphCfz6VDP58DsFx+N50PZyjQaDsU
|
||||
d0HejRqfHRMtg2O+UQkv4Z66+Vo+gc6uGuANi2xMtSYDVTAqqzF48OOPQDgYkzcG
|
||||
xcFZzTRFFZt2vPnyHj8cHcaFo/NMNVh7C3yTXevRGNm9u2mrbxCEeiHzFC2WUnvg
|
||||
U2jQuC7Fhnl33Zd3B6d3mQH6O23ncmwxTcPUJe6xZaIRrDuzwUcyhLj5Z3faag/f
|
||||
pFIIcHSiHRfoqHLGsGg+3swId/zVJSSDHr7pJUu7Cre+vZa63FqDaooqvnisrQID
|
||||
AQABo4IBADCB/TAdBgNVHQ4EFgQUo/nrOfqvbee2VklVKIFlyQEbuJUwgc0GA1Ud
|
||||
IwSBxTCBwoAUo/nrOfqvbee2VklVKIFlyQEbuJWhgZ6kgZswgZgxCzAJBgNVBAYT
|
||||
AlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEcMBoGA1UE
|
||||
ChMTSGFzaGlDb3JwIFRlc3QgQ2VydDEMMAoGA1UECxMDRGV2MRYwFAYDVQQDEw10
|
||||
ZXN0LmludGVybmFsMSAwHgYJKoZIhvcNAQkBFhF0ZXN0QGludGVybmFsLmNvbYIJ
|
||||
AIewRMI8OnvTMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADa9fV9h
|
||||
gjapBlkNmu64WX0Ufub5dsJrdHS8672P30S7ILB7Mk0W8sL65IezRsZnG898yHf9
|
||||
2uzmz5OvNTM9K380g7xFlyobSVq+6yqmmSAlA/ptAcIIZT727P5jig/DB7fzJM3g
|
||||
jctDlEGOmEe50GQXc25VKpcpjAsNQi5ER5gowQ0v3IXNZs+yU+LvxLHc0rUJ/XSp
|
||||
lFCAMOqd5uRoMOejnT51G6krvLNzPaQ3N9jQfNVY4Q0zfs0M+6dRWvqfqB9Vyq8/
|
||||
POLMld+HyAZEBk9zK3ZVIXx6XS4dkDnSNR91njLq7eouf6M7+7s/oMQZZRtAfQ6r
|
||||
wlW975rYa1ZqEdA=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1 @@
|
|||
0C
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDtTCCAp2gAwIBAgIBCzANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMCVVMx
|
||||
CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRwwGgYDVQQKExNI
|
||||
YXNoaUNvcnAgVGVzdCBDZXJ0MQwwCgYDVQQLEwNEZXYxFjAUBgNVBAMTDXRlc3Qu
|
||||
aW50ZXJuYWwxIDAeBgkqhkiG9w0BCQEWEXRlc3RAaW50ZXJuYWwuY29tMB4XDTE0
|
||||
MDQwNzE5NDE0NloXDTE1MDQwNzE5NDE0NlowfDEYMBYGA1UEAxMPdGVzdGNvLmlu
|
||||
dGVybmFsMRMwEQYDVQQIEwpDYWxpZm9ybmlhMQswCQYDVQQGEwJVUzEeMBwGCSqG
|
||||
SIb3DQEJARYPdGVzdEB0ZXN0Y28uY29tMQ8wDQYDVQQKEwZUZXN0Q28xDTALBgNV
|
||||
BAsTBEJldGEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALXNUbqVUjsspMHh
|
||||
46PB37kI6rKzZlSsyaBYWsS19tdrwS83nSXZJlaMAVPLR/TR+b1P81zxZD8m8ZmQ
|
||||
2bK70No5usFkdlbowVLAIMySIQmZF1tTLXbiCKldiwEjkOWa1xKwJauoM0XKnWkF
|
||||
mLGDxIAl84DZaLgmNj2t/q8d+laDAgMBAAGjgagwgaUwCQYDVR0TBAIwADAdBgNV
|
||||
HQ4EFgQUDNy9EoPn+YNrIlvkWMxh5QKS5JwwHwYDVR0jBBgwFoAUo/nrOfqvbee2
|
||||
VklVKIFlyQEbuJUwCwYDVR0PBAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
|
||||
BgEFBQcDAjAsBgNVHR8EJTAjMCGgH6AdhhtodHRwOi8vcGF0aC50by5jcmwvbXlj
|
||||
YS5jcmwwDQYJKoZIhvcNAQEFBQADggEBABCvJP2zMF60ooDYLaaNuPcmkokWIp/n
|
||||
X00dZ6y1aI063y8OE1CSY8rGv3W/ONkS3cOQvVhdAVtAzqGnDK3VsFZzmWR+tuqR
|
||||
KUhkzcC0X4nNq91iik1dTj2skl1Jkq6lJhrY1sR6JXOSn68Iv2KAuLVNn5tC5hzB
|
||||
WOK7S2ffqfof3eV+g0cgFNCzaS75cn8YlXBqQGpn5odcVDX8c80Xj/Si18wWDPR9
|
||||
/kq5xKRsaFkKJYOoswRwoq9kwukruMndxf7/Az/YEHdimZKMpxfK/qzI0KUw4XO0
|
||||
lpEkMZaA3l+xYB2fNHwlwyz77RNCoySCnii61hmxLNDwUiokgdJcY9U=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,12 @@
|
|||
-----BEGIN CERTIFICATE REQUEST-----
|
||||
MIIB1TCCAT4CAQAwgZQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
|
||||
MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKEwZUZXN0Q28xDTALBgNV
|
||||
BAsTBEJldGExGDAWBgNVBAMTD3Rlc3Rjby5pbnRlcm5hbDEeMBwGCSqGSIb3DQEJ
|
||||
ARYPdGVzdEB0ZXN0Y28uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1
|
||||
zVG6lVI7LKTB4eOjwd+5COqys2ZUrMmgWFrEtfbXa8EvN50l2SZWjAFTy0f00fm9
|
||||
T/Nc8WQ/JvGZkNmyu9DaObrBZHZW6MFSwCDMkiEJmRdbUy124gipXYsBI5DlmtcS
|
||||
sCWrqDNFyp1pBZixg8SAJfOA2Wi4JjY9rf6vHfpWgwIDAQABoAAwDQYJKoZIhvcN
|
||||
AQEFBQADgYEAG7SdzgaTcJ1sMJ+pH+42J9Fyp2SY+WGEP3dA7f1/Lwc1dFHeKLPL
|
||||
X0Gv6DgNkxUwWOe/yncq+dkuUkDGx3M1FRvpKCKFAywp+j0NxIll7821/2Jvf5/f
|
||||
BVHPmgUIzZEYz0d6vcCQl2RKd83wLWcR77JTrD2S1JqAEkrMw/xUa/s=
|
||||
-----END CERTIFICATE REQUEST-----
|
|
@ -0,0 +1,15 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQC1zVG6lVI7LKTB4eOjwd+5COqys2ZUrMmgWFrEtfbXa8EvN50l
|
||||
2SZWjAFTy0f00fm9T/Nc8WQ/JvGZkNmyu9DaObrBZHZW6MFSwCDMkiEJmRdbUy12
|
||||
4gipXYsBI5DlmtcSsCWrqDNFyp1pBZixg8SAJfOA2Wi4JjY9rf6vHfpWgwIDAQAB
|
||||
AoGAQNOSxg6CwPj9UulCa90w8mD8l3YjEiR+zP9UdnpQJ6aTv0t8bMeOxSOtQXzm
|
||||
DqVlAR1mMQkebuprEhA1oGcaZGx5hYIfhad8k5e18nkfogMG2J23fjyw/Zvcsbpi
|
||||
AebEHGwrPnGplG2AZ2knRVpQ0EkKLUZ3sHGQjCuh/srw5QECQQDb5vKCz181/wnE
|
||||
0CVIAg61ibwyWNR2s3i2Xhsp0UmhbTSknP2mQzdUqegMB5Y5mbruXy46EHuia5H7
|
||||
LnFXfx6BAkEA06VF154NirTBU6MCPtj0ynUkTFSYu7oJG7U+rMA7Bx4itLjAy76g
|
||||
wniz6HWHMqgQP1G8mhkHYb6fmkBQY1V7AwJANjP07t1ioJKeu856ggdPzNuIcfiH
|
||||
VkLirEEB/QrDVXDvmuu/ce37g3jl46EzHDuSYhM/97v8XYqaTwmhkmmZAQJAbFVq
|
||||
3KVwdRF06+TCn3zaQE+Z1uBulZjyVJZ/kFmNXWVVioAPX7sh+qliHZkbLRjNyDuE
|
||||
eLRbDPNQKtrEyzPUFQJAXQ4AKXWs1LMzNFueMH+qZ3NjX+GdVBrvdri7D+yzLXPL
|
||||
IwNWljwTONXQHwwRlEHIQJVw85qfdLfNngHKEW5X7w==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
|||
Instructions from https://langui.sh/2009/01/18/openssl-self-signed-ca/
|
|
@ -8,11 +8,12 @@ sidebar_current: "docs-agent-encryption"
|
|||
|
||||
The Consul agent supports encrypting all of its network traffic. The exact
|
||||
method of this encryption is described on the
|
||||
[encryption internals page](/docs/internals/security.html).
|
||||
[encryption internals page](/docs/internals/security.html). There are two
|
||||
seperate systems, one for gossip traffic and one for RPC.
|
||||
|
||||
## Enabling Encryption
|
||||
## Gossip Encryption
|
||||
|
||||
Enabling encryption only requires that you set an encryption key when
|
||||
Enabling gossip encryption only requires that you set an encryption key when
|
||||
starting the Consul agent. The key can be set using the `-encrypt` flag
|
||||
on `consul agent` or by setting the `encrypt_key` in a configuration file.
|
||||
It is advisable to put the key in a configuration file to avoid other users
|
||||
|
@ -47,3 +48,30 @@ $ consul agent -data=/tmp/consul -encrypt=cg8StVXbQJ0gPvMd9o7yrg==
|
|||
All nodes within a Consul cluster must share the same encryption key in
|
||||
order to send and receive cluster information.
|
||||
|
||||
# RPC Encryption with TLS
|
||||
|
||||
Consul supports using TLS to verify the authenticity of servers and clients. For this
|
||||
to work, Consul requires that all clients and servers have key pairs that are generated
|
||||
by a single Certificate Authority. This can be a private CA, used only internally. The
|
||||
CA then signs keys for each of the agents. [Here](https://langui.sh/2009/01/18/openssl-self-signed-ca/)
|
||||
is a tutorial on generating both a CA and signing keys using OpenSSL.
|
||||
|
||||
There are a number of things to consider when setting up TLS for Consul. Either we can
|
||||
use TLS just to verify the authenticity of the servers, or we can also verify the authenticity
|
||||
of clients. The former can be used to prevent unauthorized access. This behavior is controlled
|
||||
using either the `verify_incoming` and `verify_outgoing` [options](/docs/agent/options.html).
|
||||
|
||||
If `verify_outgoing` is set, then agents verify the authenticity of Consuls for outgoing
|
||||
connections. This means server nodes must present a certificate signed by the `ca_file` that
|
||||
the agent has. This option must be set on all agents, and there must be a `ca_file` provided
|
||||
to check the certificate against. If this is set, then all server nodes must have an appropriate
|
||||
key pair set using `cert_file` and `key_file`.
|
||||
|
||||
If `verify_incoming` is set, then the servers verify the authenticity of all incoming
|
||||
connections. Servers will also disallow any non-TLS connections. If this is set, then all
|
||||
clients must have a valid key pair set using `cert_file` and `key_file`. To force clients to
|
||||
use TLs, `verify_outgoing` must also be set.
|
||||
|
||||
TLS is used to secure the RPC calls between agents, but gossip between nodes is done over UDP
|
||||
and is secured using a symmetric key. See above for enabling gossip encryption.
|
||||
|
||||
|
|
|
@ -205,3 +205,27 @@ definitions support being updated during a reload.
|
|||
|
||||
* `statsite_addr` - Equivalent to the `-statsite` command-line flag.
|
||||
|
||||
* `verify_incoming` - If set to True, Consul requires that all incoming
|
||||
connections make use of TLS, and that the client provides a certificate signed
|
||||
by the Certificate Authority from the `ca_file`. By default, this is false, and
|
||||
Consul will not enforce the use of TLS or verify a client's authenticity. This
|
||||
only applies to Consul servers, since a client never has an incoming connection.
|
||||
|
||||
* `verify_outgoing` - If set to True, Consul requires that all outgoing connections
|
||||
make use of TLS, and that the server provide a certificate that is signed by
|
||||
the Certificate Authority from the `ca_file`. By default, this is false, and Consul
|
||||
will not make use of TLS for outgoing connections. This applies to clients and servers,
|
||||
as both will make outgoing connections.
|
||||
|
||||
* `ca_file` - This provides a the file path to a PEM encoded certificate authority.
|
||||
The certificate authority is used to check the authenticity of client and server
|
||||
connections with the appropriate `verify_incoming` or `verify_outgoing` flags.
|
||||
|
||||
* `cert_file` - This provides a the file path to a PEM encoded certificate.
|
||||
The certificate is provided to clients or servers to verify the agents authenticity.
|
||||
Must be provided along with the `key_file`.
|
||||
|
||||
* `key_file` - This provides a the file path to a PEM encoded private key.
|
||||
The key is used with the certificate to verify the agents authenticity.
|
||||
Must be provided along with the `cert_file`.
|
||||
|
||||
|
|
Loading…
Reference in New Issue