// Copyright (C) 2019 Storj Labs, Inc. // See LICENSE for copying information. package certificates import ( "context" "os" "github.com/zeebo/errs" "go.uber.org/zap" "golang.org/x/sync/errgroup" "storj.io/storj/pkg/identity" "storj.io/storj/pkg/pb" "storj.io/storj/pkg/peertls" "storj.io/storj/pkg/peertls/tlsopts" "storj.io/storj/pkg/server" "storj.io/storj/pkg/transport" "storj.io/storj/pkg/utils" "storj.io/storj/storage/boltdb" "storj.io/storj/storage/redis" ) // CertClientConfig is a config struct for use with a certificate signing service client type CertClientConfig struct { Address string `help:"address of the certificate signing rpc service"` TLS tlsopts.Config } // CertServerConfig is a config struct for use with a certificate signing service server type CertServerConfig struct { Overwrite bool `default:"false" help:"if true, overwrites config AND authorization db is truncated"` AuthorizationDBURL string `default:"bolt://$CONFDIR/authorizations.db" help:"url to the certificate signing authorization database"` MinDifficulty uint `default:"30" help:"minimum difficulty of the requester's identity required to claim an authorization"` CA identity.FullCAConfig } // Sign submits a certificate signing request given the config func (c CertClientConfig) Sign(ctx context.Context, ident *identity.FullIdentity, authToken string) ([][]byte, error) { tlsOpts, err := tlsopts.NewOptions(ident, c.TLS) if err != nil { return nil, err } client, err := NewClient(ctx, transport.NewClient(tlsOpts), c.Address) if err != nil { return nil, err } return client.Sign(ctx, authToken) } // NewAuthDB creates or opens the authorization database specified by the config func (c CertServerConfig) NewAuthDB() (*AuthorizationDB, error) { // TODO: refactor db selection logic? driver, source, err := utils.SplitDBURL(c.AuthorizationDBURL) if err != nil { return nil, peertls.ErrRevocationDB.Wrap(err) } authDB := new(AuthorizationDB) switch driver { case "bolt": _, err := os.Stat(source) if c.Overwrite && err == nil { if err := os.Remove(source); err != nil { return nil, err } } authDB.DB, err = boltdb.New(source, AuthorizationsBucket) if err != nil { return nil, ErrAuthorizationDB.Wrap(err) } case "redis": redisClient, err := redis.NewClientFrom(c.AuthorizationDBURL) if err != nil { return nil, ErrAuthorizationDB.Wrap(err) } if c.Overwrite { if err := redisClient.FlushDB(); err != nil { return nil, err } } authDB.DB = redisClient default: return nil, ErrAuthorizationDB.New("database scheme not supported: %s", driver) } return authDB, nil } // Run implements the responsibility interface, starting a certificate signing server. func (c CertServerConfig) Run(ctx context.Context, srv *server.Server) (err error) { defer mon.Task()(&ctx)(&err) authDB, err := c.NewAuthDB() if err != nil { return err } defer func() { err = errs.Combine(err, authDB.Close()) }() signer, err := c.CA.Load() if err != nil { return err } certSrv := NewServer( zap.L(), signer, authDB, uint16(c.MinDifficulty), ) pb.RegisterCertificatesServer(srv.GRPC(), certSrv) certSrv.log.Info( "Certificate signing server running", zap.String("address", srv.Addr().String()), ) ctx, cancel := context.WithCancel(ctx) var group errgroup.Group group.Go(func() error { defer cancel() <-ctx.Done() return srv.Close() }) group.Go(func() error { defer cancel() return srv.Run(ctx) }) return group.Wait() }