mirror of https://github.com/docker/docs.git
Merge pull request #229 from cyli/tls-config-refactor
Factor out TLS configuration code for server and TLS
This commit is contained in:
commit
bcdd375ce5
|
@ -1,8 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"database/sql"
|
||||
"errors"
|
||||
_ "expvar"
|
||||
|
@ -22,6 +20,7 @@ import (
|
|||
"github.com/docker/notary/cryptoservice"
|
||||
"github.com/docker/notary/signer"
|
||||
"github.com/docker/notary/signer/api"
|
||||
"github.com/docker/notary/utils"
|
||||
"github.com/docker/notary/version"
|
||||
"github.com/endophage/gotuf/data"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
|
@ -103,20 +102,13 @@ func main() {
|
|||
log.Fatalf("Certificate and key are mandatory")
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA},
|
||||
tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{
|
||||
ServerCertFile: certFile,
|
||||
ServerKeyFile: keyFile,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Fatalf("Unable to set up TLS: %s", err.Error())
|
||||
}
|
||||
tlsConfig.Rand = rand.Reader
|
||||
|
||||
cryptoServices := make(signer.CryptoServiceIndex)
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@ package main
|
|||
import (
|
||||
"bufio"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
@ -22,7 +20,7 @@ import (
|
|||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/docker/notary/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
@ -360,7 +358,6 @@ func (ps passwordStore) Basic(u *url.URL) (string, string) {
|
|||
|
||||
func getTransport(gun string, readOnly bool) http.RoundTripper {
|
||||
// Attempt to get a root CA from the config file. Nil is the host defaults.
|
||||
rootPool := x509.NewCertPool()
|
||||
rootCAFile := mainViper.GetString("remote_server.root_ca")
|
||||
if rootCAFile != "" {
|
||||
// If we haven't been given an Absolute path, we assume it's relative
|
||||
|
@ -368,19 +365,18 @@ func getTransport(gun string, readOnly bool) http.RoundTripper {
|
|||
if !filepath.IsAbs(rootCAFile) {
|
||||
rootCAFile = filepath.Join(configPath, rootCAFile)
|
||||
}
|
||||
rootCert, err := trustmanager.LoadCertFromFile(rootCAFile)
|
||||
if err != nil {
|
||||
fatalf("could not load root ca file. %s", err.Error())
|
||||
}
|
||||
rootPool.AddCert(rootCert)
|
||||
}
|
||||
|
||||
// skipTLSVerify is false by default so verification will
|
||||
// be performed.
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: mainViper.GetBool("remote_server.skipTLSVerify"),
|
||||
MinVersion: tls.VersionTLS10,
|
||||
RootCAs: rootPool,
|
||||
insecureSkipVerify := false
|
||||
if mainViper.IsSet("remote_server.skipTLSVerify") {
|
||||
insecureSkipVerify = mainViper.GetBool("remote_server.skipTLSVerify")
|
||||
}
|
||||
tlsConfig, err := utils.ConfigureClientTLS(&utils.ClientTLSOpts{
|
||||
RootCAFile: rootCAFile,
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Fatal("Unable to configure TLS: ", err.Error())
|
||||
}
|
||||
|
||||
base := &http.Transport{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
|
@ -42,27 +41,13 @@ func Run(ctx context.Context, addr, tlsCertFile, tlsKeyFile string, trust signed
|
|||
}
|
||||
|
||||
if tlsCertFile != "" && tlsKeyFile != "" {
|
||||
keypair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
|
||||
tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{
|
||||
ServerCertFile: tlsCertFile,
|
||||
ServerKeyFile: tlsKeyFile,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
},
|
||||
Certificates: []tls.Certificate{keypair},
|
||||
Rand: rand.Reader,
|
||||
}
|
||||
|
||||
logrus.Info("Enabling TLS")
|
||||
lsnr = tls.NewListener(lsnr, tlsConfig)
|
||||
} else if tlsCertFile != "" || tlsKeyFile != "" {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
pb "github.com/docker/notary/proto"
|
||||
"github.com/docker/notary/utils"
|
||||
"github.com/endophage/gotuf/data"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
|
@ -30,10 +31,14 @@ type NotarySigner struct {
|
|||
func NewNotarySigner(hostname string, port string, tlscafile string) *NotarySigner {
|
||||
var opts []grpc.DialOption
|
||||
netAddr := net.JoinHostPort(hostname, port)
|
||||
creds, err := credentials.NewClientTLSFromFile(tlscafile, hostname)
|
||||
tlsConfig, err := utils.ConfigureClientTLS(&utils.ClientTLSOpts{
|
||||
RootCAFile: tlscafile,
|
||||
ServerName: hostname,
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Fatal("fail to read: ", err)
|
||||
logrus.Fatal("Unable to set up TLS: ", err)
|
||||
}
|
||||
creds := credentials.NewTLS(tlsConfig)
|
||||
opts = append(opts, grpc.WithTransportCredentials(creds))
|
||||
conn, err := grpc.Dial(netAddr, opts...)
|
||||
|
||||
|
|
|
@ -260,6 +260,12 @@ func (s *X509FileStore) GetVerifyOptions(dnsName string) (x509.VerifyOptions, er
|
|||
return opts, nil
|
||||
}
|
||||
|
||||
// Empty returns true if there are no certificates in the X509FileStore, false
|
||||
// otherwise.
|
||||
func (s *X509FileStore) Empty() bool {
|
||||
return len(s.fingerprintMap) == 0
|
||||
}
|
||||
|
||||
func fileName(cert *x509.Certificate) (string, CertID, error) {
|
||||
certID, err := fingerprintCert(cert)
|
||||
if err != nil {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -22,6 +23,44 @@ func TestNewX509FileStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// NewX509FileStore loads any existing certs from the directory, and does
|
||||
// not overwrite any of the.
|
||||
func TestNewX509FileStoreLoadsExistingCerts(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
certBytes, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
assert.NoError(t, err)
|
||||
out, err := os.Create(filepath.Join(tempDir, "root-ca.crt"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
// to distinguish it from the canonical format
|
||||
distinguishingBytes := []byte{'\n', '\n', '\n', '\n', '\n', '\n'}
|
||||
nBytes, err := out.Write(distinguishingBytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, distinguishingBytes, nBytes)
|
||||
|
||||
nBytes, err = out.Write(certBytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, certBytes, nBytes)
|
||||
|
||||
err = out.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedCert, err := LoadCertFromFile("../fixtures/root-ca.crt")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []*x509.Certificate{expectedCert}, store.GetCertificates())
|
||||
|
||||
outBytes, err := ioutil.ReadFile(filepath.Join(tempDir, "root-ca.crt"))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, distinguishingBytes, outBytes[:6], "original file overwritten")
|
||||
assert.Equal(t, certBytes, outBytes[6:], "original file overwritten")
|
||||
}
|
||||
|
||||
func TestAddCertX509FileStore(t *testing.T) {
|
||||
// Read certificate from file
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
|
@ -82,6 +121,21 @@ func TestAddCertFromFileX509FileStore(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestNewX509FileStoreEmpty verifies the behavior of the Empty function
|
||||
func TestNewX509FileStoreEmpty(t *testing.T) {
|
||||
tempDir, err := ioutil.TempDir("", "cert-test")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
store, err := NewX509FileStore(tempDir)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, store.Empty())
|
||||
|
||||
err = store.AddCertFromFile("../fixtures/root-ca.crt")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, store.Empty())
|
||||
}
|
||||
|
||||
func TestAddCertFromPEMX509FileStore(t *testing.T) {
|
||||
b, err := ioutil.ReadFile("../fixtures/root-ca.crt")
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Client TLS cipher suites (dropping CBC ciphers for client preferred suite set)
|
||||
var clientCipherSuites = []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
}
|
||||
|
||||
// Server TLS cipher suites
|
||||
var serverCipherSuites = append(clientCipherSuites, []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
}...)
|
||||
|
||||
func poolFromFile(filename string) (*x509.CertPool, error) {
|
||||
pemBytes, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM(pemBytes); !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"Unable to parse certificates from %s", filename)
|
||||
}
|
||||
if len(pool.Subjects()) == 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"No certificates parsed from %s", filename)
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// ServerTLSOpts generates a tls configuration for servers using the
|
||||
// provided parameters.
|
||||
type ServerTLSOpts struct {
|
||||
ServerCertFile string
|
||||
ServerKeyFile string
|
||||
RequireClientAuth bool
|
||||
ClientCAFile string
|
||||
}
|
||||
|
||||
// ConfigureServerTLS specifies a set of ciphersuites, the server cert and key,
|
||||
// and optionally client authentication. Note that a tls configuration is
|
||||
// constructed that either requires and verifies client authentication or
|
||||
// doesn't deal with client certs at all. Nothing in the middle.
|
||||
//
|
||||
// Also note that if the client CA file contains invalid data, behavior is not
|
||||
// guaranteed. Currently (as of Go 1.5.1) only the valid certificates up to
|
||||
// the bad data will be parsed and added the client CA pool.
|
||||
func ConfigureServerTLS(opts *ServerTLSOpts) (*tls.Config, error) {
|
||||
keypair, err := tls.LoadX509KeyPair(
|
||||
opts.ServerCertFile, opts.ServerKeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
PreferServerCipherSuites: true,
|
||||
CipherSuites: serverCipherSuites,
|
||||
Certificates: []tls.Certificate{keypair},
|
||||
Rand: rand.Reader,
|
||||
}
|
||||
|
||||
if opts.RequireClientAuth {
|
||||
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
if opts.ClientCAFile != "" {
|
||||
pool, err := poolFromFile(opts.ClientCAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.ClientCAs = pool
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
// ClientTLSOpts is a struct that contains options to pass to
|
||||
// ConfigureClientTLS
|
||||
type ClientTLSOpts struct {
|
||||
RootCAFile string
|
||||
ServerName string
|
||||
InsecureSkipVerify bool
|
||||
ClientCertFile string
|
||||
ClientKeyFile string
|
||||
}
|
||||
|
||||
// ConfigureClientTLS generates a tls configuration for clients using the
|
||||
// provided parameters.
|
||||
///
|
||||
// Note that if the root CA file contains invalid data, behavior is not
|
||||
// guaranteed. Currently (as of Go 1.5.1) only the valid certificates up to
|
||||
// the bad data will be parsed and added the root CA pool.
|
||||
func ConfigureClientTLS(opts *ClientTLSOpts) (*tls.Config, error) {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: opts.InsecureSkipVerify,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: clientCipherSuites,
|
||||
ServerName: opts.ServerName,
|
||||
}
|
||||
|
||||
if opts.RootCAFile != "" {
|
||||
pool, err := poolFromFile(opts.RootCAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.RootCAs = pool
|
||||
}
|
||||
|
||||
if opts.ClientCertFile != "" || opts.ClientKeyFile != "" {
|
||||
keypair, err := tls.LoadX509KeyPair(
|
||||
opts.ClientCertFile, opts.ClientKeyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{keypair}
|
||||
}
|
||||
|
||||
return tlsConfig, nil
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/notary/trustmanager"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
ServerCert = "../fixtures/notary-server.crt"
|
||||
ServerKey = "../fixtures/notary-server.key"
|
||||
RootCA = "../fixtures/root-ca.crt"
|
||||
)
|
||||
|
||||
// generates a multiple-certificate file with both RSA and ECDSA certs and
|
||||
// returns the filename so that cleanup can be deferred.
|
||||
func generateMultiCert(t *testing.T) string {
|
||||
tempFile, err := ioutil.TempFile("/tmp", "cert-test")
|
||||
defer tempFile.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
assert.NoError(t, err)
|
||||
ecKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
assert.NoError(t, err)
|
||||
template, err := trustmanager.NewCertificate("gun")
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, key := range []crypto.Signer{rsaKey, ecKey} {
|
||||
derBytes, err := x509.CreateCertificate(
|
||||
rand.Reader, template, template, key.Public(), key)
|
||||
assert.NoError(t, err)
|
||||
|
||||
cert, err := x509.ParseCertificate(derBytes)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pemBytes := trustmanager.CertToPEM(cert)
|
||||
nBytes, err := tempFile.Write(pemBytes)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, nBytes, len(pemBytes))
|
||||
}
|
||||
return tempFile.Name()
|
||||
}
|
||||
|
||||
// If the cert files and directory are provided but are invalid, an error is
|
||||
// returned.
|
||||
func TestConfigServerTLSFailsIfUnableToLoadCerts(t *testing.T) {
|
||||
for i := 0; i < 3; i++ {
|
||||
files := []string{ServerCert, ServerKey, RootCA}
|
||||
files[i] = "not-real-file"
|
||||
|
||||
result, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: files[0],
|
||||
ServerKeyFile: files[1],
|
||||
RequireClientAuth: true,
|
||||
ClientCAFile: files[2],
|
||||
})
|
||||
assert.Nil(t, result)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// If server cert and key are provided, and client auth is disabled, then
|
||||
// a valid tls.Config is returned with ClientAuth set to NoClientCert
|
||||
func TestConfigServerTLSServerCertsOnly(t *testing.T) {
|
||||
keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: ServerCert,
|
||||
ServerKeyFile: ServerKey,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
assert.True(t, tlsConfig.PreferServerCipherSuites)
|
||||
assert.Equal(t, tls.NoClientCert, tlsConfig.ClientAuth)
|
||||
assert.Nil(t, tlsConfig.ClientCAs)
|
||||
}
|
||||
|
||||
// If a valid client cert file is provided, but it contains no client
|
||||
// certs, an error is returned.
|
||||
func TestConfigServerTLSWithEmptyCACertFile(t *testing.T) {
|
||||
tempFile, err := ioutil.TempFile("/tmp", "cert-test")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tempFile.Name())
|
||||
tempFile.Close()
|
||||
|
||||
tlsConfig, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: ServerCert,
|
||||
ServerKeyFile: ServerKey,
|
||||
ClientCAFile: tempFile.Name(),
|
||||
})
|
||||
assert.Nil(t, tlsConfig)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// If server cert and key are provided, and client cert file is provided with
|
||||
// one cert, a valid tls.Config is returned with the clientCAs set to that
|
||||
// cert.
|
||||
func TestConfigServerTLSWithOneCACert(t *testing.T) {
|
||||
keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: ServerCert,
|
||||
ServerKeyFile: ServerKey,
|
||||
ClientCAFile: RootCA,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
assert.True(t, tlsConfig.PreferServerCipherSuites)
|
||||
assert.Equal(t, tls.NoClientCert, tlsConfig.ClientAuth)
|
||||
assert.Len(t, tlsConfig.ClientCAs.Subjects(), 1)
|
||||
}
|
||||
|
||||
// If server cert and key are provided, and client cert file is provided with
|
||||
// multiple certs, a valid tls.Config is returned with the clientCAs set to
|
||||
// the valid cert.
|
||||
func TestConfigServerTLSWithMultipleCACerts(t *testing.T) {
|
||||
tempFilename := generateMultiCert(t)
|
||||
defer os.RemoveAll(tempFilename)
|
||||
|
||||
keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: ServerCert,
|
||||
ServerKeyFile: ServerKey,
|
||||
ClientCAFile: tempFilename,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
assert.True(t, tlsConfig.PreferServerCipherSuites)
|
||||
assert.Equal(t, tls.NoClientCert, tlsConfig.ClientAuth)
|
||||
assert.Len(t, tlsConfig.ClientCAs.Subjects(), 2)
|
||||
}
|
||||
|
||||
// If server cert and key are provided, and client auth is disabled, then
|
||||
// a valid tls.Config is returned with ClientAuth set to
|
||||
// RequireAndVerifyClientCert
|
||||
func TestConfigServerTLSClientAuthEnabled(t *testing.T) {
|
||||
keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig, err := ConfigureServerTLS(&ServerTLSOpts{
|
||||
ServerCertFile: ServerCert,
|
||||
ServerKeyFile: ServerKey,
|
||||
RequireClientAuth: true,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
assert.True(t, tlsConfig.PreferServerCipherSuites)
|
||||
assert.Equal(t, tls.RequireAndVerifyClientCert, tlsConfig.ClientAuth)
|
||||
assert.Nil(t, tlsConfig.ClientCAs)
|
||||
}
|
||||
|
||||
// The skipVerify boolean gets set on the tls.Config's InsecureSkipBoolean
|
||||
func TestConfigClientTLSNoVerify(t *testing.T) {
|
||||
for _, skip := range []bool{true, false} {
|
||||
tlsConfig, err := ConfigureClientTLS(
|
||||
&ClientTLSOpts{InsecureSkipVerify: skip})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, tlsConfig.Certificates)
|
||||
assert.Equal(t, skip, tlsConfig.InsecureSkipVerify)
|
||||
assert.Equal(t, "", tlsConfig.ServerName)
|
||||
assert.Nil(t, tlsConfig.RootCAs)
|
||||
}
|
||||
}
|
||||
|
||||
// The skipVerify boolean gets set on the tls.Config's InsecureSkipBoolean
|
||||
func TestConfigClientServerName(t *testing.T) {
|
||||
for _, name := range []string{"", "myname"} {
|
||||
tlsConfig, err := ConfigureClientTLS(&ClientTLSOpts{ServerName: name})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, tlsConfig.Certificates)
|
||||
assert.Equal(t, false, tlsConfig.InsecureSkipVerify)
|
||||
assert.Equal(t, name, tlsConfig.ServerName)
|
||||
assert.Nil(t, tlsConfig.RootCAs)
|
||||
}
|
||||
}
|
||||
|
||||
// The RootCA is set if the file provided has a single CA cert.
|
||||
func TestConfigClientTLSRootCAFileWithOneCert(t *testing.T) {
|
||||
tlsConfig, err := ConfigureClientTLS(&ClientTLSOpts{RootCAFile: RootCA})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, tlsConfig.Certificates)
|
||||
assert.Equal(t, false, tlsConfig.InsecureSkipVerify)
|
||||
assert.Equal(t, "", tlsConfig.ServerName)
|
||||
assert.Len(t, tlsConfig.RootCAs.Subjects(), 1)
|
||||
}
|
||||
|
||||
// If the root CA file provided has multiple CA certs, only the valid certs
|
||||
// are read.
|
||||
func TestConfigClientTLSRootCAFileMultipleCerts(t *testing.T) {
|
||||
tempFilename := generateMultiCert(t)
|
||||
defer os.RemoveAll(tempFilename)
|
||||
|
||||
tlsConfig, err := ConfigureClientTLS(
|
||||
&ClientTLSOpts{RootCAFile: tempFilename})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, tlsConfig.Certificates)
|
||||
assert.Equal(t, false, tlsConfig.InsecureSkipVerify)
|
||||
assert.Equal(t, "", tlsConfig.ServerName)
|
||||
assert.Len(t, tlsConfig.RootCAs.Subjects(), 2)
|
||||
}
|
||||
|
||||
// An error is returned if a root CA is provided but the file doesn't exist.
|
||||
func TestConfigClientTLSNonexistentRootCAFile(t *testing.T) {
|
||||
tlsConfig, err := ConfigureClientTLS(
|
||||
&ClientTLSOpts{RootCAFile: "not-a-file"})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
}
|
||||
|
||||
// An error is returned if either the client cert or the key are provided
|
||||
// but invalid or blank.
|
||||
func TestConfigClientTLSClientCertOrKeyInvalid(t *testing.T) {
|
||||
for i := 0; i < 2; i++ {
|
||||
for _, invalid := range []string{"not-a-file", ""} {
|
||||
files := []string{ServerCert, ServerKey}
|
||||
files[i] = invalid
|
||||
tlsConfig, err := ConfigureClientTLS(&ClientTLSOpts{
|
||||
ClientCertFile: files[0], ClientKeyFile: files[1]})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The certificate is set if the client cert and client key are provided and
|
||||
// valid.
|
||||
func TestConfigClientTLSValidClientCertAndKey(t *testing.T) {
|
||||
keypair, err := tls.LoadX509KeyPair(ServerCert, ServerKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tlsConfig, err := ConfigureClientTLS(&ClientTLSOpts{
|
||||
ClientCertFile: ServerCert, ClientKeyFile: ServerKey})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
assert.Equal(t, false, tlsConfig.InsecureSkipVerify)
|
||||
assert.Equal(t, "", tlsConfig.ServerName)
|
||||
assert.Nil(t, tlsConfig.RootCAs)
|
||||
}
|
Loading…
Reference in New Issue