Merge pull request #1853 from hashicorp/f-rpc-http-tls
TLS support for http and RPC
This commit is contained in:
commit
50ca5e1e9d
106
api/api.go
106
api/api.go
|
@ -3,6 +3,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/go-cleanhttp"
|
"github.com/hashicorp/go-cleanhttp"
|
||||||
|
rootcerts "github.com/hashicorp/go-rootcerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryOptions are used to parameterize a query
|
// QueryOptions are used to parameterize a query
|
||||||
|
@ -102,6 +104,35 @@ type Config struct {
|
||||||
// WaitTime limits how long a Watch will block. If not provided,
|
// WaitTime limits how long a Watch will block. If not provided,
|
||||||
// the agent default values will be used.
|
// the agent default values will be used.
|
||||||
WaitTime time.Duration
|
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
|
// DefaultConfig returns a default configuration for the client
|
||||||
|
@ -109,7 +140,15 @@ func DefaultConfig() *Config {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
Address: "http://127.0.0.1:4646",
|
Address: "http://127.0.0.1:4646",
|
||||||
HttpClient: cleanhttp.DefaultClient(),
|
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 != "" {
|
if addr := os.Getenv("NOMAD_ADDR"); addr != "" {
|
||||||
config.Address = addr
|
config.Address = addr
|
||||||
}
|
}
|
||||||
|
@ -128,9 +167,71 @@ func DefaultConfig() *Config {
|
||||||
Password: password,
|
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
|
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
|
// Client provides a client to the Nomad API
|
||||||
type Client struct {
|
type Client struct {
|
||||||
config Config
|
config Config
|
||||||
|
@ -151,6 +252,11 @@ func NewClient(config *Config) (*Client, error) {
|
||||||
config.HttpClient = defConfig.HttpClient
|
config.HttpClient = defConfig.HttpClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure the TLS cofigurations
|
||||||
|
if err := config.ConfigureTLS(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
client := &Client{
|
client := &Client{
|
||||||
config: *config,
|
config: *config,
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/hashicorp/nomad/client/stats"
|
"github.com/hashicorp/nomad/client/stats"
|
||||||
"github.com/hashicorp/nomad/client/vaultclient"
|
"github.com/hashicorp/nomad/client/vaultclient"
|
||||||
"github.com/hashicorp/nomad/command/agent/consul"
|
"github.com/hashicorp/nomad/command/agent/consul"
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/nomad/nomad"
|
"github.com/hashicorp/nomad/nomad"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
vaultapi "github.com/hashicorp/vault/api"
|
vaultapi "github.com/hashicorp/vault/api"
|
||||||
|
@ -164,12 +165,22 @@ var (
|
||||||
|
|
||||||
// NewClient is used to create a new client from the given configuration
|
// NewClient is used to create a new client from the given configuration
|
||||||
func NewClient(cfg *config.Config, consulSyncer *consul.Syncer, logger *log.Logger) (*Client, error) {
|
func NewClient(cfg *config.Config, consulSyncer *consul.Syncer, logger *log.Logger) (*Client, error) {
|
||||||
|
// Create the tls wrapper
|
||||||
|
var tlsWrap tlsutil.Wrapper
|
||||||
|
if cfg.TLSConfig.EnableRPC {
|
||||||
|
tw, err := cfg.TLSConfiguration().OutgoingTLSWrapper()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsWrap = tw
|
||||||
|
}
|
||||||
|
|
||||||
// Create the client
|
// Create the client
|
||||||
c := &Client{
|
c := &Client{
|
||||||
config: cfg,
|
config: cfg,
|
||||||
consulSyncer: consulSyncer,
|
consulSyncer: consulSyncer,
|
||||||
start: time.Now(),
|
start: time.Now(),
|
||||||
connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, nil),
|
connPool: nomad.NewPool(cfg.LogOutput, clientRPCCache, clientMaxStreams, tlsWrap),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
hostStatsCollector: stats.NewHostStatsCollector(),
|
hostStatsCollector: stats.NewHostStatsCollector(),
|
||||||
allocs: make(map[string]*AllocRunner),
|
allocs: make(map[string]*AllocRunner),
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||||
)
|
)
|
||||||
|
@ -132,6 +133,9 @@ type Config struct {
|
||||||
// PublishAllocationMetrics determines whether nomad is going to publish
|
// PublishAllocationMetrics determines whether nomad is going to publish
|
||||||
// allocation metrics to remote Telemetry sinks
|
// allocation metrics to remote Telemetry sinks
|
||||||
PublishAllocationMetrics bool
|
PublishAllocationMetrics bool
|
||||||
|
|
||||||
|
// TLSConfig holds various TLS related configurations
|
||||||
|
TLSConfig *config.TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) Copy() *Config {
|
func (c *Config) Copy() *Config {
|
||||||
|
@ -226,3 +230,17 @@ 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) TLSConfiguration() *tlsutil.Config {
|
||||||
|
tlsConf := &tlsutil.Config{
|
||||||
|
VerifyIncoming: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: c.TLSConfig.VerifyServerHostname,
|
||||||
|
CAFile: c.TLSConfig.CAFile,
|
||||||
|
CertFile: c.TLSConfig.CertFile,
|
||||||
|
KeyFile: c.TLSConfig.KeyFile,
|
||||||
|
ServerName: c.Node.Name,
|
||||||
|
}
|
||||||
|
return tlsConf
|
||||||
|
}
|
||||||
|
|
|
@ -244,6 +244,9 @@ func (a *Agent) serverConfig() (*nomad.Config, error) {
|
||||||
conf.ConsulConfig = a.config.Consul
|
conf.ConsulConfig = a.config.Consul
|
||||||
conf.VaultConfig = a.config.Vault
|
conf.VaultConfig = a.config.Vault
|
||||||
|
|
||||||
|
// Set the TLS config
|
||||||
|
conf.TLSConfig = a.config.TLSConfig
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,6 +360,10 @@ func (a *Agent) clientConfig() (*clientconfig.Config, error) {
|
||||||
conf.StatsCollectionInterval = a.config.Telemetry.collectionInterval
|
conf.StatsCollectionInterval = a.config.Telemetry.collectionInterval
|
||||||
conf.PublishNodeMetrics = a.config.Telemetry.PublishNodeMetrics
|
conf.PublishNodeMetrics = a.config.Telemetry.PublishNodeMetrics
|
||||||
conf.PublishAllocationMetrics = a.config.Telemetry.PublishAllocationMetrics
|
conf.PublishAllocationMetrics = a.config.Telemetry.PublishAllocationMetrics
|
||||||
|
|
||||||
|
// Set the TLS related configs
|
||||||
|
conf.TLSConfig = a.config.TLSConfig
|
||||||
|
|
||||||
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 = true
|
||||||
|
rpc = true
|
||||||
|
verify_server_hostname = true
|
||||||
|
ca_file = "foo"
|
||||||
|
cert_file = "bar"
|
||||||
|
key_file = "pipe"
|
||||||
|
}
|
||||||
|
|
|
@ -112,6 +112,10 @@ 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:"-"`
|
||||||
|
|
||||||
|
// TLSConfig provides TLS related configuration for the Nomad server and
|
||||||
|
// client
|
||||||
|
TLSConfig *config.TLSConfig `mapstructure:"tls"`
|
||||||
|
|
||||||
// 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
|
||||||
HTTPAPIResponseHeaders map[string]string `mapstructure:"http_api_response_headers"`
|
HTTPAPIResponseHeaders map[string]string `mapstructure:"http_api_response_headers"`
|
||||||
|
@ -486,6 +490,7 @@ func DefaultConfig() *Config {
|
||||||
CollectionInterval: "1s",
|
CollectionInterval: "1s",
|
||||||
collectionInterval: 1 * time.Second,
|
collectionInterval: 1 * time.Second,
|
||||||
},
|
},
|
||||||
|
TLSConfig: &config.TLSConfig{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,6 +571,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
|
||||||
|
|
|
@ -94,6 +94,7 @@ func parseConfig(result *Config, list *ast.ObjectList) error {
|
||||||
"atlas",
|
"atlas",
|
||||||
"consul",
|
"consul",
|
||||||
"vault",
|
"vault",
|
||||||
|
"tls",
|
||||||
"http_api_response_headers",
|
"http_api_response_headers",
|
||||||
}
|
}
|
||||||
if err := checkHCLKeys(list, valid); err != nil {
|
if err := checkHCLKeys(list, valid); err != nil {
|
||||||
|
@ -115,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
|
||||||
|
@ -185,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 {
|
||||||
|
@ -643,6 +652,41 @@ func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseTLSConfig(result **config.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",
|
||||||
|
"rpc",
|
||||||
|
"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 config.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": "*",
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,7 @@ package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -13,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/ugorji/go/codec"
|
"github.com/ugorji/go/codec"
|
||||||
)
|
)
|
||||||
|
@ -52,6 +54,24 @@ 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 TLS is enabled, wrap the listener with a TLS listener
|
||||||
|
if config.TLSConfig.EnableHTTP {
|
||||||
|
tlsConf := &tlsutil.Config{
|
||||||
|
VerifyIncoming: false,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: config.TLSConfig.VerifyServerHostname,
|
||||||
|
CAFile: config.TLSConfig.CAFile,
|
||||||
|
CertFile: config.TLSConfig.CertFile,
|
||||||
|
KeyFile: config.TLSConfig.KeyFile,
|
||||||
|
ServerName: config.NodeName,
|
||||||
|
}
|
||||||
|
tlsConfig, err := tlsConf.IncomingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, tlsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
// Create the mux
|
// Create the mux
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
@ -91,6 +111,23 @@ func newScadaHttp(agent *Agent, list net.Listener) *HTTPServer {
|
||||||
return srv
|
return srv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||||
|
// connections. It's used by NewHttpServer so
|
||||||
|
// dead TCP connections eventually go away.
|
||||||
|
type tcpKeepAliveListener struct {
|
||||||
|
*net.TCPListener
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||||
|
tc, err := ln.AcceptTCP()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tc.SetKeepAlive(true)
|
||||||
|
tc.SetKeepAlivePeriod(30 * time.Second)
|
||||||
|
return tc, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Shutdown is used to shutdown the HTTP server
|
// Shutdown is used to shutdown the HTTP server
|
||||||
func (s *HTTPServer) Shutdown() {
|
func (s *HTTPServer) Shutdown() {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
|
|
|
@ -46,6 +46,12 @@ type Meta struct {
|
||||||
|
|
||||||
// The region to send API requests
|
// The region to send API requests
|
||||||
region string
|
region string
|
||||||
|
|
||||||
|
caCert string
|
||||||
|
caPath string
|
||||||
|
clientCert string
|
||||||
|
clientKey string
|
||||||
|
insecure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlagSet returns a FlagSet with the common flags that every
|
// FlagSet returns a FlagSet with the common flags that every
|
||||||
|
@ -61,6 +67,13 @@ func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
|
||||||
f.StringVar(&m.flagAddress, "address", "", "")
|
f.StringVar(&m.flagAddress, "address", "", "")
|
||||||
f.StringVar(&m.region, "region", "", "")
|
f.StringVar(&m.region, "region", "", "")
|
||||||
f.BoolVar(&m.noColor, "no-color", false, "")
|
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.
|
// Create an io.Writer that writes to our UI properly for errors.
|
||||||
|
@ -95,6 +108,18 @@ func (m *Meta) Client() (*api.Client, error) {
|
||||||
if m.region != "" {
|
if m.region != "" {
|
||||||
config.Region = 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)
|
return api.NewClient(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +146,31 @@ func generalOptionsUsage() string {
|
||||||
|
|
||||||
-no-color
|
-no-color
|
||||||
Disables colored command output.
|
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)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
237
helper/tlsutil/config.go
Normal file
237
helper/tlsutil/config.go
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
package tlsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wrapper wraps a connection and enables TLS on it.
|
||||||
|
type Wrapper func(conn net.Conn) (net.Conn, error)
|
||||||
|
|
||||||
|
// Config used to create tls.Config
|
||||||
|
type Config struct {
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// ServerName is used with the TLS certificate to ensure the name we
|
||||||
|
// provide matches the certificate
|
||||||
|
ServerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendCA opens and parses the CA file and adds the certificates to
|
||||||
|
// the provided CertPool.
|
||||||
|
func (c *Config) AppendCA(pool *x509.CertPool) error {
|
||||||
|
if c.CAFile == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
data, err := ioutil.ReadFile(c.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to read CA file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pool.AppendCertsFromPEM(data) {
|
||||||
|
return fmt.Errorf("Failed to parse any CA certificates")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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. It will return a nil config if this configuration should
|
||||||
|
// not use TLS for outgoing connections.
|
||||||
|
func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
|
||||||
|
// If VerifyServerHostname is true, that implies VerifyOutgoing
|
||||||
|
if c.VerifyServerHostname {
|
||||||
|
c.VerifyOutgoing = true
|
||||||
|
}
|
||||||
|
if !c.VerifyOutgoing {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// Create the tlsConfig
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
RootCAs: x509.NewCertPool(),
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}
|
||||||
|
if c.ServerName != "" {
|
||||||
|
tlsConfig.ServerName = c.ServerName
|
||||||
|
tlsConfig.InsecureSkipVerify = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have a CA if VerifyOutgoing is set
|
||||||
|
if c.VerifyOutgoing && c.CAFile == "" {
|
||||||
|
return nil, fmt.Errorf("VerifyOutgoing set, and no CA certificate provided!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the CA cert if any
|
||||||
|
err := c.AppendCA(tlsConfig.RootCAs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutgoingTLSWrapper returns a a Wrapper based on the OutgoingTLS
|
||||||
|
// configuration. If hostname verification is on, the wrapper
|
||||||
|
// will properly generate the dynamic server name for verification.
|
||||||
|
func (c *Config) OutgoingTLSWrapper() (Wrapper, error) {
|
||||||
|
// Get the TLS config
|
||||||
|
tlsConfig, err := c.OutgoingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if TLS is not enabled
|
||||||
|
if tlsConfig == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper := func(conn net.Conn) (net.Conn, error) {
|
||||||
|
return WrapTLSClient(conn, tlsConfig)
|
||||||
|
}
|
||||||
|
return wrapper, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap a net.Conn into a client tls connection, performing any
|
||||||
|
// additional verification as needed.
|
||||||
|
//
|
||||||
|
// As of go 1.3, crypto/tls only supports either doing no certificate
|
||||||
|
// verification, or doing full verification including of the peer's
|
||||||
|
// DNS name. For consul, we want to validate that the certificate is
|
||||||
|
// signed by a known CA, but because consul doesn't use DNS names for
|
||||||
|
// node names, we don't verify the certificate DNS names. Since go 1.3
|
||||||
|
// no longer supports this mode of operation, we have to do it
|
||||||
|
// manually.
|
||||||
|
func WrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
|
||||||
|
var err error
|
||||||
|
var tlsConn *tls.Conn
|
||||||
|
|
||||||
|
tlsConn = tls.Client(conn, tlsConfig)
|
||||||
|
|
||||||
|
// If crypto/tls is doing verification, there's no need to do
|
||||||
|
// our own.
|
||||||
|
if tlsConfig.InsecureSkipVerify == false {
|
||||||
|
return tlsConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tlsConn.Handshake(); err != nil {
|
||||||
|
tlsConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following is lightly-modified from the doFullHandshake
|
||||||
|
// method in crypto/tls's handshake_client.go.
|
||||||
|
opts := x509.VerifyOptions{
|
||||||
|
Roots: tlsConfig.RootCAs,
|
||||||
|
CurrentTime: time.Now(),
|
||||||
|
DNSName: "",
|
||||||
|
Intermediates: x509.NewCertPool(),
|
||||||
|
}
|
||||||
|
|
||||||
|
certs := tlsConn.ConnectionState().PeerCertificates
|
||||||
|
for i, cert := range certs {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opts.Intermediates.AddCert(cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = certs[0].Verify(opts)
|
||||||
|
if err != nil {
|
||||||
|
tlsConn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsConn, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncomingTLSConfig generates a TLS configuration for incoming requests
|
||||||
|
func (c *Config) IncomingTLSConfig() (*tls.Config, error) {
|
||||||
|
// Create the tlsConfig
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ServerName: c.ServerName,
|
||||||
|
ClientCAs: x509.NewCertPool(),
|
||||||
|
ClientAuth: tls.NoClientCert,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the CA cert if any
|
||||||
|
err := c.AppendCA(tlsConfig.ClientCAs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 c.CAFile == "" {
|
||||||
|
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
|
||||||
|
}
|
425
helper/tlsutil/config_test.go
Normal file
425
helper/tlsutil/config_test.go
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
package tlsutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/yamux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig_AppendCA_None(t *testing.T) {
|
||||||
|
conf := &Config{}
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
err := conf.AppendCA(pool)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(pool.Subjects()) != 0 {
|
||||||
|
t.Fatalf("bad: %v", pool.Subjects())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_CACertificate_Valid(t *testing.T) {
|
||||||
|
conf := &Config{
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
}
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
err := conf.AppendCA(pool)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if len(pool.Subjects()) == 0 {
|
||||||
|
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 no config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.ServerName != "" {
|
||||||
|
t.Fatalf("expect no server name verification")
|
||||||
|
}
|
||||||
|
if !tls.InsecureSkipVerify {
|
||||||
|
t.Fatalf("should skip built-in verification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_OutgoingTLS_ServerName(t *testing.T) {
|
||||||
|
conf := &Config{
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
ServerName: "consul.example.com",
|
||||||
|
}
|
||||||
|
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.ServerName != "consul.example.com" {
|
||||||
|
t.Fatalf("expect server name")
|
||||||
|
}
|
||||||
|
if tls.InsecureSkipVerify {
|
||||||
|
t.Fatalf("should not skip built-in verification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_OutgoingTLS_VerifyHostname(t *testing.T) {
|
||||||
|
conf := &Config{
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
ServerName: "foo",
|
||||||
|
}
|
||||||
|
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.ServerName != "foo" {
|
||||||
|
t.Fatalf("expect server name")
|
||||||
|
}
|
||||||
|
if tls.InsecureSkipVerify {
|
||||||
|
t.Fatalf("should not skip built-in 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 skip verification")
|
||||||
|
}
|
||||||
|
if len(tls.Certificates) != 1 {
|
||||||
|
t.Fatalf("expected client cert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startTLSServer(config *Config) (net.Conn, chan error) {
|
||||||
|
errc := make(chan error, 1)
|
||||||
|
|
||||||
|
tlsConfigServer, err := config.IncomingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
errc <- err
|
||||||
|
return nil, errc
|
||||||
|
}
|
||||||
|
|
||||||
|
client, server := net.Pipe()
|
||||||
|
|
||||||
|
// Use yamux to buffer the reads, otherwise it's easy to deadlock
|
||||||
|
muxConf := yamux.DefaultConfig()
|
||||||
|
serverSession, _ := yamux.Server(server, muxConf)
|
||||||
|
clientSession, _ := yamux.Client(client, muxConf)
|
||||||
|
clientConn, _ := clientSession.Open()
|
||||||
|
serverConn, _ := serverSession.Accept()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
tlsServer := tls.Server(serverConn, tlsConfigServer)
|
||||||
|
if err := tlsServer.Handshake(); err != nil {
|
||||||
|
errc <- err
|
||||||
|
}
|
||||||
|
close(errc)
|
||||||
|
// Because net.Pipe() is unbuffered, if both sides
|
||||||
|
// Close() simultaneously, we will deadlock as they
|
||||||
|
// both send an alert and then block. So we make the
|
||||||
|
// server read any data from the client until error or
|
||||||
|
// EOF, which will allow the client to Close(), and
|
||||||
|
// *then* we Close() the server.
|
||||||
|
io.Copy(ioutil.Discard, tlsServer)
|
||||||
|
tlsServer.Close()
|
||||||
|
}()
|
||||||
|
return clientConn, errc
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_outgoingWrapper_OK(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
CAFile: "./test/hostname/CertAuth.crt",
|
||||||
|
CertFile: "./test/hostname/Alice.crt",
|
||||||
|
KeyFile: "./test/hostname/Alice.key",
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
ServerName: "server.dc1.consul",
|
||||||
|
}
|
||||||
|
|
||||||
|
client, errc := startTLSServer(config)
|
||||||
|
if client == nil {
|
||||||
|
t.Fatalf("startTLSServer err: %v", <-errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap, err := config.OutgoingTLSWrapper()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OutgoingTLSWrapper err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsClient, err := wrap(client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("wrapTLS err: %v", err)
|
||||||
|
}
|
||||||
|
defer tlsClient.Close()
|
||||||
|
if err := tlsClient.(*tls.Conn).Handshake(); err != nil {
|
||||||
|
t.Fatalf("write err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = <-errc
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_outgoingWrapper_BadCert(t *testing.T) {
|
||||||
|
// TODO this test is currently hanging, need to investigate more.
|
||||||
|
t.SkipNow()
|
||||||
|
config := &Config{
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
CertFile: "./test/key/ourdomain.cer",
|
||||||
|
KeyFile: "./test/key/ourdomain.key",
|
||||||
|
ServerName: "foo",
|
||||||
|
VerifyServerHostname: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, errc := startTLSServer(config)
|
||||||
|
if client == nil {
|
||||||
|
t.Fatalf("startTLSServer err: %v", <-errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrap, err := config.OutgoingTLSWrapper()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OutgoingTLSWrapper err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsClient, err := wrap(client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("wrapTLS err: %v", err)
|
||||||
|
}
|
||||||
|
defer tlsClient.Close()
|
||||||
|
err = tlsClient.(*tls.Conn).Handshake()
|
||||||
|
|
||||||
|
if _, ok := err.(x509.HostnameError); !ok {
|
||||||
|
t.Fatalf("should get hostname err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-errc
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_wrapTLS_OK(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
CertFile: "./test/key/ourdomain.cer",
|
||||||
|
KeyFile: "./test/key/ourdomain.key",
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, errc := startTLSServer(config)
|
||||||
|
if client == nil {
|
||||||
|
t.Fatalf("startTLSServer err: %v", <-errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig, err := config.OutgoingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OutgoingTLSConfig err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsClient, err := WrapTLSClient(client, clientConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("wrapTLS err: %v", err)
|
||||||
|
} else {
|
||||||
|
tlsClient.Close()
|
||||||
|
}
|
||||||
|
err = <-errc
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfig_wrapTLS_BadCert(t *testing.T) {
|
||||||
|
serverConfig := &Config{
|
||||||
|
CertFile: "./test/key/ssl-cert-snakeoil.pem",
|
||||||
|
KeyFile: "./test/key/ssl-cert-snakeoil.key",
|
||||||
|
}
|
||||||
|
|
||||||
|
client, errc := startTLSServer(serverConfig)
|
||||||
|
if client == nil {
|
||||||
|
t.Fatalf("startTLSServer err: %v", <-errc)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientConfig := &Config{
|
||||||
|
CAFile: "./test/ca/root.cer",
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
clientTLSConfig, err := clientConfig.OutgoingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OutgoingTLSConfig err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsClient, err := WrapTLSClient(client, clientTLSConfig)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("wrapTLS no err")
|
||||||
|
}
|
||||||
|
if tlsClient != nil {
|
||||||
|
t.Fatalf("returned a client")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = <-errc
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
5
helper/tlsutil/test/ca/certindex
Normal file
5
helper/tlsutil/test/ca/certindex
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
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
|
||||||
|
V 150526223338Z 0C unknown /CN=*.testco.internal/ST=California/C=US/emailAddress=test@testco.com/O=TestCo/OU=Beta
|
||||||
|
V 160526220537Z 0D unknown /CN=test.internal/ST=CA/C=US/emailAddress=test@internal.com/O=HashiCorp Test Cert/OU=Dev
|
||||||
|
V 170604185910Z 0E unknown /CN=testco.internal/ST=California/C=US/emailAddress=test@testco.com/O=Hashicorp Test Cert/OU=Beta
|
34
helper/tlsutil/test/ca/myca.conf
Normal file
34
helper/tlsutil/test/ca/myca.conf
Normal file
|
@ -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
|
27
helper/tlsutil/test/ca/privkey.pem
Normal file
27
helper/tlsutil/test/ca/privkey.pem
Normal file
|
@ -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-----
|
28
helper/tlsutil/test/ca/root.cer
Normal file
28
helper/tlsutil/test/ca/root.cer
Normal file
|
@ -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-----
|
1
helper/tlsutil/test/ca/serialfile
Normal file
1
helper/tlsutil/test/ca/serialfile
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0F
|
25
helper/tlsutil/test/hostname/Alice.crt
Normal file
25
helper/tlsutil/test/hostname/Alice.crt
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEOzCCAiWgAwIBAgIRAOpEyvnjEG/Z15f0PrOT7iowCwYJKoZIhvcNAQELMBMx
|
||||||
|
ETAPBgNVBAMTCENlcnRBdXRoMB4XDTE1MDUxMTIyNTMxN1oXDTE3MDUxMTIyNTMx
|
||||||
|
OFowEDEOMAwGA1UEAxMFQWxpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
AoIBAQCyxmMV9V0Cdp2mAXxL6h64cWLQlKsumsQfhZNImea8jLYT7+yyLpeHIF4G
|
||||||
|
7JiushloTnERyTi1wbq9BlU3BVYdX6tqvPXFFwFUXyOkDaSGS3vMCZUYd9PZg0TI
|
||||||
|
pyQK0/6+jSU7x7jDGVUMhJyvmXB9CgKxG0S8WiR6uGB9oWrTeDnXAzN1T4wNE4M+
|
||||||
|
a3P1ToT2k2IDklZ1t5gg6u9EiOAzK7QfpKXrO2MsGyGHhm+tQqNP6LuZv0u2nGW3
|
||||||
|
up+i3beQOvLQV0aeiy7zfR3KkIUCvDnmiPnkm35o6wmqFOXTNIU6VoT/l4WtU85F
|
||||||
|
Ikdtk1gkDLO1iyKiMRbj/hlRqKGxAgMBAAGjgZAwgY0wDgYDVR0PAQH/BAQDAgC4
|
||||||
|
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUgt2Os881
|
||||||
|
V/je/BOaLavjeorhbi4wHwYDVR0jBBgwFoAUB2s4Gdz7ornOiti84HF+W+nwAj8w
|
||||||
|
HAYDVR0RBBUwE4IRc2VydmVyLmRjMS5jb25zdWwwCwYJKoZIhvcNAQELA4ICAQCX
|
||||||
|
Thsbgo1Z5maIyvBJOKX5vQifaSF8kRtX9fZvipvzHCjYxvOHfaTvgtWyxHXCc3tK
|
||||||
|
DyBswsc2MeHiZ5g0KG113lwLrhcSwEsg5yo0eB7tOTQp1rmCiF6DQYs1XyOqD7P8
|
||||||
|
S6clMgJWgpM8Ltw5mYALqDpShv1ND3AOJqENj/0tvdP7Y7cilG+s76HFXRcwKTRw
|
||||||
|
4rVP+Wr+t4WdXeS8cGxboQqGc40L3HNd5cxsbIM1kucfdrPBljWmyM9aiO1Nipm2
|
||||||
|
8dyyir8AFnvoGQ6DPi58jVCCbqosL/GXtVk+IgJ+8eE5T8jvhxBovzxArSSVYIaj
|
||||||
|
ZxYi85ixfLr1DC5mg5CWWB8ZzmjaUwfyQAcL/F3Q11CkqHw1VDoDzvTBWbguBu6X
|
||||||
|
xXexlgOQx4/lr8X1pjbbAZktNTOYDt4dTuhrKPU35zW35wTnSBoPrQ3cpGlRcszE
|
||||||
|
IksZSHi41IQd0zUOGCNZYpPFq8mTwu5ECGHfNvWDH7zEuSkO54tS5Dukxqd8VIQl
|
||||||
|
h9GB2Uyel8tFm4s/Dx9+glKyvsXDJQz3JmFaB2wPyAPZ1KL4GFI5R0LjUVSFJapP
|
||||||
|
TO3Ia24naOu3qYXWQK6jGwaCbTT6tdhgNy8EI0aDmv2AgqOXycutMJXF5UqkDmwY
|
||||||
|
ZqpVdf/TrmBy42pk/C0vpqiy6E4N7WllxhiY2AekkA==
|
||||||
|
-----END CERTIFICATE-----
|
27
helper/tlsutil/test/hostname/Alice.key
Normal file
27
helper/tlsutil/test/hostname/Alice.key
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAssZjFfVdAnadpgF8S+oeuHFi0JSrLprEH4WTSJnmvIy2E+/s
|
||||||
|
si6XhyBeBuyYrrIZaE5xEck4tcG6vQZVNwVWHV+rarz1xRcBVF8jpA2khkt7zAmV
|
||||||
|
GHfT2YNEyKckCtP+vo0lO8e4wxlVDIScr5lwfQoCsRtEvFokerhgfaFq03g51wMz
|
||||||
|
dU+MDRODPmtz9U6E9pNiA5JWdbeYIOrvRIjgMyu0H6Sl6ztjLBshh4ZvrUKjT+i7
|
||||||
|
mb9Ltpxlt7qfot23kDry0FdGnosu830dypCFArw55oj55Jt+aOsJqhTl0zSFOlaE
|
||||||
|
/5eFrVPORSJHbZNYJAyztYsiojEW4/4ZUaihsQIDAQABAoIBAF3C9szZdwKHu38J
|
||||||
|
YGtgSuRpc235yx4SRbJSmECHlyBknEowl2+MSCSysR3okNtuxSyTl3HAm2GYTZw9
|
||||||
|
6guFXPji6EB/AldwDV5213Z/QT698Bu/GtdOYWm/EyA5qQmUzhKabGDCCwEoFBcQ
|
||||||
|
piziyMCLs4W3y4ENtfw3H0REmIZ3s0XQRzuDdFCEMbr0Ij6EhP3hSD6es4PWTeHY
|
||||||
|
LSwoXm0WAxyZudJLhWZaBRxvl+TDY7nVV1jRPQ+ojMJjXfyPo+c2hbbS6luj++qH
|
||||||
|
6qO7fEpr8EXhO8/0/bPUi0ozE1LVy1kXtEwfszesU9r5XeBq7yTCIa7TTJ35Niwf
|
||||||
|
T7Ar9NECgYEA0L77+B/3dtedhsMdyiHpcxV6A77OIHsezh8uJzE+nQEgqvJ88N1W
|
||||||
|
BbF7YByYmaP1/dBrPI7ON52AnDOyo2lM7fVOwr7Ch12tpoa5HFb2WndKt6KokXi/
|
||||||
|
Tk8+zoCCZICCv1mtfIaepRTmxAeyqaFthchAv1nc7ojS1BWeXMLa9usCgYEA2z6N
|
||||||
|
YD8wV44d/qIMaSDVJlusyp8pi9l0ddB591KOYHhJ5RRF1qEd0pshj9sW6pcGGJqf
|
||||||
|
XFHAkEr/ZIACJK65Y+AFcbgzhyqX8Vy9LLYzWtpFP4SpjH19pYDTHaXvWsIjBlNG
|
||||||
|
poxtGYCQ8Uedm8IhtrbUorElQVjPlmRGU14B2tMCgYAPQCTAd/VoZVBI7DBc+CVK
|
||||||
|
FyOW6nW8wcH6ZSTGED720YJFevnNzx3dxJ2y4+PyNZxfMr7i6bv/LC6dOtmuPp80
|
||||||
|
M1vRtoYXxaxOIkGb5G6TJWv8BpIyLpQrcHayN4lPNmRW/oJCOsOUY/aIE9fltLl/
|
||||||
|
sKWqVTJi6vQcMogjVskQiQKBgHcH7f+sLtXKTdSaLDzDW5X4vcZARXEs/YKdTiqN
|
||||||
|
wsjzZcMej5AoZyWZnc4Zd8ajeebPw+d+Zxqv7RqmOQOrbPGhhbMo+6jN4jJjVD27
|
||||||
|
KgSQbno+z0J8O0QovfXhyiKvNg7QFZKEuRLYb1jftd0DuAQYHTe7D2v8CLAw/tFy
|
||||||
|
P3WLAoGABcEQEDUWqxfFFCed4mYSoOHvD44YMIzeMMOHXRnGGWug0WkULxzUV1L4
|
||||||
|
fTFPCqo6xsn/F3i7xRFpIWXlOzjZKHvw16ZpeZBNcdPjyk7XifhafJYLuknRe1fZ
|
||||||
|
lzLjhmvizTpd9GQIUS+39aGwGE9JI3H0NAdNA4pvEdKlPhJnG5U=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
29
helper/tlsutil/test/hostname/CertAuth.crt
Normal file
29
helper/tlsutil/test/hostname/CertAuth.crt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFADCCAuqgAwIBAgIBATALBgkqhkiG9w0BAQswEzERMA8GA1UEAxMIQ2VydEF1
|
||||||
|
dGgwHhcNMTUwNTExMjI0NjQzWhcNMjUwNTExMjI0NjU0WjATMREwDwYDVQQDEwhD
|
||||||
|
ZXJ0QXV0aDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALcMByyynHsA
|
||||||
|
+K4PJwo5+XHygaEZAhPGvHiKQK2Cbc9NDm0ZTzx0rA/dRTZlvouhDyzcJHm+6R1F
|
||||||
|
j6zQv7iaSC3qQtJiPnPsfZ+/0XhFZ3fQWMnfDiGbZpF1kJF01ofB6vnsuocFC0zG
|
||||||
|
aGC+SZiLAzs+QMP3Bebw1elCBIeoN+8NWnRYmLsYIaYGJGBSbNo/lCpLTuinofUn
|
||||||
|
L3ehWEGv1INwpHnSVeN0Ml2GFe23d7PUlj/wNIHgUdpUR+KEJxIP3klwtsI3QpSH
|
||||||
|
c4VjWdf4aIcka6K3IFuw+K0PUh3xAAPnMpAQOtCZk0AhF5rlvUbevC6jADxpKxLp
|
||||||
|
OONmvCTer4LtyNURAoBH52vbK0r/DNcTpPEFV0IP66nXUFgkk0mRKsu8HTb4IOkC
|
||||||
|
X3K4mp18EiWUUtrHZAnNct0iIniDBqKK0yhSNhztG6VakVt/1WdQY9Ey3mNtxN1O
|
||||||
|
thqWFKdpKUzPKYC3P6PfVpiE7+VbWTLLXba+8BPe8BxWPsVkjJqGSGnCte4COusz
|
||||||
|
M8/7bbTgifwJfsepwFtZG53tvwjWlO46Exl30VoDNTaIGvs1fO0GqJlh2A7FN5F2
|
||||||
|
S1rS5VYHtPK8QdmUSvyq+7JDBc1HNT5I2zsIQbNcLwDTZ5EsbU6QR7NHDJKxjv/w
|
||||||
|
bs3eTXJSSNcFD74wRU10pXjgE5wOFu9TAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIA
|
||||||
|
BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQHazgZ3Puiuc6K2LzgcX5b6fAC
|
||||||
|
PzAfBgNVHSMEGDAWgBQHazgZ3Puiuc6K2LzgcX5b6fACPzALBgkqhkiG9w0BAQsD
|
||||||
|
ggIBAEmeNrSUhpHg1I8dtfqu9hCU/6IZThjtcFA+QcPkkMa+Z1k0SOtsgW8MdlcA
|
||||||
|
gCf5g5yQZ0DdpWM9nDB6xDIhQdccm91idHgf8wmpEHUj0an4uyn2ESCt8eqrAWf7
|
||||||
|
AClYORCASTYfguJCxcfvwtI1uqaOeCxSOdmFay79UVitVsWeonbCRGsVgBDifJxw
|
||||||
|
G2oCQqoYAmXPM4J6syk5GHhB1O9MMq+g1+hOx9s+XHyTui9FL4V+IUO1ygVqEQB5
|
||||||
|
PSiRBvcIsajSGVao+vK0gf2XfcXzqr3y3NhBky9rFMp1g+ykb2yWekV4WiROJlCj
|
||||||
|
TsWwWZDRyjiGahDbho/XW8JciouHZhJdjhmO31rqW3HdFviCTdXMiGk3GQIzz/Jg
|
||||||
|
P+enOaHXoY9lcxzDvY9z1BysWBgNvNrMnVge/fLP9o+a0a0PRIIVl8T0Ef3zeg1O
|
||||||
|
CLCSy/1Vae5Tx63ZTFvGFdOSusYkG9rlAUHXZE364JRCKzM9Bz0bM+t+LaO0MaEb
|
||||||
|
YoxcXEPU+gB2IvmARpInN3oHexR6ekuYHVTRGdWrdmuHFzc7eFwygRqTFdoCCU+G
|
||||||
|
QZEkd+lOEyv0zvQqYg+Jp0AEGz2B2zB53uBVECtn0EqrSdPtRzUBSByXVs6QhSXn
|
||||||
|
eVmy+z3U3MecP63X6oSPXekqSyZFuegXpNNuHkjNoL4ep2ix
|
||||||
|
-----END CERTIFICATE-----
|
23
helper/tlsutil/test/key/ourdomain.cer
Normal file
23
helper/tlsutil/test/key/ourdomain.cer
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDwzCCAqugAwIBAgIBDjANBgkqhkiG9w0BAQUFADCBmDELMAkGA1UEBhMCVVMx
|
||||||
|
CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRwwGgYDVQQKExNI
|
||||||
|
YXNoaUNvcnAgVGVzdCBDZXJ0MQwwCgYDVQQLEwNEZXYxFjAUBgNVBAMTDXRlc3Qu
|
||||||
|
aW50ZXJuYWwxIDAeBgkqhkiG9w0BCQEWEXRlc3RAaW50ZXJuYWwuY29tMB4XDTE2
|
||||||
|
MDYwNDE4NTkxMFoXDTE3MDYwNDE4NTkxMFowgYkxGDAWBgNVBAMTD3Rlc3Rjby5p
|
||||||
|
bnRlcm5hbDETMBEGA1UECBMKQ2FsaWZvcm5pYTELMAkGA1UEBhMCVVMxHjAcBgkq
|
||||||
|
hkiG9w0BCQEWD3Rlc3RAdGVzdGNvLmNvbTEcMBoGA1UEChMTSGFzaGljb3JwIFRl
|
||||||
|
c3QgQ2VydDENMAsGA1UECxMEQmV0YTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
|
||||||
|
gYEAzNCiwaIhJ26tqXzmwB+oJzstp41X8ygpsywCOzEuHgD+Dck2CLOhlJHmqO3b
|
||||||
|
sHfv87g3HtdJaF1tO1PQqdHxpJS3vyogUuP8iy6rHgkGtExWgsRxILtbpfzyG1Rt
|
||||||
|
TgO4pg7uOlCe1p5oDNnHj1YTgmhtRpB0x054DdSvjKVHY5UCAwEAAaOBqDCBpTAJ
|
||||||
|
BgNVHRMEAjAAMB0GA1UdDgQWBBTHULSXbpeHHFM+lq/HYq8m38a00TAfBgNVHSME
|
||||||
|
GDAWgBSj+es5+q9t57ZWSVUogWXJARu4lTALBgNVHQ8EBAMCBaAwHQYDVR0lBBYw
|
||||||
|
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMCwGA1UdHwQlMCMwIaAfoB2GG2h0dHA6Ly9w
|
||||||
|
YXRoLnRvLmNybC9teWNhLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAjJKtWTVyFk7u
|
||||||
|
WGQlXHRBgt0/qWjnHzVOXcVJxaL1uuddhY1mOsmHxuvbM5SqfvnyRf1Dbvmm4y+E
|
||||||
|
8hNn9lESmQKRiq1XX2H0/K8AXfNIglqLCB+Fx5GgLJ8jboDsuJsq7SxYHVj4NIOY
|
||||||
|
9gf83cwxpxAV9JuR9izBIs7afP75MvjjuveLgIpyNzbdCt9/71WQY3Wt0epYRWLD
|
||||||
|
DGeP4zEBR6KMu3vILWX3kRlpvX1OcfqvcRpzpNRDmpzzMwOHQBtBp3/7lf43yGLg
|
||||||
|
ZxWuUzWCN5RXZZHhsSNqlplXjcyCEyAPTxiE0hbAKhAJeWjadnUJ88SC41bGjDor
|
||||||
|
FU3JVe6aqw==
|
||||||
|
-----END CERTIFICATE-----
|
13
helper/tlsutil/test/key/ourdomain.csr
Normal file
13
helper/tlsutil/test/key/ourdomain.csr
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIB4DCCAUkCAQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
|
||||||
|
MRQwEgYDVQQHEwtMb3MgQW5nZWxlczEcMBoGA1UEChMTSGFzaGljb3JwIFRlc3Qg
|
||||||
|
Q2VydDENMAsGA1UECxMEQmV0YTEYMBYGA1UEAxMPdGVzdGNvLmludGVybmFsMR4w
|
||||||
|
HAYJKoZIhvcNAQkBFg90ZXN0QHRlc3Rjby5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
|
||||||
|
gY0AMIGJAoGBAMzQosGiISdural85sAfqCc7LaeNV/MoKbMsAjsxLh4A/g3JNgiz
|
||||||
|
oZSR5qjt27B37/O4Nx7XSWhdbTtT0KnR8aSUt78qIFLj/Isuqx4JBrRMVoLEcSC7
|
||||||
|
W6X88htUbU4DuKYO7jpQntaeaAzZx49WE4JobUaQdMdOeA3Ur4ylR2OVAgMBAAGg
|
||||||
|
ADANBgkqhkiG9w0BAQUFAAOBgQBvz0CFO6td/cc6MzyWXNgfiYqdvDvq9JoEvROS
|
||||||
|
CG6ZsOAJc15ePw9px4wYK2fQZQVZkSWl0vpIZNnIWAdQboAKAQmfnE6CPHc+5ePJ
|
||||||
|
LxFIL2vG/4UYnKdbR2dxSpNxyfF59tXwVDS6qeietaZz596F2D3cBwBflxJcK2nD
|
||||||
|
JkTOOA==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
15
helper/tlsutil/test/key/ourdomain.key
Normal file
15
helper/tlsutil/test/key/ourdomain.key
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXQIBAAKBgQDM0KLBoiEnbq2pfObAH6gnOy2njVfzKCmzLAI7MS4eAP4NyTYI
|
||||||
|
s6GUkeao7duwd+/zuDce10loXW07U9Cp0fGklLe/KiBS4/yLLqseCQa0TFaCxHEg
|
||||||
|
u1ul/PIbVG1OA7imDu46UJ7WnmgM2cePVhOCaG1GkHTHTngN1K+MpUdjlQIDAQAB
|
||||||
|
AoGAJY4oGdtRuvpHa6mUYiwr2C24vF1lgBegNdNfAV2OOEA0VXDMsKHHggeSxO0A
|
||||||
|
eIY1gHoJ2WTed/2RluxSeaEebpwNp1e2V+JpJRVAF+PNBGng1WMIlK81vC6zZspm
|
||||||
|
AKe9O3XQZmIGKw1YyTOmYERAImjAarP0sKRAjkdfRTHEEcECQQD1W6ZtCJQvdDC/
|
||||||
|
GKKEOUzIB8l/hUAQSHc0pEhctLWU1VnFGfhFFRnUr2FFrbnF5gvvz8GdQtEXXuzX
|
||||||
|
9NSlnrblAkEA1bLQNwRz77ZQoMHme/E13ITDoBl+YUWZVN7+n0DkUgu6JdRsmYh6
|
||||||
|
nAIcoAS0CjxyNoPssDshdjyLcMxuT27+8QJAYCYEN+IOv/HWUGyE8y/JKPH0Qmaj
|
||||||
|
Tj43iIeAW+Ps7GAXB6g/pEK6lqALjFSh4i1eLCHMC6Ztba0jPxWQZz6lCQJBAJfD
|
||||||
|
oQmhmPChyssQSa62FGlixEecM1vDgOx8XdodzMjT2URqgyHagjDvcwLVtEMcwIQk
|
||||||
|
1uAlblM5FlJqbODu1BECQQC71WumyTo9/9Z9QR67WRd0yE1JHs3HUUvzw4JFNkRs
|
||||||
|
n0Tu7GZHDdzLaSodtC47tPJfYkdJwxBr20+RSCzF3VZF
|
||||||
|
-----END RSA PRIVATE KEY-----
|
28
helper/tlsutil/test/key/ssl-cert-snakeoil.key
Normal file
28
helper/tlsutil/test/key/ssl-cert-snakeoil.key
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYVw5skn/3Ka72
|
||||||
|
32ZaCrKtRVoQzan3tghq41KpQe3yZxIZbKy7sbwfdXnXVSwTAbq/3BYi9rya2t/v
|
||||||
|
W95yZh6JgfrLBvWl9Jo1EttZIxDhzCXGP+MPWm2KdNtHr84JznJbdxRpR0Jb4ykK
|
||||||
|
2d9dXbLJvCw8eEDFgOVGrj60USMir46sZFRvGWlMi+yHSOE+WQXaU40Dr0ZJqNvd
|
||||||
|
RNO9BtqpLaecZQaYTvlkyVdhjUE3+gQ0zEAQqpLcWi+zB5/IyR2+KwxDT3vAJumd
|
||||||
|
G7rIaGatPE8k0Ahb+zMKFFGYCoQ3sjbAbrQmrVtH4SU6ggl+CxpVdxshrK1W05Ms
|
||||||
|
WAiPw81/AgMBAAECggEAKjDIKlpjxGMHsTOeNV8yu2H0D6TcSefhOl885q9p5UU+
|
||||||
|
nWC5Sx19b7EsYtdEcix7LCGS25y86YJX+8kx16OcvvpvW5ru2z+Zt1IHHxocl7yF
|
||||||
|
fWVGNd9Pz5m8jf12NClj2fyeKW3xPhROE8Srr/yu+nLNObnF//6EOEWRCv9r176C
|
||||||
|
+dzYvYVNPP48Ug7NpjQB94CBprtJyqvuoXvBPtpARXazVniYEhnzG1Gaj1TiCII5
|
||||||
|
+emaMjKcWIEJ5stbBb3lUtqgm8bRNb/qcxoFfqTzHP+hbum9hbRz0KEIlAkm7uAv
|
||||||
|
S0TlyLuaj+gPQ+LwNX8EhGKUdlK/VM5bj2kq/tg3AQKBgQD/+A8ruHNa5nKGKNzP
|
||||||
|
dp+hXiL2sSzefMjDa2+sRJ0yftIMqYRfCJwzYumjfyycfCsu1LHainlQjSO6Kkgc
|
||||||
|
c0xVxnahWyPCQiqZuo9lLx4EVXCdRqWRg+pbyQhTSz90hfWEKD7XWsI8uRkOEnW8
|
||||||
|
36FiyovGDFxl0esaKrFNSFdmgQKBgQDYXcSIRJk41f7vL6FVmchpUnVYoD75k9YT
|
||||||
|
FqEplNMw6gXcqbC2aNH5wj7EJlRboyVpjXV4N0d2Cz6AwREJpr/rYpq68AixXmVs
|
||||||
|
kTKwevoHm/tln7CN+CyIEy6KXdLp4KoWLFfSG6tHWRwIGFxWEGrrIZS6Eznu4GPe
|
||||||
|
V2yOnMkz/wKBgC6nXtSALP5PbGZJgl2J6HR3/PVru5rdsZX0ugjzBJfUh6JpL0hH
|
||||||
|
AHlZOO5k2pO3CgPiHnyPqqbk4rMmy7frx+kGYE7ulqjseGlGmKY/nT/69qij3L+W
|
||||||
|
BJwwGwVbfLhXRjWNRE7qKub4cbmf4bfIJtkjw7AYRqsERM6jI2fLnKqBAoGAUBzY
|
||||||
|
CkSsHxlNXa7bI+DfDfBUNs6OwsZ0e3jjj4vlbrUYGo5SOhgxtzKvHt26Wnvb/Gs+
|
||||||
|
VZbSROkA6ZeTAWnWogdOl20NKu9yynIwvJusPGkK+qPYMZj0lCXWE7GNyL9A+xjM
|
||||||
|
I6XPE4nxESZD+jH2BL3YXdWEm+hF0iu4rE1tSm0CgYEAxssvvX7qcfTmxsp1YSHJ
|
||||||
|
H5j9ifkakci5W2VbCbdMtdOlgIlCFr2JYguaL98jx7WIJ4iH54ue/fbOdlkPCOsz
|
||||||
|
YGU4TceSRHeEJ7F6c67NOXm8j2TquAW2uYH87w07g2PIUwl/pp439qoDiThA6jEX
|
||||||
|
2ztyXgNUi7poqehPUoQuvC0=
|
||||||
|
-----END PRIVATE KEY-----
|
17
helper/tlsutil/test/key/ssl-cert-snakeoil.pem
Normal file
17
helper/tlsutil/test/key/ssl-cert-snakeoil.pem
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICsjCCAZqgAwIBAgIJAMi7aUCplU3VMA0GCSqGSIb3DQEBBQUAMBExDzANBgNV
|
||||||
|
BAMTBnVidW50dTAeFw0xMjEyMDIwNDQ3MzBaFw0yMjExMzAwNDQ3MzBaMBExDzAN
|
||||||
|
BgNVBAMTBnVidW50dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANhX
|
||||||
|
DmySf/cprvbfZloKsq1FWhDNqfe2CGrjUqlB7fJnEhlsrLuxvB91eddVLBMBur/c
|
||||||
|
FiL2vJra3+9b3nJmHomB+ssG9aX0mjUS21kjEOHMJcY/4w9abYp020evzgnOclt3
|
||||||
|
FGlHQlvjKQrZ311dssm8LDx4QMWA5UauPrRRIyKvjqxkVG8ZaUyL7IdI4T5ZBdpT
|
||||||
|
jQOvRkmo291E070G2qktp5xlBphO+WTJV2GNQTf6BDTMQBCqktxaL7MHn8jJHb4r
|
||||||
|
DENPe8Am6Z0bushoZq08TyTQCFv7MwoUUZgKhDeyNsButCatW0fhJTqCCX4LGlV3
|
||||||
|
GyGsrVbTkyxYCI/DzX8CAwEAAaMNMAswCQYDVR0TBAIwADANBgkqhkiG9w0BAQUF
|
||||||
|
AAOCAQEAQaS5yAih5NBV2edX1wkIQfAUElqmzoXvxsozDYy+P+S5tJeFXDSqzTAy
|
||||||
|
qkd/6qjkBdaARfKUJZeT/jRjqxoNtE9SR4PMOnD4zrqD26ujgZRVtPImbmVxCnMI
|
||||||
|
1B9LwvhpDHZuPGN5bPp3o+iDYea8zkS3Y31Ic889KSwKBDb1LlNogOdved+2DGd1
|
||||||
|
yCxEErImbl4B0+QPrRk2bWbDfKhDfJ2FV+9kWIoEuCQBpr2tj1E5zvTadOVm5P2M
|
||||||
|
u7kjGl4w0GIAONiMC9l2TwMmPuG1jpM/WjQkG0sTKOCl7xQKgXBNJ78Wm2bfGtgb
|
||||||
|
shr/PNbS/EyISlUa07+zJtiRnr/EiQ==
|
||||||
|
-----END CERTIFICATE-----
|
1
helper/tlsutil/test/notes.txt
Normal file
1
helper/tlsutil/test/notes.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Instructions from https://langui.sh/2009/01/18/openssl-self-signed-ca/
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/memberlist"
|
"github.com/hashicorp/memberlist"
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/nomad/nomad/structs/config"
|
"github.com/hashicorp/nomad/nomad/structs/config"
|
||||||
"github.com/hashicorp/nomad/scheduler"
|
"github.com/hashicorp/nomad/scheduler"
|
||||||
|
@ -191,6 +192,9 @@ type Config struct {
|
||||||
// This period is meant to be long enough for a leader election to take
|
// This period is meant to be long enough for a leader election to take
|
||||||
// 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
|
||||||
|
|
||||||
|
// TLSConfig holds various TLS related configurations
|
||||||
|
TLSConfig *config.TLSConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckVersion is used to check if the ProtocolVersion is valid
|
// CheckVersion is used to check if the ProtocolVersion is valid
|
||||||
|
@ -263,3 +267,17 @@ func DefaultConfig() *Config {
|
||||||
c.RaftConfig.ShutdownOnRemove = false
|
c.RaftConfig.ShutdownOnRemove = false
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tlsConfig returns a TLSUtil Config based on the server configuration
|
||||||
|
func (c *Config) tlsConfig() *tlsutil.Config {
|
||||||
|
tlsConf := &tlsutil.Config{
|
||||||
|
VerifyIncoming: true,
|
||||||
|
VerifyOutgoing: true,
|
||||||
|
VerifyServerHostname: c.TLSConfig.VerifyServerHostname,
|
||||||
|
CAFile: c.TLSConfig.CAFile,
|
||||||
|
CertFile: c.TLSConfig.CertFile,
|
||||||
|
KeyFile: c.TLSConfig.KeyFile,
|
||||||
|
ServerName: c.NodeName,
|
||||||
|
}
|
||||||
|
return tlsConf
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/tlsutil"
|
|
||||||
"github.com/hashicorp/net-rpc-msgpackrpc"
|
"github.com/hashicorp/net-rpc-msgpackrpc"
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/yamux"
|
"github.com/hashicorp/yamux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ type ConnPool struct {
|
||||||
limiter map[string]chan struct{}
|
limiter map[string]chan struct{}
|
||||||
|
|
||||||
// TLS wrapper
|
// TLS wrapper
|
||||||
tlsWrap tlsutil.DCWrapper
|
tlsWrap tlsutil.Wrapper
|
||||||
|
|
||||||
// Used to indicate the pool is shutdown
|
// Used to indicate the pool is shutdown
|
||||||
shutdown bool
|
shutdown bool
|
||||||
|
@ -141,7 +141,7 @@ type ConnPool struct {
|
||||||
// Set maxTime to 0 to disable reaping. maxStreams is used to control
|
// Set maxTime to 0 to disable reaping. maxStreams is used to control
|
||||||
// the number of idle streams allowed.
|
// the number of idle streams allowed.
|
||||||
// If TLS settings are provided outgoing connections use TLS.
|
// If TLS settings are provided outgoing connections use TLS.
|
||||||
func NewPool(logOutput io.Writer, maxTime time.Duration, maxStreams int, tlsWrap tlsutil.DCWrapper) *ConnPool {
|
func NewPool(logOutput io.Writer, maxTime time.Duration, maxStreams int, tlsWrap tlsutil.Wrapper) *ConnPool {
|
||||||
pool := &ConnPool{
|
pool := &ConnPool{
|
||||||
logOutput: logOutput,
|
logOutput: logOutput,
|
||||||
maxTime: maxTime,
|
maxTime: maxTime,
|
||||||
|
@ -261,7 +261,7 @@ func (p *ConnPool) getNewConn(region string, addr net.Addr, version int) (*Conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the connection in a TLS client
|
// Wrap the connection in a TLS client
|
||||||
tlsConn, err := p.tlsWrap(region, conn)
|
tlsConn, err := p.tlsWrap(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/tlsutil"
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RaftLayer implements the raft.StreamLayer interface,
|
// RaftLayer implements the raft.StreamLayer interface,
|
||||||
|
|
|
@ -18,9 +18,9 @@ import (
|
||||||
|
|
||||||
consulapi "github.com/hashicorp/consul/api"
|
consulapi "github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/consul/tlsutil"
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/nomad/command/agent/consul"
|
"github.com/hashicorp/nomad/command/agent/consul"
|
||||||
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
||||||
"github.com/hashicorp/nomad/nomad/state"
|
"github.com/hashicorp/nomad/nomad/state"
|
||||||
"github.com/hashicorp/nomad/nomad/structs"
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
"github.com/hashicorp/raft"
|
"github.com/hashicorp/raft"
|
||||||
|
@ -187,11 +187,29 @@ func NewServer(config *Config, consulSyncer *consul.Syncer, logger *log.Logger)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure TLS
|
||||||
|
var tlsWrap tlsutil.Wrapper
|
||||||
|
var incomingTLS *tls.Config
|
||||||
|
if config.TLSConfig.EnableRPC {
|
||||||
|
tlsConf := config.tlsConfig()
|
||||||
|
tw, err := tlsConf.OutgoingTLSWrapper()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsWrap = tw
|
||||||
|
|
||||||
|
itls, err := tlsConf.IncomingTLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
incomingTLS = itls
|
||||||
|
}
|
||||||
|
|
||||||
// Create the server
|
// Create the server
|
||||||
s := &Server{
|
s := &Server{
|
||||||
config: config,
|
config: config,
|
||||||
consulSyncer: consulSyncer,
|
consulSyncer: consulSyncer,
|
||||||
connPool: NewPool(config.LogOutput, serverRPCCache, serverMaxStreams, nil),
|
connPool: NewPool(config.LogOutput, serverRPCCache, serverMaxStreams, tlsWrap),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
rpcServer: rpc.NewServer(),
|
rpcServer: rpc.NewServer(),
|
||||||
peers: make(map[string][]*serverParts),
|
peers: make(map[string][]*serverParts),
|
||||||
|
@ -201,6 +219,7 @@ func NewServer(config *Config, consulSyncer *consul.Syncer, logger *log.Logger)
|
||||||
evalBroker: evalBroker,
|
evalBroker: evalBroker,
|
||||||
blockedEvals: blockedEvals,
|
blockedEvals: blockedEvals,
|
||||||
planQueue: planQueue,
|
planQueue: planQueue,
|
||||||
|
rpcTLS: incomingTLS,
|
||||||
shutdownCh: make(chan struct{}),
|
shutdownCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,8 +234,7 @@ 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(nil); 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)
|
||||||
return nil, fmt.Errorf("Failed to start RPC layer: %v", err)
|
return nil, fmt.Errorf("Failed to start RPC layer: %v", err)
|
||||||
|
@ -576,7 +594,7 @@ func (s *Server) setupVaultClient() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupRPC is used to setup the RPC listener
|
// setupRPC is used to setup the RPC listener
|
||||||
func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error {
|
func (s *Server) setupRPC(tlsWrap tlsutil.Wrapper) error {
|
||||||
// Create endpoints
|
// Create endpoints
|
||||||
s.endpoints.Status = &Status{s}
|
s.endpoints.Status = &Status{s}
|
||||||
s.endpoints.Node = &Node{srv: s}
|
s.endpoints.Node = &Node{srv: s}
|
||||||
|
@ -622,11 +640,7 @@ func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error {
|
||||||
return fmt.Errorf("RPC advertise address is not advertisable: %v", addr)
|
return fmt.Errorf("RPC advertise address is not advertisable: %v", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide a DC specific wrapper. Raft replication is only
|
s.raftLayer = NewRaftLayer(s.rpcAdvertise, tlsWrap)
|
||||||
// ever done in the same datacenter, so we can provide it as a constant.
|
|
||||||
// wrapper := tlsutil.SpecificDC(s.config.Datacenter, tlsWrap)
|
|
||||||
// TODO: TLS...
|
|
||||||
s.raftLayer = NewRaftLayer(s.rpcAdvertise, nil)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
57
nomad/structs/config/tls.go
Normal file
57
nomad/structs/config/tls.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
// TLSConfig provides TLS related configuration
|
||||||
|
type TLSConfig struct {
|
||||||
|
|
||||||
|
// EnableHTTP enabled TLS for http traffic to the Nomad server and clients
|
||||||
|
EnableHTTP bool `mapstructure:"http"`
|
||||||
|
|
||||||
|
// EnableRPC enables TLS for RPC and Raft traffic to the Nomad servers
|
||||||
|
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>.
|
||||||
|
// 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
Loading…
Reference in a new issue