mirror of https://github.com/docker/docs.git
Use shared configuration parsing tools in notary-server.
This changes the 'addr' parameter of notary-server's config to 'http_addr', so we can add a GRPC server to notary-server if necessary. This also allows environment variables to override the notary-server config file entries, as notary-signer already does. The bugsnag configuration has also been changed so that the bugsnag parameters are under the "bugsnag" key. Signed-off-by: Ying Li <ying.li@docker.com>
This commit is contained in:
parent
9e5ac006ec
commit
c43776d36f
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"server": {
|
||||
"addr": ":4443",
|
||||
"http_addr": ":4443",
|
||||
"tls_key_file": "./fixtures/notary-server.key",
|
||||
"tls_cert_file": "./fixtures/notary-server.crt"
|
||||
},
|
||||
|
|
|
@ -13,18 +13,16 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/docker/distribution/health"
|
||||
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
||||
_ "github.com/docker/distribution/registry/auth/token"
|
||||
"github.com/docker/notary/server/storage"
|
||||
"github.com/docker/notary/signer/client"
|
||||
"github.com/docker/notary/tuf/signed"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
bugsnag_hook "github.com/Sirupsen/logrus/hooks/bugsnag"
|
||||
"github.com/docker/notary/server"
|
||||
"github.com/docker/notary/server/storage"
|
||||
"github.com/docker/notary/utils"
|
||||
"github.com/docker/notary/version"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -36,38 +34,41 @@ const DebugAddress = "localhost:8080"
|
|||
var (
|
||||
debug bool
|
||||
configFile string
|
||||
envPrefix = "NOTARY_SERVER"
|
||||
mainViper = viper.New()
|
||||
)
|
||||
|
||||
func init() {
|
||||
// set default log level to Error
|
||||
mainViper.SetDefault("logging", map[string]interface{}{"level": 2})
|
||||
|
||||
utils.SetupViper(mainViper, envPrefix)
|
||||
// Setup flags
|
||||
flag.StringVar(&configFile, "config", "", "Path to configuration file")
|
||||
flag.BoolVar(&debug, "debug", false, "Enable the debugging server on localhost:8080")
|
||||
}
|
||||
|
||||
// optionally sets up TLS for the server - if no TLS configuration is
|
||||
// specified, TLS is not enabled.
|
||||
func serverTLS(configuration *viper.Viper) (*tls.Config, error) {
|
||||
tlsCertFile := configuration.GetString("server.tls_cert_file")
|
||||
tlsKeyFile := configuration.GetString("server.tls_key_file")
|
||||
|
||||
if tlsCertFile == "" && tlsKeyFile == "" {
|
||||
return nil, nil
|
||||
} else if tlsCertFile == "" || tlsKeyFile == "" {
|
||||
return nil, fmt.Errorf("Partial TLS configuration found. Either include both a cert and key file in the configuration, or include neither to disable TLS.")
|
||||
// get the address for the HTTP server, and parses the optional TLS
|
||||
// configuration for the server - if no TLS configuration is specified,
|
||||
// TLS is not enabled.
|
||||
func getAddrAndTLSConfig(configuration *viper.Viper) (string, *tls.Config, error) {
|
||||
httpAddr := configuration.GetString("server.http_addr")
|
||||
if httpAddr == "" {
|
||||
return "", nil, fmt.Errorf("http listen address required for server")
|
||||
}
|
||||
|
||||
tlsConfig, err := utils.ConfigureServerTLS(&utils.ServerTLSOpts{
|
||||
ServerCertFile: tlsCertFile,
|
||||
ServerKeyFile: tlsKeyFile,
|
||||
})
|
||||
tlsOpts, err := utils.ParseServerTLS(configuration, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to set up TLS: %s", err.Error())
|
||||
return "", nil, fmt.Errorf(err.Error())
|
||||
}
|
||||
return tlsConfig, nil
|
||||
// do not support this yet since the client doesn't have client cert support
|
||||
if tlsOpts != nil {
|
||||
tlsOpts.ClientCAFile = ""
|
||||
tlsConfig, err := utils.ConfigureServerTLS(tlsOpts)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf(
|
||||
"unable to set up TLS for server: %s", err.Error())
|
||||
}
|
||||
return httpAddr, tlsConfig, nil
|
||||
}
|
||||
return httpAddr, nil, nil
|
||||
}
|
||||
|
||||
// sets up TLS for the GRPC connection to notary-signer
|
||||
|
@ -94,6 +95,28 @@ func grpcTLS(configuration *viper.Viper) (*tls.Config, error) {
|
|||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
func getStore(configuration *viper.Viper, allowedBackends []string) (
|
||||
storage.MetaStore, error) {
|
||||
|
||||
storeConfig, err := utils.ParseStorage(configuration, allowedBackends)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storeConfig != nil {
|
||||
logrus.Infof("Using %s backend", storeConfig.Backend)
|
||||
store, err := storage.NewSQLStorage(storeConfig.Backend, storeConfig.Source)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error starting DB driver: ", err.Error())
|
||||
}
|
||||
health.RegisterPeriodicFunc(
|
||||
"DB operational", store.CheckHealth, time.Second*60)
|
||||
return store, nil
|
||||
}
|
||||
|
||||
logrus.Debug("Using memory backend")
|
||||
return storage.NewMemStorage(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
@ -115,40 +138,27 @@ func main() {
|
|||
mainViper.SetConfigName(strings.TrimSuffix(filename, ext))
|
||||
mainViper.AddConfigPath(configPath)
|
||||
|
||||
// Automatically accept configuration options from the environment
|
||||
mainViper.SetEnvPrefix("NOTARY_SERVER")
|
||||
mainViper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
mainViper.AutomaticEnv()
|
||||
|
||||
err := mainViper.ReadInConfig()
|
||||
if err != nil {
|
||||
logrus.Error("Viper Error: ", err.Error())
|
||||
logrus.Error("Could not read config at ", configFile)
|
||||
os.Exit(1)
|
||||
}
|
||||
lvl, err := logrus.ParseLevel(mainViper.GetString("logging.level"))
|
||||
|
||||
// default is error level
|
||||
lvl, err := utils.ParseLogLevel(mainViper, logrus.ErrorLevel)
|
||||
if err != nil {
|
||||
lvl = logrus.ErrorLevel
|
||||
logrus.Error("Could not parse log level from config. Defaulting to ErrorLevel")
|
||||
logrus.Fatal(err.Error())
|
||||
}
|
||||
logrus.SetLevel(lvl)
|
||||
|
||||
// set up bugsnag and attach to logrus
|
||||
bugs := mainViper.GetString("reporting.bugsnag")
|
||||
if bugs != "" {
|
||||
apiKey := mainViper.GetString("reporting.bugsnag_api_key")
|
||||
releaseStage := mainViper.GetString("reporting.bugsnag_release_stage")
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
APIKey: apiKey,
|
||||
ReleaseStage: releaseStage,
|
||||
})
|
||||
hook, err := bugsnag_hook.NewBugsnagHook()
|
||||
if err != nil {
|
||||
logrus.Error("Could not attach bugsnag to logrus: ", err.Error())
|
||||
} else {
|
||||
logrus.AddHook(hook)
|
||||
}
|
||||
// parse bugsnag config
|
||||
bugsnagConf, err := utils.ParseBugsnag(mainViper)
|
||||
if err != nil {
|
||||
logrus.Fatal(err.Error())
|
||||
}
|
||||
utils.SetUpBugsnag(bugsnagConf)
|
||||
|
||||
keyAlgo := mainViper.GetString("trust_service.key_algorithm")
|
||||
if keyAlgo == "" {
|
||||
logrus.Fatal("no key algorithm configured.")
|
||||
|
@ -188,23 +198,13 @@ func main() {
|
|||
trust = signed.NewEd25519()
|
||||
}
|
||||
|
||||
if mainViper.GetString("storage.backend") == "mysql" {
|
||||
logrus.Info("Using mysql backend")
|
||||
dbURL := mainViper.GetString("storage.db_url")
|
||||
store, err := storage.NewSQLStorage("mysql", dbURL)
|
||||
if err != nil {
|
||||
logrus.Fatal("Error starting DB driver: ", err.Error())
|
||||
return // not strictly needed but let's be explicit
|
||||
}
|
||||
health.RegisterPeriodicFunc(
|
||||
"DB operational", store.CheckHealth, time.Second*60)
|
||||
ctx = context.WithValue(ctx, "metaStore", store)
|
||||
} else {
|
||||
logrus.Debug("Using memory backend")
|
||||
ctx = context.WithValue(ctx, "metaStore", storage.NewMemStorage())
|
||||
store, err := getStore(mainViper, []string{"mysql"})
|
||||
if err != nil {
|
||||
logrus.Fatal(err.Error())
|
||||
}
|
||||
ctx = context.WithValue(ctx, "metaStore", store)
|
||||
|
||||
tlsConfig, err := serverTLS(mainViper)
|
||||
httpAddr, tlsConfig, err := getAddrAndTLSConfig(mainViper)
|
||||
if err != nil {
|
||||
logrus.Fatal(err.Error())
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ func main() {
|
|||
logrus.Info("Starting Server")
|
||||
err = server.Run(
|
||||
ctx,
|
||||
mainViper.GetString("server.addr"),
|
||||
httpAddr,
|
||||
tlsConfig,
|
||||
trust,
|
||||
mainViper.GetString("auth.type"),
|
||||
|
|
|
@ -4,9 +4,12 @@ import (
|
|||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/notary/server/storage"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -18,63 +21,71 @@ const (
|
|||
)
|
||||
|
||||
// initializes a viper object with test configuration
|
||||
func configure(jsonConfig []byte) *viper.Viper {
|
||||
func configure(jsonConfig string) *viper.Viper {
|
||||
config := viper.New()
|
||||
config.SetConfigType("json")
|
||||
config.ReadConfig(bytes.NewBuffer(jsonConfig))
|
||||
config.ReadConfig(bytes.NewBuffer([]byte(jsonConfig)))
|
||||
return config
|
||||
}
|
||||
|
||||
// If neither the cert nor the key are provided, a nil tls config is returned.
|
||||
func TestServerTLSMissingCertAndKey(t *testing.T) {
|
||||
tlsConfig, err := serverTLS(configure([]byte(`{"server": {}}`)))
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
}
|
||||
|
||||
// Cert and Key either both have to be empty or both have to be provided.
|
||||
func TestServerTLSMissingCertAndOrKey(t *testing.T) {
|
||||
configs := []string{
|
||||
fmt.Sprintf(`{"tls_cert_file": "%s"}`, Cert),
|
||||
fmt.Sprintf(`{"tls_key_file": "%s"}`, Key),
|
||||
func TestGetAddrAndTLSConfigInvalidTLS(t *testing.T) {
|
||||
invalids := []string{
|
||||
`{"server": {
|
||||
"http_addr": ":1234",
|
||||
"tls_key_file": "nope"
|
||||
}}`,
|
||||
}
|
||||
for _, serverConfig := range configs {
|
||||
config := configure(
|
||||
[]byte(fmt.Sprintf(`{"server": %s}`, serverConfig)))
|
||||
tlsConfig, err := serverTLS(config)
|
||||
for _, configJSON := range invalids {
|
||||
_, _, err := getAddrAndTLSConfig(configure(configJSON))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
assert.True(t,
|
||||
strings.Contains(err.Error(), "Partial TLS configuration found."))
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of the functionality of serverTLS 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 TestServerTLSSuccess(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": {"tls_cert_file": "%s", "tls_key_file": "%s"}}`,
|
||||
Cert, Key)
|
||||
tlsConfig, err := serverTLS(configure([]byte(config)))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
func TestGetAddrAndTLSConfigNoHTTPAddr(t *testing.T) {
|
||||
_, _, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{
|
||||
"server": {
|
||||
"tls_cert_file": "%s",
|
||||
"tls_key_file": "%s"
|
||||
}
|
||||
}`, Cert, Key)))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "http listen address required for server")
|
||||
}
|
||||
|
||||
// The rest of the functionality of serverTLS depends upon
|
||||
// utils.ConfigureServerTLS, so this test just asserts that if it fails,
|
||||
// the error is propogated.
|
||||
func TestServerTLSFailure(t *testing.T) {
|
||||
config := fmt.Sprintf(
|
||||
`{"server": {"tls_cert_file": "non-exist", "tls_key_file": "%s"}}`,
|
||||
Key)
|
||||
tlsConfig, err := serverTLS(configure([]byte(config)))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
assert.True(t, strings.Contains(err.Error(), "Unable to set up TLS"))
|
||||
func TestGetAddrAndTLSConfigSuccessWithTLS(t *testing.T) {
|
||||
httpAddr, tlsConf, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{
|
||||
"server": {
|
||||
"http_addr": ":2345",
|
||||
"tls_cert_file": "%s",
|
||||
"tls_key_file": "%s"
|
||||
}
|
||||
}`, Cert, Key)))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ":2345", httpAddr)
|
||||
assert.NotNil(t, tlsConf)
|
||||
}
|
||||
|
||||
func TestGetAddrAndTLSConfigSuccessWithoutTLS(t *testing.T) {
|
||||
httpAddr, tlsConf, err := getAddrAndTLSConfig(configure(
|
||||
`{"server": {"http_addr": ":2345"}}`))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ":2345", httpAddr)
|
||||
assert.Nil(t, tlsConf)
|
||||
}
|
||||
|
||||
// We don't support client CAs yet on notary server
|
||||
func TestGetAddrAndTLSConfigSkipClientTLS(t *testing.T) {
|
||||
httpAddr, tlsConf, err := getAddrAndTLSConfig(configure(fmt.Sprintf(`{
|
||||
"server": {
|
||||
"http_addr": ":2345",
|
||||
"tls_cert_file": "%s",
|
||||
"tls_key_file": "%s",
|
||||
"client_ca_file": "%s"
|
||||
}
|
||||
}`, Cert, Key, Root)))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, ":2345", httpAddr)
|
||||
assert.Nil(t, tlsConf.ClientCAs)
|
||||
}
|
||||
|
||||
// Client cert and Key either both have to be empty or both have to be
|
||||
|
@ -88,7 +99,7 @@ func TestGrpcTLSMissingCertOrKey(t *testing.T) {
|
|||
jsonConfig := fmt.Sprintf(
|
||||
`{"trust_service": {"hostname": "notary-signer", %s}}`,
|
||||
trustConfig)
|
||||
config := configure([]byte(jsonConfig))
|
||||
config := configure(jsonConfig)
|
||||
tlsConfig, err := grpcTLS(config)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
|
@ -101,7 +112,7 @@ func TestGrpcTLSMissingCertOrKey(t *testing.T) {
|
|||
// the provided serverName is still returned.
|
||||
func TestGrpcTLSNoConfig(t *testing.T) {
|
||||
tlsConfig, err := grpcTLS(
|
||||
configure([]byte(`{"trust_service": {"hostname": "notary-signer"}}`)))
|
||||
configure(`{"trust_service": {"hostname": "notary-signer"}}`))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "notary-signer", tlsConfig.ServerName)
|
||||
assert.Nil(t, tlsConfig.RootCAs)
|
||||
|
@ -121,7 +132,7 @@ func TestGrpcTLSSuccess(t *testing.T) {
|
|||
"tls_client_cert": "%s",
|
||||
"tls_client_key": "%s"}}`,
|
||||
Cert, Key)
|
||||
tlsConfig, err := grpcTLS(configure([]byte(config)))
|
||||
tlsConfig, err := grpcTLS(configure(config))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []tls.Certificate{keypair}, tlsConfig.Certificates)
|
||||
}
|
||||
|
@ -136,9 +147,40 @@ func TestGrpcTLSFailure(t *testing.T) {
|
|||
"tls_client_cert": "no-exist",
|
||||
"tls_client_key": "%s"}}`,
|
||||
Key)
|
||||
tlsConfig, err := grpcTLS(configure([]byte(config)))
|
||||
tlsConfig, err := grpcTLS(configure(config))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tlsConfig)
|
||||
assert.True(t, strings.Contains(err.Error(),
|
||||
"Unable to configure TLS to the trust service"))
|
||||
}
|
||||
|
||||
// Just to ensure that errors are propogated
|
||||
func TestGetStoreInvalid(t *testing.T) {
|
||||
config := `{"storage": {"backend": "asdf", "db_url": "/tmp/1234"}}`
|
||||
|
||||
_, err := getStore(configure(config), []string{"mysql"})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestGetStoreDBStore(t *testing.T) {
|
||||
tmpFile, err := ioutil.TempFile("/tmp", "sqlite3")
|
||||
assert.NoError(t, err)
|
||||
tmpFile.Close()
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
config := fmt.Sprintf(`{"storage": {"backend": "sqlite3", "db_url": "%s"}}`,
|
||||
tmpFile.Name())
|
||||
|
||||
store, err := getStore(configure(config), []string{"sqlite3"})
|
||||
assert.NoError(t, err)
|
||||
_, ok := store.(*storage.SQLStorage)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestGetMemoryStore(t *testing.T) {
|
||||
config := fmt.Sprintf(`{"storage": {}}`)
|
||||
store, err := getStore(configure(config), []string{"mysql"})
|
||||
assert.NoError(t, err)
|
||||
_, ok := store.(*storage.MemStorage)
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue