Allow client CAs to be provided to notary-signer.

Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
Ying Li 2015-10-19 11:27:34 -07:00
parent daa36b43b7
commit bbf941d198
2 changed files with 100 additions and 17 deletions

View File

@ -1,10 +1,12 @@
package main package main
import ( import (
"crypto/tls"
"database/sql" "database/sql"
"errors" "errors"
_ "expvar" _ "expvar"
"flag" "flag"
"fmt"
"log" "log"
"net" "net"
"net/http" "net/http"
@ -68,6 +70,30 @@ func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (p
return passphrase, false, nil return passphrase, false, nil
} }
// parses and sets up the TLS for the signer http + grpc server
func signerTLS(configuration *viper.Viper, printUsage bool) (*tls.Config, error) {
certFile := configuration.GetString("server.cert_file")
keyFile := configuration.GetString("server.key_file")
if certFile == "" || keyFile == "" {
if printUsage {
usage()
}
return nil, fmt.Errorf("Certificate and key are mandatory")
}
clientCAFile := configuration.GetString("server.client_ca_file")
tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{
ServerCertFile: certFile,
ServerKeyFile: keyFile,
RequireClientAuth: clientCAFile != "",
ClientCAFile: clientCAFile,
})
if err != nil {
return nil, fmt.Errorf("Unable to set up TLS: %s", err.Error())
}
return tlsConfig, nil
}
func main() { func main() {
flag.Usage = usage flag.Usage = usage
flag.Parse() flag.Parse()
@ -95,19 +121,9 @@ func main() {
logrus.SetLevel(logrus.Level(mainViper.GetInt("logging.level"))) logrus.SetLevel(logrus.Level(mainViper.GetInt("logging.level")))
certFile := mainViper.GetString("server.cert_file") tlsConfig, err := signerTLS(mainViper, true)
keyFile := mainViper.GetString("server.key_file")
if certFile == "" || keyFile == "" {
usage()
log.Fatalf("Certificate and key are mandatory")
}
tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{
ServerCertFile: certFile,
ServerKeyFile: keyFile,
})
if err != nil { if err != nil {
logrus.Fatalf("Unable to set up TLS: %s", err.Error()) logrus.Fatalf(err.Error())
} }
cryptoServices := make(signer.CryptoServiceIndex) cryptoServices := make(signer.CryptoServiceIndex)
@ -163,10 +179,7 @@ func main() {
if err != nil { if err != nil {
log.Fatalf("failed to listen %v", err) log.Fatalf("failed to listen %v", err)
} }
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile) creds := credentials.NewTLS(tlsConfig)
if err != nil {
log.Fatalf("failed to generate credentials %v", err)
}
opts := []grpc.ServerOption{grpc.Creds(creds)} opts := []grpc.ServerOption{grpc.Creds(creds)}
grpcServer := grpc.NewServer(opts...) grpcServer := grpc.NewServer(opts...)
@ -191,7 +204,7 @@ func main() {
log.Println("HTTP server listening on", httpAddr) log.Println("HTTP server listening on", httpAddr)
} }
err = server.ListenAndServeTLS(certFile, keyFile) err = server.ListenAndServe()
if err != nil { if err != nil {
log.Fatal("HTTP server failed to start:", err) log.Fatal("HTTP server failed to start:", err)
} }

View File

@ -1 +1,71 @@
package main package main
import (
"bytes"
"crypto/tls"
"fmt"
"strings"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
)
const (
Cert = "../../fixtures/notary-signer.crt"
Key = "../../fixtures/notary-signer.key"
Root = "../../fixtures/root-ca.crt"
)
// initializes a viper object with test configuration
func configure(jsonConfig []byte) *viper.Viper {
config := viper.New()
config.SetConfigType("json")
config.ReadConfig(bytes.NewBuffer(jsonConfig))
return config
}
func TestSignerTLSMissingCertAndOrKey(t *testing.T) {
configs := []string{
"{}",
fmt.Sprintf(`{"cert_file": "%s"}`, Cert),
fmt.Sprintf(`{"key_file": "%s"}`, Key),
}
for _, serverConfig := range configs {
config := configure(
[]byte(fmt.Sprintf(`{"server": %s}`, serverConfig)))
tlsConfig, err := signerTLS(config, false)
assert.Error(t, err)
assert.Nil(t, tlsConfig)
assert.Equal(t, "Certificate and key are mandatory", err.Error())
}
}
// The rest of the functionality of singerTLS depends upon
// utils.ConfigureServerTLS, so this test just asserts that if successful,
// the correct tls.Config is returned based on all the configuration parameters
func TestSignerTLSSuccess(t *testing.T) {
keypair, err := tls.LoadX509KeyPair(Cert, Key)
assert.NoError(t, err, "Unable to load cert and key for testing")
config := fmt.Sprintf(
`{"server": {"cert_file": "%s", "key_file": "%s", "client_ca_file": "%s"}}`,
Cert, Key, Cert)
tlsConfig, err := signerTLS(configure([]byte(config)), false)
assert.NoError(t, err)
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
assert.NotNil(t, tlsConfig.ClientCAs)
}
// The rest of the functionality of singerTLS depends upon
// utils.ConfigureServerTLS, so this test just asserts that if it fails,
// the error is propogated.
func TestSignerTLSFailure(t *testing.T) {
config := fmt.Sprintf(
`{"server": {"cert_file": "%s", "key_file": "%s", "client_ca_file": "%s"}}`,
Cert, Key, "non-existant")
tlsConfig, err := signerTLS(configure([]byte(config)), false)
assert.Error(t, err)
assert.Nil(t, tlsConfig)
assert.True(t, strings.Contains(err.Error(), "Unable to set up TLS"))
}