mirror of https://github.com/docker/docs.git
				
				
				
			
		
			
				
	
	
		
			258 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			258 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
| // +build pkcs11
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"database/sql"
 | |
| 	"errors"
 | |
| 	_ "expvar"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"google.golang.org/grpc"
 | |
| 	"google.golang.org/grpc/credentials"
 | |
| 
 | |
| 	"github.com/docker/distribution/health"
 | |
| 	"github.com/docker/notary/cryptoservice"
 | |
| 	"github.com/docker/notary/signer"
 | |
| 	"github.com/docker/notary/signer/api"
 | |
| 	"github.com/docker/notary/signer/keydbstore"
 | |
| 	"github.com/docker/notary/tuf/data"
 | |
| 	"github.com/docker/notary/utils"
 | |
| 	"github.com/docker/notary/version"
 | |
| 	_ "github.com/go-sql-driver/mysql"
 | |
| 	"github.com/miekg/pkcs11"
 | |
| 	"github.com/spf13/viper"
 | |
| 
 | |
| 	"github.com/Sirupsen/logrus"
 | |
| 	pb "github.com/docker/notary/proto"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	debugAddr       = "localhost:8080"
 | |
| 	dbType          = "mysql"
 | |
| 	envPrefix       = "NOTARY_SIGNER"
 | |
| 	defaultAliasEnv = "DEFAULT_ALIAS"
 | |
| 	pinCode         = "PIN"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	debug      bool
 | |
| 	configFile string
 | |
| 	mainViper  = viper.New()
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	// set default log level to Error
 | |
| 	mainViper.SetDefault("logging", map[string]interface{}{"level": 2})
 | |
| 
 | |
| 	mainViper.SetEnvPrefix(envPrefix)
 | |
| 	mainViper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
 | |
| 	mainViper.AutomaticEnv()
 | |
| 
 | |
| 	// Setup flags
 | |
| 	flag.StringVar(&configFile, "config", "", "Path to configuration file")
 | |
| 	flag.BoolVar(&debug, "debug", false, "show the version and exit")
 | |
| }
 | |
| 
 | |
| func passphraseRetriever(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
 | |
| 	passphrase = mainViper.GetString(strings.ToUpper(alias))
 | |
| 
 | |
| 	if passphrase == "" {
 | |
| 		return "", false, errors.New("expected env variable to not be empty: " + alias)
 | |
| 	}
 | |
| 
 | |
| 	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() {
 | |
| 	flag.Usage = usage
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	if debug {
 | |
| 		go debugServer(debugAddr)
 | |
| 	}
 | |
| 
 | |
| 	// when the signer starts print the version for debugging and issue logs later
 | |
| 	logrus.Infof("Version: %s, Git commit: %s", version.NotaryVersion, version.GitCommit)
 | |
| 
 | |
| 	filename := filepath.Base(configFile)
 | |
| 	ext := filepath.Ext(configFile)
 | |
| 	configPath := filepath.Dir(configFile)
 | |
| 
 | |
| 	mainViper.SetConfigType(strings.TrimPrefix(ext, "."))
 | |
| 	mainViper.SetConfigName(strings.TrimSuffix(filename, ext))
 | |
| 	mainViper.AddConfigPath(configPath)
 | |
| 	err := mainViper.ReadInConfig()
 | |
| 	if err != nil {
 | |
| 		logrus.Error("Viper Error: ", err.Error())
 | |
| 		logrus.Error("Could not read config at ", configFile)
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| 
 | |
| 	logrus.SetLevel(logrus.Level(mainViper.GetInt("logging.level")))
 | |
| 
 | |
| 	tlsConfig, err := signerTLS(mainViper, true)
 | |
| 	if err != nil {
 | |
| 		logrus.Fatalf(err.Error())
 | |
| 	}
 | |
| 
 | |
| 	cryptoServices := make(signer.CryptoServiceIndex)
 | |
| 
 | |
| 	configDBType := strings.ToLower(mainViper.GetString("storage.backend"))
 | |
| 	dbURL := mainViper.GetString("storage.db_url")
 | |
| 	if configDBType != dbType || dbURL == "" {
 | |
| 		usage()
 | |
| 		log.Fatalf("Currently only a MySQL database backend is supported.")
 | |
| 	}
 | |
| 	dbSQL, err := sql.Open(configDBType, dbURL)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("failed to open the database: %s, %v", dbURL, err)
 | |
| 	}
 | |
| 
 | |
| 	defaultAlias := mainViper.GetString(defaultAliasEnv)
 | |
| 	logrus.Debug("Default Alias: ", defaultAlias)
 | |
| 	keyStore, err := keydbstore.NewKeyDBStore(passphraseRetriever, defaultAlias, configDBType, dbSQL)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("failed to create a new keydbstore: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	health.RegisterPeriodicFunc(
 | |
| 		"DB operational", keyStore.HealthCheck, time.Second*60)
 | |
| 
 | |
| 	cryptoService := cryptoservice.NewCryptoService("", keyStore)
 | |
| 
 | |
| 	cryptoServices[data.ED25519Key] = cryptoService
 | |
| 	cryptoServices[data.ECDSAKey] = cryptoService
 | |
| 
 | |
| 	//RPC server setup
 | |
| 	kms := &api.KeyManagementServer{CryptoServices: cryptoServices,
 | |
| 		HealthChecker: health.CheckStatus}
 | |
| 	ss := &api.SignerServer{CryptoServices: cryptoServices,
 | |
| 		HealthChecker: health.CheckStatus}
 | |
| 
 | |
| 	rpcAddr := mainViper.GetString("server.grpc_addr")
 | |
| 	lis, err := net.Listen("tcp", rpcAddr)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("failed to listen %v", err)
 | |
| 	}
 | |
| 	creds := credentials.NewTLS(tlsConfig)
 | |
| 	opts := []grpc.ServerOption{grpc.Creds(creds)}
 | |
| 	grpcServer := grpc.NewServer(opts...)
 | |
| 
 | |
| 	pb.RegisterKeyManagementServer(grpcServer, kms)
 | |
| 	pb.RegisterSignerServer(grpcServer, ss)
 | |
| 
 | |
| 	go grpcServer.Serve(lis)
 | |
| 
 | |
| 	httpAddr := mainViper.GetString("server.http_addr")
 | |
| 	if httpAddr == "" {
 | |
| 		log.Fatalf("Server address is required")
 | |
| 	}
 | |
| 	//HTTP server setup
 | |
| 	server := http.Server{
 | |
| 		Addr:      httpAddr,
 | |
| 		Handler:   api.Handlers(cryptoServices),
 | |
| 		TLSConfig: tlsConfig,
 | |
| 	}
 | |
| 
 | |
| 	if debug {
 | |
| 		log.Println("RPC server listening on", rpcAddr)
 | |
| 		log.Println("HTTP server listening on", httpAddr)
 | |
| 	}
 | |
| 
 | |
| 	err = server.ListenAndServeTLS("", "")
 | |
| 	if err != nil {
 | |
| 		log.Fatal("HTTPS server failed to start:", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func usage() {
 | |
| 	log.Println("usage:", os.Args[0], "<config>")
 | |
| 	flag.PrintDefaults()
 | |
| }
 | |
| 
 | |
| // debugServer starts the debug server with pprof, expvar among other
 | |
| // endpoints. The addr should not be exposed externally. For most of these to
 | |
| // work, tls cannot be enabled on the endpoint, so it is generally separate.
 | |
| func debugServer(addr string) {
 | |
| 	log.Println("Debug server listening on", addr)
 | |
| 	if err := http.ListenAndServe(addr, nil); err != nil {
 | |
| 		log.Fatalf("error listening on debug interface: %v", err)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // SetupHSMEnv is a method that depends on the existences
 | |
| func SetupHSMEnv(libraryPath, pin string) (*pkcs11.Ctx, pkcs11.SessionHandle) {
 | |
| 	p := pkcs11.New(libraryPath)
 | |
| 
 | |
| 	if p == nil {
 | |
| 		log.Fatalf("Failed to init library")
 | |
| 	}
 | |
| 
 | |
| 	if err := p.Initialize(); err != nil {
 | |
| 		log.Fatalf("Initialize error %s\n", err.Error())
 | |
| 	}
 | |
| 
 | |
| 	slots, err := p.GetSlotList(true)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("Failed to list HSM slots %s", err)
 | |
| 	}
 | |
| 	// Check to see if we got any slots from the HSM.
 | |
| 	if len(slots) < 1 {
 | |
| 		log.Fatalln("No HSM Slots found")
 | |
| 	}
 | |
| 
 | |
| 	// CKF_SERIAL_SESSION: TRUE if cryptographic functions are performed in serial with the application; FALSE if the functions may be performed in parallel with the application.
 | |
| 	// CKF_RW_SESSION: TRUE if the session is read/write; FALSE if the session is read-only
 | |
| 	session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
 | |
| 	if err != nil {
 | |
| 		log.Fatalf("Failed to Start Session with HSM %s", err)
 | |
| 	}
 | |
| 
 | |
| 	if err = p.Login(session, pkcs11.CKU_USER, pin); err != nil {
 | |
| 		log.Fatalf("User PIN %s\n", err.Error())
 | |
| 	}
 | |
| 
 | |
| 	return p, session
 | |
| }
 | |
| 
 | |
| func cleanup(ctx *pkcs11.Ctx, session pkcs11.SessionHandle) {
 | |
| 	ctx.Destroy()
 | |
| 	ctx.Finalize()
 | |
| 	ctx.CloseSession(session)
 | |
| 	ctx.Logout(session)
 | |
| }
 |