Changed the way TLS config is parsed
This commit is contained in:
parent
2e3118e69c
commit
e03927bb5c
|
@ -134,10 +134,10 @@ type Config struct {
|
||||||
// allocation metrics to remote Telemetry sinks
|
// allocation metrics to remote Telemetry sinks
|
||||||
PublishAllocationMetrics bool
|
PublishAllocationMetrics bool
|
||||||
|
|
||||||
// Don't verify callers identity
|
// HttpTLS enables TLS for the HTTP endpoints on the clients.
|
||||||
HttpTLS bool `mapstructure:"http_tls"`
|
HttpTLS bool `mapstructure:"http_tls"`
|
||||||
|
|
||||||
// Verify inbound and outbound
|
// RpcTLS enables TLS for the outgoing TLS connections to the Nomad servers.
|
||||||
RpcTLS bool `mapstructure:"rpc_tls"`
|
RpcTLS bool `mapstructure:"rpc_tls"`
|
||||||
|
|
||||||
// VerifyServerHostname is used to enable hostname verification of servers. This
|
// VerifyServerHostname is used to enable hostname verification of servers. This
|
||||||
|
@ -254,6 +254,7 @@ func (c *Config) ReadStringListToMapDefault(key, defaultValue string) map[string
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig returns a TLSUtil Config based on the client configuration
|
||||||
func (c *Config) TLSConfig() *tlsutil.Config {
|
func (c *Config) TLSConfig() *tlsutil.Config {
|
||||||
tlsConf := &tlsutil.Config{
|
tlsConf := &tlsutil.Config{
|
||||||
VerifyIncoming: true,
|
VerifyIncoming: true,
|
||||||
|
|
|
@ -245,11 +245,12 @@ func (a *Agent) serverConfig() (*nomad.Config, error) {
|
||||||
conf.VaultConfig = a.config.Vault
|
conf.VaultConfig = a.config.Vault
|
||||||
|
|
||||||
// Set the TLS related configs
|
// Set the TLS related configs
|
||||||
conf.RpcTLS = a.config.RpcTLS
|
conf.RpcTLS = a.config.TLSConfig.EnableRPC
|
||||||
conf.VerifyServerHostname = a.config.VerifyServerHostname
|
conf.RequireTLS = conf.RpcTLS
|
||||||
conf.CAFile = a.config.CAFile
|
conf.VerifyServerHostname = a.config.TLSConfig.VerifyServerHostname
|
||||||
conf.CertFile = a.config.CertFile
|
conf.CAFile = a.config.TLSConfig.CAFile
|
||||||
conf.KeyFile = a.config.KeyFile
|
conf.CertFile = a.config.TLSConfig.CertFile
|
||||||
|
conf.KeyFile = a.config.TLSConfig.KeyFile
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
@ -366,12 +367,12 @@ func (a *Agent) clientConfig() (*clientconfig.Config, error) {
|
||||||
conf.PublishAllocationMetrics = a.config.Telemetry.PublishAllocationMetrics
|
conf.PublishAllocationMetrics = a.config.Telemetry.PublishAllocationMetrics
|
||||||
|
|
||||||
// Set the TLS related configs
|
// Set the TLS related configs
|
||||||
conf.HttpTLS = a.config.HttpTLS
|
conf.HttpTLS = a.config.TLSConfig.EnableHTTP
|
||||||
conf.RpcTLS = a.config.RpcTLS
|
conf.RpcTLS = a.config.TLSConfig.EnableRPC
|
||||||
conf.VerifyServerHostname = a.config.VerifyServerHostname
|
conf.VerifyServerHostname = a.config.TLSConfig.VerifyServerHostname
|
||||||
conf.CAFile = a.config.CAFile
|
conf.CAFile = a.config.TLSConfig.CAFile
|
||||||
conf.CertFile = a.config.CertFile
|
conf.CertFile = a.config.TLSConfig.CertFile
|
||||||
conf.KeyFile = a.config.KeyFile
|
conf.KeyFile = a.config.TLSConfig.KeyFile
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,3 +121,11 @@ vault {
|
||||||
tls_server_name = "foobar"
|
tls_server_name = "foobar"
|
||||||
tls_skip_verify = true
|
tls_skip_verify = true
|
||||||
}
|
}
|
||||||
|
tls {
|
||||||
|
http_tls = true
|
||||||
|
rpc_tls = true
|
||||||
|
verify_server_hostname = true
|
||||||
|
ca_file = "foo"
|
||||||
|
cert_file = "bar"
|
||||||
|
key_file = "pipe"
|
||||||
|
}
|
||||||
|
|
|
@ -112,31 +112,9 @@ type Config struct {
|
||||||
// List of config files that have been loaded (in order)
|
// List of config files that have been loaded (in order)
|
||||||
Files []string `mapstructure:"-"`
|
Files []string `mapstructure:"-"`
|
||||||
|
|
||||||
// Don't verify callers identity
|
// TLSConfig provides TLS related configuration for the Nomad server and
|
||||||
HttpTLS bool `mapstructure:"http_tls"`
|
// client
|
||||||
|
TLSConfig *TLSConfig `mapstructure:"tls"`
|
||||||
// Verify inbound and outbound
|
|
||||||
RpcTLS bool `mapstructure:"rpc_tls"`
|
|
||||||
|
|
||||||
// VerifyServerHostname is used to enable hostname verification of servers. This
|
|
||||||
// ensures that the certificate presented is valid for server.<datacenter>.<domain>.
|
|
||||||
// This prevents a compromised client from being restarted as a server, and then
|
|
||||||
// intercepting request traffic as well as being added as a raft peer. This should be
|
|
||||||
// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
|
|
||||||
// existing clients.
|
|
||||||
VerifyServerHostname bool `mapstructure:"verify_server_hostname"`
|
|
||||||
|
|
||||||
// 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"`
|
|
||||||
|
|
||||||
// HTTPAPIResponseHeaders allows users to configure the Nomad http agent to
|
// HTTPAPIResponseHeaders allows users to configure the Nomad http agent to
|
||||||
// set arbritrary headers on API responses
|
// set arbritrary headers on API responses
|
||||||
|
@ -161,6 +139,36 @@ type AtlasConfig struct {
|
||||||
Endpoint string `mapstructure:"endpoint"`
|
Endpoint string `mapstructure:"endpoint"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig provides TLS related configuration
|
||||||
|
type TLSConfig struct {
|
||||||
|
|
||||||
|
// EnableHTTP enabled TLS for http traffic to the Nomad server and clients
|
||||||
|
EnableHTTP bool `mapstructure:"http_tls"`
|
||||||
|
|
||||||
|
// EnableRPC enables TLS for RPC and Raft traffic to the Nomad servers
|
||||||
|
EnableRPC bool `mapstructure:"rpc_tls"`
|
||||||
|
|
||||||
|
// VerifyServerHostname is used to enable hostname verification of servers. This
|
||||||
|
// ensures that the certificate presented is valid for server.<datacenter>.<domain>.
|
||||||
|
// This prevents a compromised client from being restarted as a server, and then
|
||||||
|
// intercepting request traffic as well as being added as a raft peer. This should be
|
||||||
|
// enabled by default with VerifyOutgoing, but for legacy reasons we cannot break
|
||||||
|
// existing clients.
|
||||||
|
VerifyServerHostname bool `mapstructure:"verify_server_hostname"`
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
// ClientConfig is configuration specific to the client mode
|
// ClientConfig is configuration specific to the client mode
|
||||||
type ClientConfig struct {
|
type ClientConfig struct {
|
||||||
// Enabled controls if we are a client
|
// Enabled controls if we are a client
|
||||||
|
@ -512,6 +520,7 @@ func DefaultConfig() *Config {
|
||||||
CollectionInterval: "1s",
|
CollectionInterval: "1s",
|
||||||
collectionInterval: 1 * time.Second,
|
collectionInterval: 1 * time.Second,
|
||||||
},
|
},
|
||||||
|
TLSConfig: &TLSConfig{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,24 +592,6 @@ func (c *Config) Merge(b *Config) *Config {
|
||||||
if b.DisableAnonymousSignature {
|
if b.DisableAnonymousSignature {
|
||||||
result.DisableAnonymousSignature = true
|
result.DisableAnonymousSignature = true
|
||||||
}
|
}
|
||||||
if b.HttpTLS {
|
|
||||||
result.HttpTLS = true
|
|
||||||
}
|
|
||||||
if b.RpcTLS {
|
|
||||||
result.RpcTLS = true
|
|
||||||
}
|
|
||||||
if b.VerifyServerHostname {
|
|
||||||
result.VerifyServerHostname = true
|
|
||||||
}
|
|
||||||
if b.CAFile != "" {
|
|
||||||
result.CAFile = b.CAFile
|
|
||||||
}
|
|
||||||
if b.CertFile != "" {
|
|
||||||
result.CertFile = b.CertFile
|
|
||||||
}
|
|
||||||
if b.KeyFile != "" {
|
|
||||||
result.KeyFile = b.KeyFile
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the telemetry config
|
// Apply the telemetry config
|
||||||
if result.Telemetry == nil && b.Telemetry != nil {
|
if result.Telemetry == nil && b.Telemetry != nil {
|
||||||
|
@ -610,6 +601,14 @@ func (c *Config) Merge(b *Config) *Config {
|
||||||
result.Telemetry = result.Telemetry.Merge(b.Telemetry)
|
result.Telemetry = result.Telemetry.Merge(b.Telemetry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply the TLS Config
|
||||||
|
if result.TLSConfig == nil && b.TLSConfig != nil {
|
||||||
|
tlsConfig := *b.TLSConfig
|
||||||
|
result.TLSConfig = &tlsConfig
|
||||||
|
} else if b.TLSConfig != nil {
|
||||||
|
result.TLSConfig = result.TLSConfig.Merge(b.TLSConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// Apply the client config
|
// Apply the client config
|
||||||
if result.Client == nil && b.Client != nil {
|
if result.Client == nil && b.Client != nil {
|
||||||
client := *b.Client
|
client := *b.Client
|
||||||
|
@ -808,6 +807,32 @@ func (a *ClientConfig) Merge(b *ClientConfig) *ClientConfig {
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge is used to merge two TLS configs together
|
||||||
|
func (t *TLSConfig) Merge(b *TLSConfig) *TLSConfig {
|
||||||
|
result := *t
|
||||||
|
|
||||||
|
if b.EnableHTTP {
|
||||||
|
result.EnableHTTP = true
|
||||||
|
}
|
||||||
|
if b.EnableRPC {
|
||||||
|
result.EnableRPC = true
|
||||||
|
}
|
||||||
|
if b.VerifyServerHostname {
|
||||||
|
result.VerifyServerHostname = true
|
||||||
|
}
|
||||||
|
if b.CAFile != "" {
|
||||||
|
result.CAFile = b.CAFile
|
||||||
|
}
|
||||||
|
if b.CertFile != "" {
|
||||||
|
result.CertFile = b.CertFile
|
||||||
|
}
|
||||||
|
if b.KeyFile != "" {
|
||||||
|
result.KeyFile = b.KeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
// Merge is used to merge two telemetry configs together
|
// Merge is used to merge two telemetry configs together
|
||||||
func (a *Telemetry) Merge(b *Telemetry) *Telemetry {
|
func (a *Telemetry) Merge(b *Telemetry) *Telemetry {
|
||||||
result := *a
|
result := *a
|
||||||
|
|
|
@ -94,12 +94,7 @@ func parseConfig(result *Config, list *ast.ObjectList) error {
|
||||||
"atlas",
|
"atlas",
|
||||||
"consul",
|
"consul",
|
||||||
"vault",
|
"vault",
|
||||||
"http_tls",
|
"tls",
|
||||||
"rpc_tls",
|
|
||||||
"verify_server_hostname",
|
|
||||||
"ca_file",
|
|
||||||
"cert_file",
|
|
||||||
"key_file",
|
|
||||||
"http_api_response_headers",
|
"http_api_response_headers",
|
||||||
}
|
}
|
||||||
if err := checkHCLKeys(list, valid); err != nil {
|
if err := checkHCLKeys(list, valid); err != nil {
|
||||||
|
@ -121,6 +116,7 @@ func parseConfig(result *Config, list *ast.ObjectList) error {
|
||||||
delete(m, "atlas")
|
delete(m, "atlas")
|
||||||
delete(m, "consul")
|
delete(m, "consul")
|
||||||
delete(m, "vault")
|
delete(m, "vault")
|
||||||
|
delete(m, "tls")
|
||||||
delete(m, "http_api_response_headers")
|
delete(m, "http_api_response_headers")
|
||||||
|
|
||||||
// Decode the rest
|
// Decode the rest
|
||||||
|
@ -191,6 +187,13 @@ func parseConfig(result *Config, list *ast.ObjectList) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse the TLS config
|
||||||
|
if o := list.Filter("tls"); len(o.Items) > 0 {
|
||||||
|
if err := parseTLSConfig(&result.TLSConfig, o); err != nil {
|
||||||
|
return multierror.Prefix(err, "tls ->")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Parse out http_api_response_headers fields. These are in HCL as a list so
|
// Parse out http_api_response_headers fields. These are in HCL as a list so
|
||||||
// we need to iterate over them and merge them.
|
// we need to iterate over them and merge them.
|
||||||
if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 {
|
if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 {
|
||||||
|
@ -649,6 +652,41 @@ func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTLSConfig(result **TLSConfig, list *ast.ObjectList) error {
|
||||||
|
list = list.Elem()
|
||||||
|
if len(list.Items) > 1 {
|
||||||
|
return fmt.Errorf("only one 'tls' block allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the TLS object
|
||||||
|
listVal := list.Items[0].Val
|
||||||
|
|
||||||
|
valid := []string{
|
||||||
|
"http_tls",
|
||||||
|
"rpc_tls",
|
||||||
|
"verify_server_hostname",
|
||||||
|
"ca_file",
|
||||||
|
"cert_file",
|
||||||
|
"key_file",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := checkHCLKeys(listVal, valid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var m map[string]interface{}
|
||||||
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var tlsConfig TLSConfig
|
||||||
|
if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*result = &tlsConfig
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error {
|
func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error {
|
||||||
list = list.Elem()
|
list = list.Elem()
|
||||||
if len(list.Items) > 1 {
|
if len(list.Items) > 1 {
|
||||||
|
|
|
@ -136,6 +136,14 @@ func TestConfig_Parse(t *testing.T) {
|
||||||
TaskTokenTTL: "1s",
|
TaskTokenTTL: "1s",
|
||||||
Token: "12345",
|
Token: "12345",
|
||||||
},
|
},
|
||||||
|
TLSConfig: &TLSConfig{
|
||||||
|
EnableHTTP: true,
|
||||||
|
EnableRPC: true,
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
CAFile: "foo",
|
||||||
|
CertFile: "bar",
|
||||||
|
KeyFile: "pipe",
|
||||||
|
},
|
||||||
HTTPAPIResponseHeaders: map[string]string{
|
HTTPAPIResponseHeaders: map[string]string{
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
},
|
},
|
||||||
|
|
|
@ -54,14 +54,14 @@ func NewHTTPServer(agent *Agent, config *Config, logOutput io.Writer) (*HTTPServ
|
||||||
return nil, fmt.Errorf("failed to start HTTP listener: %v", err)
|
return nil, fmt.Errorf("failed to start HTTP listener: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.HttpTLS {
|
if config.TLSConfig.EnableHTTP {
|
||||||
tlsConf := &tlsutil.Config{
|
tlsConf := &tlsutil.Config{
|
||||||
VerifyIncoming: true,
|
VerifyIncoming: false,
|
||||||
VerifyOutgoing: true,
|
VerifyOutgoing: true,
|
||||||
VerifyServerHostname: config.VerifyServerHostname,
|
VerifyServerHostname: config.TLSConfig.VerifyServerHostname,
|
||||||
CAFile: config.CAFile,
|
CAFile: config.TLSConfig.CAFile,
|
||||||
CertFile: config.CertFile,
|
CertFile: config.TLSConfig.CertFile,
|
||||||
KeyFile: config.KeyFile,
|
KeyFile: config.TLSConfig.KeyFile,
|
||||||
ServerName: config.NodeName,
|
ServerName: config.NodeName,
|
||||||
}
|
}
|
||||||
tlsConfig, err := tlsConf.IncomingTLSConfig()
|
tlsConfig, err := tlsConf.IncomingTLSConfig()
|
||||||
|
|
|
@ -193,6 +193,7 @@ type Config struct {
|
||||||
// place, and a small jitter is applied to avoid a thundering herd.
|
// place, and a small jitter is applied to avoid a thundering herd.
|
||||||
RPCHoldTimeout time.Duration
|
RPCHoldTimeout time.Duration
|
||||||
|
|
||||||
|
// Enable TLS for incoming RPC calls from Nomad clients
|
||||||
RpcTLS bool
|
RpcTLS bool
|
||||||
|
|
||||||
// VerifyServerHostname is used to enable hostname verification of servers. This
|
// VerifyServerHostname is used to enable hostname verification of servers. This
|
||||||
|
@ -287,6 +288,7 @@ func DefaultConfig() *Config {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tlsConfig returns a TLSUtil Config based on the server configuration
|
||||||
func (c *Config) tlsConfig() *tlsutil.Config {
|
func (c *Config) tlsConfig() *tlsutil.Config {
|
||||||
tlsConf := &tlsutil.Config{
|
tlsConf := &tlsutil.Config{
|
||||||
VerifyIncoming: true,
|
VerifyIncoming: true,
|
||||||
|
|
|
@ -234,7 +234,6 @@ func NewServer(config *Config, consulSyncer *consul.Syncer, logger *log.Logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the RPC layer
|
// Initialize the RPC layer
|
||||||
// TODO: TLS...
|
|
||||||
if err := s.setupRPC(tlsWrap); err != nil {
|
if err := s.setupRPC(tlsWrap); err != nil {
|
||||||
s.Shutdown()
|
s.Shutdown()
|
||||||
s.logger.Printf("[ERR] nomad: failed to start RPC layer: %s", err)
|
s.logger.Printf("[ERR] nomad: failed to start RPC layer: %s", err)
|
||||||
|
|
|
@ -9,8 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Wrapper is a variant of DCWrapper, where the DC is provided as
|
// Wrapper wraps a connection and enables TLS on it.
|
||||||
// a constant value. This is usually done by currying DCWrapper.
|
|
||||||
type Wrapper func(conn net.Conn) (net.Conn, error)
|
type Wrapper func(conn net.Conn) (net.Conn, error)
|
||||||
|
|
||||||
// Config used to create tls.Config
|
// Config used to create tls.Config
|
||||||
|
@ -126,7 +125,7 @@ func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
|
||||||
return tlsConfig, nil
|
return tlsConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutgoingTLSWrapper returns a a DCWrapper based on the OutgoingTLS
|
// OutgoingTLSWrapper returns a a Wrapper based on the OutgoingTLS
|
||||||
// configuration. If hostname verification is on, the wrapper
|
// configuration. If hostname verification is on, the wrapper
|
||||||
// will properly generate the dynamic server name for verification.
|
// will properly generate the dynamic server name for verification.
|
||||||
func (c *Config) OutgoingTLSWrapper() (Wrapper, error) {
|
func (c *Config) OutgoingTLSWrapper() (Wrapper, error) {
|
||||||
|
|
|
@ -256,6 +256,7 @@ func TestConfig_outgoingWrapper_OK(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfig_outgoingWrapper_BadCert(t *testing.T) {
|
func TestConfig_outgoingWrapper_BadCert(t *testing.T) {
|
||||||
|
// TODO this test is currently hanging, need to investigate more.
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
config := &Config{
|
config := &Config{
|
||||||
CAFile: "../test/ca/root.cer",
|
CAFile: "../test/ca/root.cer",
|
||||||
|
|
Loading…
Reference in a new issue