mirror of https://github.com/docker/docs.git
259 lines
8.5 KiB
Go
259 lines
8.5 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
_ "github.com/docker/distribution/registry/auth/htpasswd"
|
|
_ "github.com/docker/distribution/registry/auth/token"
|
|
"github.com/docker/go-connections/tlsconfig"
|
|
"github.com/docker/notary"
|
|
"github.com/docker/notary/server"
|
|
"github.com/docker/notary/server/storage"
|
|
"github.com/docker/notary/signer/client"
|
|
"github.com/docker/notary/storage/rethinkdb"
|
|
"github.com/docker/notary/tuf/data"
|
|
"github.com/docker/notary/tuf/signed"
|
|
"github.com/docker/notary/utils"
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"github.com/spf13/viper"
|
|
"golang.org/x/net/context"
|
|
"gopkg.in/dancannon/gorethink.v2"
|
|
)
|
|
|
|
// 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.ParseServerTLS(configuration, false)
|
|
if err != nil {
|
|
return "", nil, fmt.Errorf(err.Error())
|
|
}
|
|
return httpAddr, tlsConfig, nil
|
|
}
|
|
|
|
// sets up TLS for the GRPC connection to notary-signer
|
|
func grpcTLS(configuration *viper.Viper) (*tls.Config, error) {
|
|
rootCA := utils.GetPathRelativeToConfig(configuration, "trust_service.tls_ca_file")
|
|
clientCert := utils.GetPathRelativeToConfig(configuration, "trust_service.tls_client_cert")
|
|
clientKey := utils.GetPathRelativeToConfig(configuration, "trust_service.tls_client_key")
|
|
|
|
if clientCert == "" && clientKey != "" || clientCert != "" && clientKey == "" {
|
|
return nil, fmt.Errorf("either pass both client key and cert, or neither")
|
|
}
|
|
|
|
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
|
|
CAFile: rootCA,
|
|
CertFile: clientCert,
|
|
KeyFile: clientKey,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"Unable to configure TLS to the trust service: %s", err.Error())
|
|
}
|
|
return tlsConfig, nil
|
|
}
|
|
|
|
// parses the configuration and returns a backing store for the TUF files
|
|
func getStore(configuration *viper.Viper, hRegister healthRegister) (
|
|
storage.MetaStore, error) {
|
|
var store storage.MetaStore
|
|
backend := configuration.GetString("storage.backend")
|
|
logrus.Infof("Using %s backend", backend)
|
|
|
|
switch backend {
|
|
case notary.MemoryBackend:
|
|
return storage.NewMemStorage(), nil
|
|
case notary.MySQLBackend, notary.SQLiteBackend:
|
|
storeConfig, err := utils.ParseSQLStorage(configuration)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s, err := storage.NewSQLStorage(storeConfig.Backend, storeConfig.Source)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error())
|
|
}
|
|
store = *storage.NewTUFMetaStorage(s)
|
|
hRegister("DB operational", s.CheckHealth, time.Minute)
|
|
case notary.RethinkDBBackend:
|
|
var sess *gorethink.Session
|
|
storeConfig, err := utils.ParseRethinkDBStorage(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, notary.NotaryServerUser)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error starting %s driver: %s", backend, err.Error())
|
|
}
|
|
s := storage.NewRethinkDBStorage(storeConfig.DBName, sess)
|
|
store = *storage.NewTUFMetaStorage(s)
|
|
hRegister("DB operational", s.CheckHealth, time.Minute)
|
|
default:
|
|
return nil, fmt.Errorf("%s is not a supported storage backend", backend)
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
type signerFactory func(hostname, port string, tlsConfig *tls.Config) *client.NotarySigner
|
|
type healthRegister func(name string, checkFunc func() error, duration time.Duration)
|
|
|
|
// parses the configuration and determines which trust service and key algorithm
|
|
// to return
|
|
func getTrustService(configuration *viper.Viper, sFactory signerFactory,
|
|
hRegister healthRegister) (signed.CryptoService, string, error) {
|
|
|
|
switch configuration.GetString("trust_service.type") {
|
|
case "local":
|
|
logrus.Info("Using local signing service, which requires ED25519. " +
|
|
"Ignoring all other trust_service parameters, including keyAlgorithm")
|
|
return signed.NewEd25519(), data.ED25519Key, nil
|
|
case "remote":
|
|
default:
|
|
return nil, "", fmt.Errorf(
|
|
"must specify either a \"local\" or \"remote\" type for trust_service")
|
|
}
|
|
|
|
keyAlgo := configuration.GetString("trust_service.key_algorithm")
|
|
if keyAlgo != data.ED25519Key && keyAlgo != data.ECDSAKey && keyAlgo != data.RSAKey {
|
|
return nil, "", fmt.Errorf("invalid key algorithm configured: %s", keyAlgo)
|
|
}
|
|
|
|
clientTLS, err := grpcTLS(configuration)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
logrus.Info("Using remote signing service")
|
|
|
|
notarySigner := sFactory(
|
|
configuration.GetString("trust_service.hostname"),
|
|
configuration.GetString("trust_service.port"),
|
|
clientTLS,
|
|
)
|
|
|
|
minute := 1 * time.Minute
|
|
hRegister(
|
|
"Trust operational",
|
|
// If the trust service fails, the server is degraded but not
|
|
// exactly unhealthy, so always return healthy and just log an
|
|
// error.
|
|
func() error {
|
|
err := notarySigner.CheckHealth(minute)
|
|
if err != nil {
|
|
logrus.Error("Trust not fully operational: ", err.Error())
|
|
}
|
|
return nil
|
|
},
|
|
minute)
|
|
return notarySigner, keyAlgo, nil
|
|
}
|
|
|
|
// Parse the cache configurations for GET-ting current and checksummed metadata,
|
|
// returning the configuration for current (non-content-addressed) metadata
|
|
// first, then the configuration for consistent (content-addressed) metadata
|
|
// second. The configuration consists mainly of the max-age (an integer in seconds,
|
|
// just like in the Cache-Control header) for each type of metadata.
|
|
// The max-age must be between 0 and 31536000 (one year in seconds, which is
|
|
// the recommended maximum time data is cached), else parsing will return an error.
|
|
// A max-age of 0 will disable caching for that type of download (consistent or current).
|
|
func getCacheConfig(configuration *viper.Viper) (current, consistent utils.CacheControlConfig, err error) {
|
|
cccs := make(map[string]utils.CacheControlConfig)
|
|
currentOpt, consistentOpt := "current_metadata", "consistent_metadata"
|
|
|
|
defaults := map[string]int{
|
|
currentOpt: int(notary.CurrentMetadataCacheMaxAge.Seconds()),
|
|
consistentOpt: int(notary.ConsistentMetadataCacheMaxAge.Seconds()),
|
|
}
|
|
maxMaxAge := int(notary.CacheMaxAgeLimit.Seconds())
|
|
|
|
for optionName, seconds := range defaults {
|
|
m := configuration.GetString(fmt.Sprintf("caching.max_age.%s", optionName))
|
|
if m != "" {
|
|
seconds, err = strconv.Atoi(m)
|
|
if err != nil || seconds < 0 || seconds > maxMaxAge {
|
|
return nil, nil, fmt.Errorf(
|
|
"must specify a cache-control max-age between 0 and %v", maxMaxAge)
|
|
}
|
|
}
|
|
cccs[optionName] = utils.NewCacheControlConfig(seconds, optionName == currentOpt)
|
|
}
|
|
current = cccs[currentOpt]
|
|
consistent = cccs[consistentOpt]
|
|
return
|
|
}
|
|
|
|
func parseServerConfig(configFilePath string, hRegister healthRegister) (context.Context, server.Config, error) {
|
|
config := viper.New()
|
|
utils.SetupViper(config, envPrefix)
|
|
|
|
// parse viper config
|
|
if err := utils.ParseViper(config, configFilePath); err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// default is error level
|
|
lvl, err := utils.ParseLogLevel(config, logrus.ErrorLevel)
|
|
if err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
logrus.SetLevel(lvl)
|
|
|
|
// parse bugsnag config
|
|
bugsnagConf, err := utils.ParseBugsnag(config)
|
|
if err != nil {
|
|
return ctx, server.Config{}, err
|
|
}
|
|
utils.SetUpBugsnag(bugsnagConf)
|
|
|
|
trust, keyAlgo, err := getTrustService(config, client.NewNotarySigner, hRegister)
|
|
if err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
ctx = context.WithValue(ctx, "keyAlgorithm", keyAlgo)
|
|
|
|
store, err := getStore(config, hRegister)
|
|
if err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
ctx = context.WithValue(ctx, "metaStore", store)
|
|
|
|
currentCache, consistentCache, err := getCacheConfig(config)
|
|
if err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
|
|
httpAddr, tlsConfig, err := getAddrAndTLSConfig(config)
|
|
if err != nil {
|
|
return nil, server.Config{}, err
|
|
}
|
|
|
|
return ctx, server.Config{
|
|
Addr: httpAddr,
|
|
TLSConfig: tlsConfig,
|
|
Trust: trust,
|
|
AuthMethod: config.GetString("auth.type"),
|
|
AuthOpts: config.Get("auth.options"),
|
|
CurrentCacheControlConfig: currentCache,
|
|
ConsistentCacheControlConfig: consistentCache,
|
|
}, nil
|
|
}
|