Skip to content

Commit

Permalink
Enabling TLS on cli
Browse files Browse the repository at this point in the history
  • Loading branch information
diptanu committed Oct 25, 2016
1 parent e115f83 commit 0e6e5b3
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 12 deletions.
106 changes: 106 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package api
import (
"bytes"
"compress/gzip"
"crypto/tls"
"encoding/json"
"fmt"
"io"
Expand All @@ -14,6 +15,7 @@ import (
"time"

"github.com/hashicorp/go-cleanhttp"
rootcerts "github.com/hashicorp/go-rootcerts"
)

// QueryOptions are used to parameterize a query
Expand Down Expand Up @@ -102,14 +104,51 @@ type Config struct {
// WaitTime limits how long a Watch will block. If not provided,
// the agent default values will be used.
WaitTime time.Duration

// TLSConfig provides the various TLS related configurations for the http
// client
TLSConfig *TLSConfig
}

// TLSConfig contains the parameters needed to configure TLS on the HTTP client
// used to communicate with Nomad.
type TLSConfig struct {
// CACert is the path to a PEM-encoded CA cert file to use to verify the
// Nomad server SSL certificate.
CACert string

// CAPath is the path to a directory of PEM-encoded CA cert files to verify
// the Nomad server SSL certificate.
CAPath string

// ClientCert is the path to the certificate for Nomad communication
ClientCert string

// ClientKey is the path to the private key for Nomad communication
ClientKey string

// TLSServerName, if set, is used to set the SNI host when connecting via
// TLS.
TLSServerName string

// Insecure enables or disables SSL verification
Insecure bool
}

// DefaultConfig returns a default configuration for the client
func DefaultConfig() *Config {
config := &Config{
Address: "http://127.0.0.1:4646",
HttpClient: cleanhttp.DefaultClient(),
TLSConfig: &TLSConfig{},
}
config.HttpClient.Timeout = time.Second * 60
transport := config.HttpClient.Transport.(*http.Transport)
transport.TLSHandshakeTimeout = 10 * time.Second
transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}

if addr := os.Getenv("NOMAD_ADDR"); addr != "" {
config.Address = addr
}
Expand All @@ -128,9 +167,71 @@ func DefaultConfig() *Config {
Password: password,
}
}

// Read TLS specific env vars
if v := os.Getenv("NOMAD_CACERT"); v != "" {
config.TLSConfig.CACert = v
}
if v := os.Getenv("NOMAD_CAPATH"); v != "" {
config.TLSConfig.CAPath = v
}
if v := os.Getenv("NOMAD_CLIENT_CERT"); v != "" {
config.TLSConfig.ClientCert = v
}
if v := os.Getenv("NOMAD_CLIENT_KEY"); v != "" {
config.TLSConfig.ClientKey = v
}
if v := os.Getenv("NOMAD_SKIP_VERIFY"); v != "" {
if insecure, err := strconv.ParseBool(v); err == nil {
config.TLSConfig.Insecure = insecure
}
}

return config
}

// ConfigureTLS applies a set of TLS configurations to the the HTTP client.
func (c *Config) ConfigureTLS() error {
if c.HttpClient == nil {
return fmt.Errorf("config HTTP Client must be set")
}

var clientCert tls.Certificate
foundClientCert := false
if c.TLSConfig.ClientCert != "" || c.TLSConfig.ClientKey != "" {
if c.TLSConfig.ClientCert != "" && c.TLSConfig.ClientKey != "" {
var err error
clientCert, err = tls.LoadX509KeyPair(c.TLSConfig.ClientCert, c.TLSConfig.ClientKey)
if err != nil {
return err
}
foundClientCert = true
} else if c.TLSConfig.ClientCert != "" || c.TLSConfig.ClientKey != "" {
return fmt.Errorf("Both client cert and client key must be provided")
}
}

clientTLSConfig := c.HttpClient.Transport.(*http.Transport).TLSClientConfig
rootConfig := &rootcerts.Config{
CAFile: c.TLSConfig.CACert,
CAPath: c.TLSConfig.CAPath,
}
if err := rootcerts.ConfigureTLS(clientTLSConfig, rootConfig); err != nil {
return err
}

clientTLSConfig.InsecureSkipVerify = c.TLSConfig.Insecure

if foundClientCert {
clientTLSConfig.Certificates = []tls.Certificate{clientCert}
}
if c.TLSConfig.TLSServerName != "" {
clientTLSConfig.ServerName = c.TLSConfig.TLSServerName
}

return nil
}

// Client provides a client to the Nomad API
type Client struct {
config Config
Expand All @@ -151,6 +252,11 @@ func NewClient(config *Config) (*Client, error) {
config.HttpClient = defConfig.HttpClient
}

// Configure the TLS cofigurations
if err := config.ConfigureTLS(); err != nil {
return nil, err
}

client := &Client{
config: *config,
}
Expand Down
12 changes: 6 additions & 6 deletions client/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,30 +135,30 @@ type Config struct {
PublishAllocationMetrics bool

// HttpTLS enables TLS for the HTTP endpoints on the clients.
HttpTLS bool `mapstructure:"http_tls"`
HttpTLS bool

// RpcTLS enables TLS for the outgoing TLS connections to the Nomad servers.
RpcTLS bool `mapstructure:"rpc_tls"`
RpcTLS bool

// 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"`
VerifyServerHostname bool

// 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"`
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 `mapstructure:"cert_file"`
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 `mapstructure:"key_file"`
KeyFile string
}

func (c *Config) Copy() *Config {
Expand Down
4 changes: 2 additions & 2 deletions command/agent/config-test-fixtures/basic.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ vault {
tls_skip_verify = true
}
tls {
enable_http = true
enable_rpc = true
http = true
rpc = true
verify_server_hostname = true
ca_file = "foo"
cert_file = "bar"
Expand Down
4 changes: 2 additions & 2 deletions command/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ type AtlasConfig struct {
type TLSConfig struct {

// EnableHTTP enabled TLS for http traffic to the Nomad server and clients
EnableHTTP bool `mapstructure:"enable_http"`
EnableHTTP bool `mapstructure:"http"`

// EnableRPC enables TLS for RPC and Raft traffic to the Nomad servers
EnableRPC bool `mapstructure:"enable_rpc"`
EnableRPC bool `mapstructure:"rpc"`

// VerifyServerHostname is used to enable hostname verification of servers. This
// ensures that the certificate presented is valid for server.<datacenter>.<domain>.
Expand Down
4 changes: 2 additions & 2 deletions command/agent/config_parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,8 +662,8 @@ func parseTLSConfig(result **TLSConfig, list *ast.ObjectList) error {
listVal := list.Items[0].Val

valid := []string{
"enable_http",
"enable_rpc",
"http",
"rpc",
"verify_server_hostname",
"ca_file",
"cert_file",
Expand Down
1 change: 1 addition & 0 deletions command/agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func NewHTTPServer(agent *Agent, config *Config, logOutput io.Writer) (*HTTPServ
return nil, fmt.Errorf("failed to start HTTP listener: %v", err)
}

// If TLS is enabled, wrap the listener with a TLS listener
if config.TLSConfig.EnableHTTP {
tlsConf := &tlsutil.Config{
VerifyIncoming: false,
Expand Down
50 changes: 50 additions & 0 deletions command/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ type Meta struct {

// The region to send API requests
region string

caCert string
caPath string
clientCert string
clientKey string
insecure bool
}

// FlagSet returns a FlagSet with the common flags that every
Expand All @@ -61,6 +67,13 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
f.StringVar(&m.flagAddress, "address", "", "")
f.StringVar(&m.region, "region", "", "")
f.BoolVar(&m.noColor, "no-color", false, "")
f.StringVar(&m.caCert, "ca-cert", "", "")
f.StringVar(&m.caPath, "ca-path", "", "")
f.StringVar(&m.clientCert, "client-cert", "", "")
f.StringVar(&m.clientKey, "client-key", "", "")
f.BoolVar(&m.insecure, "insecure", false, "")
f.BoolVar(&m.insecure, "tls-skip-verify", false, "")

}

// Create an io.Writer that writes to our UI properly for errors.
Expand Down Expand Up @@ -95,6 +108,18 @@ func (m *Meta) Client() (*api.Client, error) {
if m.region != "" {
config.Region = m.region
}
// If we need custom TLS configuration, then set it
if m.caCert != "" || m.caPath != "" || m.clientCert != "" || m.clientKey != "" || m.insecure {
t := &api.TLSConfig{
CACert: m.caCert,
CAPath: m.caPath,
ClientCert: m.clientCert,
ClientKey: m.clientKey,
Insecure: m.insecure,
}
config.TLSConfig = t
}

return api.NewClient(config)
}

Expand All @@ -121,6 +146,31 @@ func generalOptionsUsage() string {
-no-color
Disables colored command output.
-ca-cert=<path>
Path to a PEM encoded CA cert file to use to verify the
Nomad server SSL certificate. Overrides the NOMAD_CACERT
environment variable if set.
-ca-path=<path>
Path to a directory of PEM encoded CA cert files to verify
the Nomad server SSL certificate. If both -ca-cert and
-ca-path are specified, -ca-cert is used. Overrides the
NOMAD_CAPATH environment variable if set.
-client-cert=<path>
Path to a PEM encoded client certificate for TLS authentication
to the Nomad server. Must also specify -client-key. Overrides
the NOMAD_CLIENT_CERT environment variable if set.
-client-key=<path>
Path to an unencrypted PEM encoded private key matching the
client certificate from -client-cert. Overrides the
NOMAD_CLIENT_KEY environment variable if set.
-tls-skip-verify
Do not verify TLS certificate. This is highly not recommended. Verification
will also be skipped if NOMAD_SKIP_VERIFY is set.
`
return strings.TrimSpace(helpText)
}

0 comments on commit 0e6e5b3

Please sign in to comment.