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 84d6ac2d51
commit cd56a5ebdd
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) { func TestClientTLSOptions(t *testing.T) {
t.Parallel() 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.CAFile = "../test/client_certs/rootca.crt"
conf.CertFile = "../test/client_certs/server.crt" conf.CertFile = "../test/client_certs/server.crt"
conf.KeyFile = "../test/client_certs/server.key" 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 // Start a server without VerifyIncomingHTTPS
clientWithoutCerts, err := NewClient(&Config{ _, srvNoVerify := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
Address: s.HTTPSAddr, conf.CAFile = "../test/client_certs/rootca.crt"
Scheme: "https", conf.CertFile = "../test/client_certs/server.crt"
TLSConfig: TLSConfig{ conf.KeyFile = "../test/client_certs/server.key"
Address: "consul.test", conf.VerifyIncomingHTTPS = false
CAFile: "../test/client_certs/rootca.crt",
},
}) })
if err != nil { defer srvNoVerify.Stop()
t.Fatal(err)
}
// Should fail // Client without a cert
_, err = clientWithoutCerts.Agent().Self() t.Run("client without cert, validation", func(t *testing.T) {
if err == nil || !strings.Contains(err.Error(), "bad certificate") { client, err := NewClient(&Config{
t.Fatal(err) 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 // Should fail
clientWithCerts, err := NewClient(&Config{ _, err = client.Agent().Self()
Address: s.HTTPSAddr, if err == nil || !strings.Contains(err.Error(), "bad certificate") {
Scheme: "https", t.Fatal(err)
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 // Client with a valid cert
_, err = clientWithCerts.Agent().Self() t.Run("client with cert, validation", func(t *testing.T) {
if err != nil { client, err := NewClient(&Config{
t.Fatal(err) 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) { func TestSetQueryOptions(t *testing.T) {

View File

@ -449,7 +449,7 @@ func (a *Agent) consulConfig() *consul.Config {
a.config.Version, a.config.VersionPrerelease, revision) a.config.Version, a.config.VersionPrerelease, revision)
// Copy the TLS configuration // Copy the TLS configuration
base.VerifyIncoming = a.config.VerifyIncoming base.VerifyIncoming = a.config.VerifyIncoming || a.config.VerifyIncomingRPC
base.VerifyOutgoing = a.config.VerifyOutgoing base.VerifyOutgoing = a.config.VerifyOutgoing
base.VerifyServerHostname = a.config.VerifyServerHostname base.VerifyServerHostname = a.config.VerifyServerHostname
base.CAFile = a.config.CAFile 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. // must match a provided certificate authority. This can be used to force client auth.
VerifyIncoming bool `mapstructure:"verify_incoming"` 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. // VerifyOutgoing is used to verify the authenticity of outgoing connections.
// This means that TLS requests are used. TLS connections must match a provided // This means that TLS requests are used. TLS connections must match a provided
// certificate authority. This is used to verify authenticity of server nodes. // certificate authority. This is used to verify authenticity of server nodes.
@ -1546,6 +1556,12 @@ func MergeConfig(a, b *Config) *Config {
if b.VerifyIncoming { if b.VerifyIncoming {
result.VerifyIncoming = true result.VerifyIncoming = true
} }
if b.VerifyIncomingRPC {
result.VerifyIncomingRPC = true
}
if b.VerifyIncomingHTTPS {
result.VerifyIncomingHTTPS = true
}
if b.VerifyOutgoing { if b.VerifyOutgoing {
result.VerifyOutgoing = true result.VerifyOutgoing = true
} }

View File

@ -355,7 +355,8 @@ func TestDecodeConfig(t *testing.T) {
} }
// TLS // 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}` "tls_cipher_suites": "TLS_RSA_WITH_AES_256_CBC_SHA", "tls_prefer_server_cipher_suites": true}`
config, err = DecodeConfig(bytes.NewReader([]byte(input))) config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil { if err != nil {
@ -366,6 +367,14 @@ func TestDecodeConfig(t *testing.T) {
t.Fatalf("bad: %#v", config) 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 { if config.VerifyOutgoing != true {
t.Fatalf("bad: %#v", config) t.Fatalf("bad: %#v", config)
} }

View File

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

View File

@ -56,32 +56,34 @@ type TestAddressConfig struct {
// TestServerConfig is the main server configuration struct. // TestServerConfig is the main server configuration struct.
type TestServerConfig struct { type TestServerConfig struct {
NodeName string `json:"node_name"` NodeName string `json:"node_name"`
NodeID string `json:"node_id"` NodeID string `json:"node_id"`
NodeMeta map[string]string `json:"node_meta,omitempty"` NodeMeta map[string]string `json:"node_meta,omitempty"`
Performance *TestPerformanceConfig `json:"performance,omitempty"` Performance *TestPerformanceConfig `json:"performance,omitempty"`
Bootstrap bool `json:"bootstrap,omitempty"` Bootstrap bool `json:"bootstrap,omitempty"`
Server bool `json:"server,omitempty"` Server bool `json:"server,omitempty"`
DataDir string `json:"data_dir,omitempty"` DataDir string `json:"data_dir,omitempty"`
Datacenter string `json:"datacenter,omitempty"` Datacenter string `json:"datacenter,omitempty"`
DisableCheckpoint bool `json:"disable_update_check"` DisableCheckpoint bool `json:"disable_update_check"`
LogLevel string `json:"log_level,omitempty"` LogLevel string `json:"log_level,omitempty"`
Bind string `json:"bind_addr,omitempty"` Bind string `json:"bind_addr,omitempty"`
Addresses *TestAddressConfig `json:"addresses,omitempty"` Addresses *TestAddressConfig `json:"addresses,omitempty"`
Ports *TestPortConfig `json:"ports,omitempty"` Ports *TestPortConfig `json:"ports,omitempty"`
RaftProtocol int `json:"raft_protocol,omitempty"` RaftProtocol int `json:"raft_protocol,omitempty"`
ACLMasterToken string `json:"acl_master_token,omitempty"` ACLMasterToken string `json:"acl_master_token,omitempty"`
ACLDatacenter string `json:"acl_datacenter,omitempty"` ACLDatacenter string `json:"acl_datacenter,omitempty"`
ACLDefaultPolicy string `json:"acl_default_policy,omitempty"` ACLDefaultPolicy string `json:"acl_default_policy,omitempty"`
ACLEnforceVersion8 bool `json:"acl_enforce_version_8"` ACLEnforceVersion8 bool `json:"acl_enforce_version_8"`
Encrypt string `json:"encrypt,omitempty"` Encrypt string `json:"encrypt,omitempty"`
CAFile string `json:"ca_file,omitempty"` CAFile string `json:"ca_file,omitempty"`
CertFile string `json:"cert_file,omitempty"` CertFile string `json:"cert_file,omitempty"`
KeyFile string `json:"key_file,omitempty"` KeyFile string `json:"key_file,omitempty"`
VerifyIncoming bool `json:"verify_incoming,omitempty"` VerifyIncoming bool `json:"verify_incoming,omitempty"`
VerifyOutgoing bool `json:"verify_outgoing,omitempty"` VerifyIncomingRPC bool `json:"verify_incoming_rpc,omitempty"`
Stdout, Stderr io.Writer `json:"-"` VerifyIncomingHTTPS bool `json:"verify_incoming_https,omitempty"`
Args []string `json:"-"` VerifyOutgoing bool `json:"verify_outgoing,omitempty"`
Stdout, Stderr io.Writer `json:"-"`
Args []string `json:"-"`
} }
// ServerConfigCallback is a function interface which can be // 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 * <a name="verify_incoming"></a><a href="#verify_incoming">`verify_incoming`</a> - If
set to true, Consul requires that all incoming set to true, Consul requires that all incoming
connections make use of TLS and that the client provides a certificate signed 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 by a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path).
Consul will not enforce the use of TLS or verify a client's authenticity. This This applies to both server RPC and to the HTTPS API. By default, this is false, and
applies to both server RPC and to the HTTPS API. To enable the HTTPS API, you Consul will not enforce the use of TLS or verify a client's authenticity.
must define an HTTPS port via the [`ports`](#ports) configuration. By default, HTTPS
is disabled. * <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 * <a name="verify_outgoing"></a><a href="#verify_outgoing">`verify_outgoing`</a> - If set to
true, Consul requires that all outgoing connections true, Consul requires that all outgoing connections
make use of TLS and that the server provides a certificate that is signed by 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 a Certificate Authority from the [`ca_file`](#ca_file) or [`ca_path`](#ca_path). By default,
will not make use of TLS for outgoing connections. This applies to clients and servers this is false, and Consul will not make use of TLS for outgoing connections. This applies to clients
as both will make outgoing connections. 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 * <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 true, Consul verifies for all outgoing connections that the TLS certificate presented by the servers