Add separate option for verifying incoming HTTPS traffic (#2974)

* Add separate option for verifying incoming HTTPS traffic
This commit is contained in:
Kyle Havlovitz 2017-04-28 16:15:55 -07:00 committed by GitHub
parent 1973e66c07
commit b5ed2ba536
7 changed files with 171 additions and 74 deletions

View File

@ -256,53 +256,111 @@ func TestSetupTLSConfig(t *testing.T) {
func TestClientTLSOptions(t *testing.T) {
t.Parallel()
_, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
// Start a server that verifies incoming HTTPS connections
_, srvVerify := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
conf.CAFile = "../test/client_certs/rootca.crt"
conf.CertFile = "../test/client_certs/server.crt"
conf.KeyFile = "../test/client_certs/server.key"
conf.VerifyIncoming = true
conf.VerifyIncomingHTTPS = true
})
defer s.Stop()
defer srvVerify.Stop()
// Set up a client without certs
clientWithoutCerts, err := NewClient(&Config{
Address: s.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
},
// Start a server without VerifyIncomingHTTPS
_, srvNoVerify := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
conf.CAFile = "../test/client_certs/rootca.crt"
conf.CertFile = "../test/client_certs/server.crt"
conf.KeyFile = "../test/client_certs/server.key"
conf.VerifyIncomingHTTPS = false
})
if err != nil {
t.Fatal(err)
}
defer srvNoVerify.Stop()
// Should fail
_, err = clientWithoutCerts.Agent().Self()
if err == nil || !strings.Contains(err.Error(), "bad certificate") {
t.Fatal(err)
}
// Client without a cert
t.Run("client without cert, validation", func(t *testing.T) {
client, err := NewClient(&Config{
Address: srvVerify.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
},
})
if err != nil {
t.Fatal(err)
}
// Set up a client with valid certs
clientWithCerts, err := NewClient(&Config{
Address: s.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
CertFile: "../test/client_certs/client.crt",
KeyFile: "../test/client_certs/client.key",
},
// Should fail
_, err = client.Agent().Self()
if err == nil || !strings.Contains(err.Error(), "bad certificate") {
t.Fatal(err)
}
})
if err != nil {
t.Fatal(err)
}
// Should succeed
_, err = clientWithCerts.Agent().Self()
if err != nil {
t.Fatal(err)
}
// Client with a valid cert
t.Run("client with cert, validation", func(t *testing.T) {
client, err := NewClient(&Config{
Address: srvVerify.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
CertFile: "../test/client_certs/client.crt",
KeyFile: "../test/client_certs/client.key",
},
})
if err != nil {
t.Fatal(err)
}
// Should succeed
_, err = client.Agent().Self()
if err != nil {
t.Fatal(err)
}
})
// Client without a cert
t.Run("client without cert, no validation", func(t *testing.T) {
client, err := NewClient(&Config{
Address: srvNoVerify.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
},
})
if err != nil {
t.Fatal(err)
}
// Should succeed
_, err = client.Agent().Self()
if err != nil {
t.Fatal(err)
}
})
// Client with a valid cert
t.Run("client with cert, no validation", func(t *testing.T) {
client, err := NewClient(&Config{
Address: srvNoVerify.HTTPSAddr,
Scheme: "https",
TLSConfig: TLSConfig{
Address: "consul.test",
CAFile: "../test/client_certs/rootca.crt",
CertFile: "../test/client_certs/client.crt",
KeyFile: "../test/client_certs/client.key",
},
})
if err != nil {
t.Fatal(err)
}
// Should succeed
_, err = client.Agent().Self()
if err != nil {
t.Fatal(err)
}
})
}
func TestSetQueryOptions(t *testing.T) {

View File

@ -449,7 +449,7 @@ func (a *Agent) consulConfig() *consul.Config {
a.config.Version, a.config.VersionPrerelease, revision)
// Copy the TLS configuration
base.VerifyIncoming = a.config.VerifyIncoming
base.VerifyIncoming = a.config.VerifyIncoming || a.config.VerifyIncomingRPC
base.VerifyOutgoing = a.config.VerifyOutgoing
base.VerifyServerHostname = a.config.VerifyServerHostname
base.CAFile = a.config.CAFile

View File

@ -453,6 +453,16 @@ type Config struct {
// must match a provided certificate authority. This can be used to force client auth.
VerifyIncoming bool `mapstructure:"verify_incoming"`
// VerifyIncomingRPC is used to verify the authenticity of incoming RPC 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.
VerifyIncomingRPC bool `mapstructure:"verify_incoming_rpc"`
// VerifyIncomingHTTPS is used to verify the authenticity of incoming HTTPS 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.
VerifyIncomingHTTPS bool `mapstructure:"verify_incoming_https"`
// 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.
@ -1546,6 +1556,12 @@ func MergeConfig(a, b *Config) *Config {
if b.VerifyIncoming {
result.VerifyIncoming = true
}
if b.VerifyIncomingRPC {
result.VerifyIncomingRPC = true
}
if b.VerifyIncomingHTTPS {
result.VerifyIncomingHTTPS = true
}
if b.VerifyOutgoing {
result.VerifyOutgoing = true
}

View File

@ -355,7 +355,8 @@ func TestDecodeConfig(t *testing.T) {
}
// TLS
input = `{"verify_incoming": true, "verify_outgoing": true, "verify_server_hostname": true, "tls_min_version": "tls12",
input = `{"verify_incoming": true, "verify_incoming_rpc": true, "verify_incoming_https": true,
"verify_outgoing": true, "verify_server_hostname": true, "tls_min_version": "tls12",
"tls_cipher_suites": "TLS_RSA_WITH_AES_256_CBC_SHA", "tls_prefer_server_cipher_suites": true}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
@ -366,6 +367,14 @@ func TestDecodeConfig(t *testing.T) {
t.Fatalf("bad: %#v", config)
}
if config.VerifyIncomingRPC != true {
t.Fatalf("bad: %#v", config)
}
if config.VerifyIncomingHTTPS != true {
t.Fatalf("bad: %#v", config)
}
if config.VerifyOutgoing != true {
t.Fatalf("bad: %#v", config)
}

View File

@ -56,7 +56,7 @@ func NewHTTPServers(agent *Agent, config *Config, logOutput io.Writer) ([]*HTTPS
}
tlsConf := &tlsutil.Config{
VerifyIncoming: config.VerifyIncoming,
VerifyIncoming: config.VerifyIncoming || config.VerifyIncomingHTTPS,
VerifyOutgoing: config.VerifyOutgoing,
CAFile: config.CAFile,
CAPath: config.CAPath,

View File

@ -56,32 +56,34 @@ type TestAddressConfig struct {
// TestServerConfig is the main server configuration struct.
type TestServerConfig struct {
NodeName string `json:"node_name"`
NodeID string `json:"node_id"`
NodeMeta map[string]string `json:"node_meta,omitempty"`
Performance *TestPerformanceConfig `json:"performance,omitempty"`
Bootstrap bool `json:"bootstrap,omitempty"`
Server bool `json:"server,omitempty"`
DataDir string `json:"data_dir,omitempty"`
Datacenter string `json:"datacenter,omitempty"`
DisableCheckpoint bool `json:"disable_update_check"`
LogLevel string `json:"log_level,omitempty"`
Bind string `json:"bind_addr,omitempty"`
Addresses *TestAddressConfig `json:"addresses,omitempty"`
Ports *TestPortConfig `json:"ports,omitempty"`
RaftProtocol int `json:"raft_protocol,omitempty"`
ACLMasterToken string `json:"acl_master_token,omitempty"`
ACLDatacenter string `json:"acl_datacenter,omitempty"`
ACLDefaultPolicy string `json:"acl_default_policy,omitempty"`
ACLEnforceVersion8 bool `json:"acl_enforce_version_8"`
Encrypt string `json:"encrypt,omitempty"`
CAFile string `json:"ca_file,omitempty"`
CertFile string `json:"cert_file,omitempty"`
KeyFile string `json:"key_file,omitempty"`
VerifyIncoming bool `json:"verify_incoming,omitempty"`
VerifyOutgoing bool `json:"verify_outgoing,omitempty"`
Stdout, Stderr io.Writer `json:"-"`
Args []string `json:"-"`
NodeName string `json:"node_name"`
NodeID string `json:"node_id"`
NodeMeta map[string]string `json:"node_meta,omitempty"`
Performance *TestPerformanceConfig `json:"performance,omitempty"`
Bootstrap bool `json:"bootstrap,omitempty"`
Server bool `json:"server,omitempty"`
DataDir string `json:"data_dir,omitempty"`
Datacenter string `json:"datacenter,omitempty"`
DisableCheckpoint bool `json:"disable_update_check"`
LogLevel string `json:"log_level,omitempty"`
Bind string `json:"bind_addr,omitempty"`
Addresses *TestAddressConfig `json:"addresses,omitempty"`
Ports *TestPortConfig `json:"ports,omitempty"`
RaftProtocol int `json:"raft_protocol,omitempty"`
ACLMasterToken string `json:"acl_master_token,omitempty"`
ACLDatacenter string `json:"acl_datacenter,omitempty"`
ACLDefaultPolicy string `json:"acl_default_policy,omitempty"`
ACLEnforceVersion8 bool `json:"acl_enforce_version_8"`
Encrypt string `json:"encrypt,omitempty"`
CAFile string `json:"ca_file,omitempty"`
CertFile string `json:"cert_file,omitempty"`
KeyFile string `json:"key_file,omitempty"`
VerifyIncoming bool `json:"verify_incoming,omitempty"`
VerifyIncomingRPC bool `json:"verify_incoming_rpc,omitempty"`
VerifyIncomingHTTPS bool `json:"verify_incoming_https,omitempty"`
VerifyOutgoing bool `json:"verify_outgoing,omitempty"`
Stdout, Stderr io.Writer `json:"-"`
Args []string `json:"-"`
}
// ServerConfigCallback is a function interface which can be

View File

@ -1052,18 +1052,30 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* <a name="verify_incoming"></a><a href="#verify_incoming">`verify_incoming`</a> - 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`](#ca_file). By default, this is false, and
Consul will not enforce the use of TLS or verify a client's authenticity. This
applies to both server RPC and to the HTTPS API. To enable the HTTPS API, you
must define an HTTPS port via the [`ports`](#ports) configuration. By default, HTTPS
is disabled.
by a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path).
This applies to both server RPC and to the HTTPS API. By default, this is false, and
Consul will not enforce the use of TLS or verify a client's authenticity.
* <a name="verify_incoming_rpc"></a><a href="#verify_incoming_rpc">`verify_incoming_rpc`</a> - If
set to true, Consul requires that all incoming RPC
connections make use of TLS and that the client provides a certificate signed
by a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path). By default,
this is false, and Consul will not enforce the use of TLS or verify a client's authenticity.
* <a name="verify_incoming_https"></a><a href="#verify_incoming_https">`verify_incoming_https`</a> - If
set to true, Consul requires that all incoming HTTPS
connections make use of TLS and that the client provides a certificate signed
by a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path). By default,
this is false, and Consul will not enforce the use of TLS or verify a client's authenticity. To
enable the HTTPS API, you must define an HTTPS port via the [`ports`](#ports) configuration. By
default, HTTPS is disabled.
* <a name="verify_outgoing"></a><a href="#verify_outgoing">`verify_outgoing`</a> - If set to
true, Consul requires that all outgoing connections
make use of TLS and that the server provides a certificate that is signed by
the Certificate Authority from the [`ca_file`](#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.
a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path). 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.
* <a name="verify_server_hostname"></a><a href="#verify_server_hostname">`verify_server_hostname`</a> - If set to
true, Consul verifies for all outgoing connections that the TLS certificate presented by the servers