mirror of https://github.com/docker/docs.git
252 lines
7.0 KiB
Go
252 lines
7.0 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/distribution/health"
|
|
"github.com/docker/go-connections/tlsconfig"
|
|
"github.com/docker/notary"
|
|
"github.com/docker/notary/cryptoservice"
|
|
"github.com/docker/notary/passphrase"
|
|
pb "github.com/docker/notary/proto"
|
|
"github.com/docker/notary/signer"
|
|
"github.com/docker/notary/signer/api"
|
|
"github.com/docker/notary/signer/keydbstore"
|
|
"github.com/docker/notary/storage"
|
|
"github.com/docker/notary/storage/rethinkdb"
|
|
"github.com/docker/notary/trustmanager"
|
|
"github.com/docker/notary/tuf/data"
|
|
tufutils "github.com/docker/notary/tuf/utils"
|
|
"github.com/docker/notary/utils"
|
|
"github.com/spf13/viper"
|
|
"gopkg.in/dancannon/gorethink.v2"
|
|
)
|
|
|
|
const (
|
|
envPrefix = "NOTARY_SIGNER"
|
|
defaultAliasEnv = "DEFAULT_ALIAS"
|
|
)
|
|
|
|
func parseSignerConfig(configFilePath string) (signer.Config, error) {
|
|
config := viper.New()
|
|
utils.SetupViper(config, envPrefix)
|
|
|
|
// parse viper config
|
|
if err := utils.ParseViper(config, configFilePath); err != nil {
|
|
return signer.Config{}, err
|
|
}
|
|
|
|
// default is error level
|
|
lvl, err := utils.ParseLogLevel(config, logrus.ErrorLevel)
|
|
if err != nil {
|
|
return signer.Config{}, err
|
|
}
|
|
logrus.SetLevel(lvl)
|
|
|
|
// parse bugsnag config
|
|
bugsnagConf, err := utils.ParseBugsnag(config)
|
|
if err != nil {
|
|
return signer.Config{}, err
|
|
}
|
|
utils.SetUpBugsnag(bugsnagConf)
|
|
|
|
// parse server config
|
|
httpAddr, grpcAddr, tlsConfig, err := getAddrAndTLSConfig(config)
|
|
if err != nil {
|
|
return signer.Config{}, err
|
|
}
|
|
|
|
// setup the cryptoservices
|
|
cryptoServices, err := setUpCryptoservices(config, []string{notary.MySQLBackend, notary.MemoryBackend, notary.RethinkDBBackend})
|
|
if err != nil {
|
|
return signer.Config{}, err
|
|
}
|
|
|
|
return signer.Config{
|
|
HTTPAddr: httpAddr,
|
|
GRPCAddr: grpcAddr,
|
|
TLSConfig: tlsConfig,
|
|
CryptoServices: cryptoServices,
|
|
}, nil
|
|
}
|
|
|
|
func getEnv(env string) string {
|
|
v := viper.New()
|
|
utils.SetupViper(v, envPrefix)
|
|
return v.GetString(strings.ToUpper(env))
|
|
}
|
|
|
|
func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
|
|
passphrase = getEnv(alias)
|
|
|
|
if passphrase == "" {
|
|
return "", false, errors.New("expected env variable to not be empty: " + alias)
|
|
}
|
|
|
|
return passphrase, false, nil
|
|
}
|
|
|
|
// Reads the configuration file for storage setup, and sets up the cryptoservice
|
|
// mapping
|
|
func setUpCryptoservices(configuration *viper.Viper, allowedBackends []string) (
|
|
signer.CryptoServiceIndex, error) {
|
|
backend := configuration.GetString("storage.backend")
|
|
|
|
if !tufutils.StrSliceContains(allowedBackends, backend) {
|
|
return nil, fmt.Errorf("%s is not an allowed backend, must be one of: %s", backend, allowedBackends)
|
|
}
|
|
|
|
var keyStore trustmanager.KeyStore
|
|
switch backend {
|
|
case notary.MemoryBackend:
|
|
keyStore = trustmanager.NewKeyMemoryStore(
|
|
passphrase.ConstantRetriever("memory-db-ignore"))
|
|
case notary.RethinkDBBackend:
|
|
var sess *gorethink.Session
|
|
storeConfig, err := utils.ParseRethinkDBStorage(configuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defaultAlias, err := getDefaultAlias(configuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tlsOpts := tlsconfig.Options{
|
|
CAFile: storeConfig.CA,
|
|
CertFile: storeConfig.Cert,
|
|
KeyFile: storeConfig.Key,
|
|
}
|
|
if doBootstrap {
|
|
sess, err = rethinkdb.AdminConnection(tlsOpts, storeConfig.Source)
|
|
} else {
|
|
sess, err = rethinkdb.UserConnection(tlsOpts, storeConfig.Source, storeConfig.Username, storeConfig.Password)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error())
|
|
}
|
|
s := keydbstore.NewRethinkDBKeyStore(storeConfig.DBName, storeConfig.Username, storeConfig.Password, passphraseRetriever, defaultAlias, sess)
|
|
health.RegisterPeriodicFunc("DB operational", s.CheckHealth, time.Minute)
|
|
keyStore = s
|
|
case notary.MySQLBackend, notary.SQLiteBackend:
|
|
storeConfig, err := utils.ParseSQLStorage(configuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defaultAlias, err := getDefaultAlias(configuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dbStore, err := keydbstore.NewKeyDBStore(
|
|
passphraseRetriever, defaultAlias, storeConfig.Backend, storeConfig.Source)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create a new keydbstore: %v", err)
|
|
}
|
|
|
|
health.RegisterPeriodicFunc(
|
|
"DB operational", dbStore.HealthCheck, time.Minute)
|
|
keyStore = dbStore
|
|
}
|
|
|
|
if doBootstrap {
|
|
err := bootstrap(keyStore)
|
|
if err != nil {
|
|
logrus.Fatal(err.Error())
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
|
|
cryptoService := cryptoservice.NewCryptoService(keyStore)
|
|
cryptoServices := make(signer.CryptoServiceIndex)
|
|
cryptoServices[data.ED25519Key] = cryptoService
|
|
cryptoServices[data.ECDSAKey] = cryptoService
|
|
return cryptoServices, nil
|
|
}
|
|
|
|
func getDefaultAlias(configuration *viper.Viper) (string, error) {
|
|
defaultAlias := configuration.GetString("storage.default_alias")
|
|
if defaultAlias == "" {
|
|
// backwards compatibility - support this environment variable
|
|
defaultAlias = configuration.GetString(defaultAliasEnv)
|
|
}
|
|
|
|
if defaultAlias == "" {
|
|
return "", fmt.Errorf("must provide a default alias for the key DB")
|
|
}
|
|
logrus.Debug("Default Alias: ", defaultAlias)
|
|
return defaultAlias, nil
|
|
}
|
|
|
|
// set up the GRPC server
|
|
func setupGRPCServer(grpcAddr string, tlsConfig *tls.Config,
|
|
cryptoServices signer.CryptoServiceIndex) (*grpc.Server, net.Listener, error) {
|
|
|
|
//RPC server setup
|
|
kms := &api.KeyManagementServer{CryptoServices: cryptoServices,
|
|
HealthChecker: health.CheckStatus}
|
|
ss := &api.SignerServer{CryptoServices: cryptoServices,
|
|
HealthChecker: health.CheckStatus}
|
|
|
|
lis, err := net.Listen("tcp", grpcAddr)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("grpc server failed to listen on %s: %v",
|
|
grpcAddr, err)
|
|
}
|
|
|
|
creds := credentials.NewTLS(tlsConfig)
|
|
opts := []grpc.ServerOption{grpc.Creds(creds)}
|
|
grpcServer := grpc.NewServer(opts...)
|
|
|
|
pb.RegisterKeyManagementServer(grpcServer, kms)
|
|
pb.RegisterSignerServer(grpcServer, ss)
|
|
|
|
return grpcServer, lis, nil
|
|
}
|
|
|
|
func setupHTTPServer(httpAddr string, tlsConfig *tls.Config,
|
|
cryptoServices signer.CryptoServiceIndex) *http.Server {
|
|
|
|
return &http.Server{
|
|
Addr: httpAddr,
|
|
Handler: api.Handlers(cryptoServices),
|
|
TLSConfig: tlsConfig,
|
|
}
|
|
}
|
|
|
|
func getAddrAndTLSConfig(configuration *viper.Viper) (string, string, *tls.Config, error) {
|
|
tlsConfig, err := utils.ParseServerTLS(configuration, true)
|
|
if err != nil {
|
|
return "", "", nil, fmt.Errorf("unable to set up TLS: %s", err.Error())
|
|
}
|
|
|
|
grpcAddr := configuration.GetString("server.grpc_addr")
|
|
if grpcAddr == "" {
|
|
return "", "", nil, fmt.Errorf("grpc listen address required for server")
|
|
}
|
|
|
|
httpAddr := configuration.GetString("server.http_addr")
|
|
if httpAddr == "" {
|
|
return "", "", nil, fmt.Errorf("http listen address required for server")
|
|
}
|
|
|
|
return httpAddr, grpcAddr, tlsConfig, nil
|
|
}
|
|
|
|
func bootstrap(s interface{}) error {
|
|
store, ok := s.(storage.Bootstrapper)
|
|
if !ok {
|
|
return fmt.Errorf("Store does not support bootstrapping.")
|
|
}
|
|
return store.Bootstrap()
|
|
}
|