2018-10-05 21:48:36 +00:00
|
|
|
package authority
|
|
|
|
|
|
|
|
import (
|
2019-12-10 07:14:56 +00:00
|
|
|
"context"
|
2020-11-03 13:26:46 +00:00
|
|
|
"crypto"
|
2018-10-05 21:48:36 +00:00
|
|
|
"crypto/sha256"
|
2019-01-05 01:51:32 +00:00
|
|
|
"crypto/x509"
|
2018-10-05 21:48:36 +00:00
|
|
|
"encoding/hex"
|
2020-05-08 01:59:30 +00:00
|
|
|
"log"
|
2018-10-05 21:48:36 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2020-09-09 02:26:32 +00:00
|
|
|
"github.com/smallstep/certificates/cas"
|
2021-05-26 04:13:01 +00:00
|
|
|
"github.com/smallstep/certificates/linkedca"
|
2020-09-09 02:26:32 +00:00
|
|
|
|
2019-08-01 22:04:56 +00:00
|
|
|
"github.com/pkg/errors"
|
2021-05-18 04:07:25 +00:00
|
|
|
"github.com/smallstep/certificates/authority/admin"
|
2021-05-03 19:48:20 +00:00
|
|
|
"github.com/smallstep/certificates/authority/config"
|
|
|
|
"github.com/smallstep/certificates/authority/mgmt"
|
|
|
|
authMgmtNosql "github.com/smallstep/certificates/authority/mgmt/db/nosql"
|
2019-03-06 23:00:23 +00:00
|
|
|
"github.com/smallstep/certificates/authority/provisioner"
|
2020-09-09 02:26:32 +00:00
|
|
|
casapi "github.com/smallstep/certificates/cas/apiv1"
|
2019-03-05 08:07:13 +00:00
|
|
|
"github.com/smallstep/certificates/db"
|
2020-01-10 02:42:26 +00:00
|
|
|
"github.com/smallstep/certificates/kms"
|
|
|
|
kmsapi "github.com/smallstep/certificates/kms/apiv1"
|
2020-11-03 13:26:46 +00:00
|
|
|
"github.com/smallstep/certificates/kms/sshagentkms"
|
2019-11-21 01:01:31 +00:00
|
|
|
"github.com/smallstep/certificates/templates"
|
2021-05-03 19:48:20 +00:00
|
|
|
"github.com/smallstep/nosql"
|
2020-08-14 22:33:50 +00:00
|
|
|
"go.step.sm/crypto/pemutil"
|
2019-09-25 02:12:13 +00:00
|
|
|
"golang.org/x/crypto/ssh"
|
2018-10-05 21:48:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Authority implements the Certificate Authority internal interface.
|
|
|
|
type Authority struct {
|
2021-05-03 19:48:20 +00:00
|
|
|
config *config.Config
|
2021-05-20 20:01:58 +00:00
|
|
|
adminDB mgmt.DB
|
2020-01-11 02:33:48 +00:00
|
|
|
keyManager kms.KeyManager
|
|
|
|
provisioners *provisioner.Collection
|
2021-05-18 04:07:25 +00:00
|
|
|
admins *admin.Collection
|
2020-01-11 02:33:48 +00:00
|
|
|
db db.AuthDB
|
2020-06-17 00:57:35 +00:00
|
|
|
templates *templates.Templates
|
2020-01-11 02:33:48 +00:00
|
|
|
|
|
|
|
// X509 CA
|
2020-09-09 02:26:32 +00:00
|
|
|
x509CAService cas.CertificateAuthorityService
|
2020-01-11 02:33:48 +00:00
|
|
|
rootX509Certs []*x509.Certificate
|
|
|
|
federatedX509Certs []*x509.Certificate
|
|
|
|
certificates *sync.Map
|
|
|
|
|
|
|
|
// SSH CA
|
2019-10-09 01:09:41 +00:00
|
|
|
sshCAUserCertSignKey ssh.Signer
|
|
|
|
sshCAHostCertSignKey ssh.Signer
|
|
|
|
sshCAUserCerts []ssh.PublicKey
|
|
|
|
sshCAHostCerts []ssh.PublicKey
|
|
|
|
sshCAUserFederatedCerts []ssh.PublicKey
|
|
|
|
sshCAHostFederatedCerts []ssh.PublicKey
|
2020-01-11 02:33:48 +00:00
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
// Do not re-initialize
|
2020-01-11 02:33:48 +00:00
|
|
|
initOnce bool
|
|
|
|
startTime time.Time
|
|
|
|
|
2019-11-15 02:24:58 +00:00
|
|
|
// Custom functions
|
2021-05-03 19:48:20 +00:00
|
|
|
sshBastionFunc func(ctx context.Context, user, hostname string) (*config.Bastion, error)
|
2019-12-10 07:14:56 +00:00
|
|
|
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)
|
2021-05-03 19:48:20 +00:00
|
|
|
sshGetHostsFunc func(ctx context.Context, cert *x509.Certificate) ([]config.Host, error)
|
2019-12-10 07:14:56 +00:00
|
|
|
getIdentityFunc provisioner.GetIdentityFunc
|
2018-10-05 21:48:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates and initiates a new Authority type.
|
2021-05-03 19:48:20 +00:00
|
|
|
func New(config *config.Config, opts ...Option) (*Authority, error) {
|
2018-10-25 22:40:12 +00:00
|
|
|
err := config.Validate()
|
|
|
|
if err != nil {
|
2018-10-05 21:48:36 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-25 22:40:12 +00:00
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
var a = &Authority{
|
2019-03-06 23:00:23 +00:00
|
|
|
config: config,
|
|
|
|
certificates: new(sync.Map),
|
2018-10-05 21:48:36 +00:00
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
|
|
|
|
// Apply options.
|
|
|
|
for _, fn := range opts {
|
|
|
|
if err := fn(a); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-05-10 22:58:37 +00:00
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
|
|
|
|
// Initialize authority from options or configuration.
|
2018-10-05 21:48:36 +00:00
|
|
|
if err := a.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2020-05-06 00:46:22 +00:00
|
|
|
// NewEmbedded initializes an authority that can be embedded in a different
|
|
|
|
// project without the limitations of the config.
|
|
|
|
func NewEmbedded(opts ...Option) (*Authority, error) {
|
2020-05-05 01:52:18 +00:00
|
|
|
a := &Authority{
|
2021-05-03 19:48:20 +00:00
|
|
|
config: &config.Config{},
|
2020-05-05 01:52:18 +00:00
|
|
|
certificates: new(sync.Map),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply options.
|
|
|
|
for _, fn := range opts {
|
|
|
|
if err := fn(a); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate required options
|
|
|
|
switch {
|
2020-05-06 20:00:42 +00:00
|
|
|
case a.config == nil:
|
|
|
|
return nil, errors.New("cannot create an authority without a configuration")
|
2020-05-05 01:52:18 +00:00
|
|
|
case len(a.rootX509Certs) == 0 && a.config.Root.HasEmpties():
|
|
|
|
return nil, errors.New("cannot create an authority without a root certificate")
|
2020-09-16 20:31:26 +00:00
|
|
|
case a.x509CAService == nil && a.config.IntermediateCert == "":
|
2020-05-05 01:52:18 +00:00
|
|
|
return nil, errors.New("cannot create an authority without an issuer certificate")
|
2020-09-16 20:31:26 +00:00
|
|
|
case a.x509CAService == nil && a.config.IntermediateKey == "":
|
2020-05-05 01:52:18 +00:00
|
|
|
return nil, errors.New("cannot create an authority without an issuer signer")
|
|
|
|
}
|
|
|
|
|
2020-05-06 20:00:42 +00:00
|
|
|
// Initialize config required fields.
|
2021-05-03 19:48:20 +00:00
|
|
|
a.config.Init()
|
2020-05-06 20:00:42 +00:00
|
|
|
|
2020-05-05 01:52:18 +00:00
|
|
|
// Initialize authority from options or configuration.
|
|
|
|
if err := a.init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:55:31 +00:00
|
|
|
// ReloadAuthConfig reloads dynamic fields of the AuthConfig.
|
2021-05-26 04:13:01 +00:00
|
|
|
func (a *Authority) ReloadAuthConfig(ctx context.Context) error {
|
|
|
|
provs, err := a.adminDB.GetProvisioners(ctx)
|
2021-05-18 04:07:25 +00:00
|
|
|
if err != nil {
|
2021-05-26 04:13:01 +00:00
|
|
|
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority")
|
2021-05-18 04:07:25 +00:00
|
|
|
}
|
2021-05-26 04:13:01 +00:00
|
|
|
a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs)
|
|
|
|
if err != nil {
|
|
|
|
return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates")
|
|
|
|
}
|
|
|
|
a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(ctx)
|
2021-05-18 04:07:25 +00:00
|
|
|
if err != nil {
|
2021-05-26 04:13:01 +00:00
|
|
|
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority")
|
2021-05-18 04:07:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Merge global and configuration claims
|
|
|
|
claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// TODO: should we also be combining the ssh federated roots here?
|
|
|
|
// If we rotate ssh roots keys, sshpop provisioner will lose ability to
|
|
|
|
// validate old SSH certificates, unless they are added as federated certs.
|
2021-05-26 04:13:01 +00:00
|
|
|
sshKeys, err := a.GetSSHRoots(ctx)
|
2021-05-18 04:07:25 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Initialize provisioners
|
|
|
|
audiences := a.config.GetAudiences()
|
|
|
|
a.provisioners = provisioner.NewCollection(audiences)
|
|
|
|
config := provisioner.Config{
|
|
|
|
Claims: claimer.Claims(),
|
|
|
|
Audiences: audiences,
|
|
|
|
DB: a.db,
|
|
|
|
SSHKeys: &provisioner.SSHKeys{
|
|
|
|
UserKeys: sshKeys.UserKeys,
|
|
|
|
HostKeys: sshKeys.HostKeys,
|
|
|
|
},
|
|
|
|
GetIdentityFunc: a.getIdentityFunc,
|
|
|
|
}
|
|
|
|
// Store all the provisioners
|
|
|
|
for _, p := range a.config.AuthorityConfig.Provisioners {
|
|
|
|
if err := p.Init(config); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := a.provisioners.Store(p); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Store all the admins
|
2021-05-18 23:50:54 +00:00
|
|
|
a.admins = admin.NewCollection(a.provisioners)
|
2021-05-18 04:07:25 +00:00
|
|
|
for _, adm := range a.config.AuthorityConfig.Admins {
|
|
|
|
if err := a.admins.Store(adm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-05 21:48:36 +00:00
|
|
|
// init performs validation and initializes the fields of an Authority struct.
|
|
|
|
func (a *Authority) init() error {
|
|
|
|
// Check if handler has already been validated/initialized.
|
|
|
|
if a.initOnce {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2020-01-10 02:42:26 +00:00
|
|
|
|
2020-09-21 22:27:20 +00:00
|
|
|
// Initialize step-ca Database if it's not already initialized with WithDB.
|
|
|
|
// If a.config.DB is nil then a simple, barebones in memory DB will be used.
|
|
|
|
if a.db == nil {
|
|
|
|
if a.db, err = db.New(a.config.DB); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-26 04:13:01 +00:00
|
|
|
if len(a.config.AuthorityConfig.Provisioners) == 0 {
|
|
|
|
// Initialize step-ca Admin Database if it's not already initialized using
|
|
|
|
// WithAdminDB.
|
|
|
|
if a.adminDB == nil {
|
|
|
|
// Check if AuthConfig already exists
|
|
|
|
a.adminDB, err = authMgmtNosql.New(a.db.(nosql.DB), mgmt.DefaultAuthorityID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-05-06 06:02:42 +00:00
|
|
|
}
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|
2021-05-12 07:03:40 +00:00
|
|
|
|
2021-05-26 04:13:01 +00:00
|
|
|
provs, err := a.adminDB.GetProvisioners(context.Background())
|
2021-05-07 00:03:12 +00:00
|
|
|
if err != nil {
|
2021-05-26 21:55:31 +00:00
|
|
|
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority")
|
2021-05-07 00:03:12 +00:00
|
|
|
}
|
2021-05-26 04:13:01 +00:00
|
|
|
if len(provs) == 0 {
|
|
|
|
// Create First Provisioner
|
|
|
|
prov, err := mgmt.CreateFirstProvisioner(context.Background(), a.adminDB, a.config.Password)
|
|
|
|
if err != nil {
|
2021-05-26 21:55:31 +00:00
|
|
|
return mgmt.WrapErrorISE(err, "error creating first provisioner")
|
|
|
|
}
|
|
|
|
certProv, err := provisionerToCertificates(prov)
|
|
|
|
if err != nil {
|
|
|
|
return mgmt.WrapErrorISE(err, "error converting provisioner to certificates type")
|
2021-05-26 04:13:01 +00:00
|
|
|
}
|
2021-05-26 21:55:31 +00:00
|
|
|
a.config.AuthorityConfig.Provisioners = []provisioner.Interface{certProv}
|
|
|
|
|
2021-05-26 04:13:01 +00:00
|
|
|
// Create First Admin
|
|
|
|
adm := &linkedca.Admin{
|
|
|
|
ProvisionerId: prov.Id,
|
|
|
|
Subject: "step",
|
|
|
|
Type: linkedca.Admin_SUPER_ADMIN,
|
|
|
|
}
|
|
|
|
if err := a.adminDB.CreateAdmin(context.Background(), adm); err != nil {
|
|
|
|
// TODO should we try to clean up?
|
|
|
|
return mgmt.WrapErrorISE(err, "error creating first admin")
|
|
|
|
}
|
|
|
|
a.config.AuthorityConfig.Admins = []*linkedca.Admin{adm}
|
|
|
|
} else {
|
|
|
|
a.config.AuthorityConfig.Provisioners, err = provisionerListToCertificates(provs)
|
|
|
|
if err != nil {
|
2021-05-26 21:55:31 +00:00
|
|
|
return mgmt.WrapErrorISE(err, "error converting provisioner list to certificates type")
|
2021-05-26 04:13:01 +00:00
|
|
|
}
|
|
|
|
a.config.AuthorityConfig.Admins, err = a.adminDB.GetAdmins(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return mgmt.WrapErrorISE(err, "error getting provisioners to initialize authority")
|
|
|
|
}
|
|
|
|
}
|
2021-05-03 19:48:20 +00:00
|
|
|
}
|
|
|
|
|
2020-01-11 02:33:48 +00:00
|
|
|
// Initialize key manager if it has not been set in the options.
|
2020-01-10 02:42:26 +00:00
|
|
|
if a.keyManager == nil {
|
2020-01-15 02:47:05 +00:00
|
|
|
var options kmsapi.Options
|
|
|
|
if a.config.KMS != nil {
|
|
|
|
options = *a.config.KMS
|
|
|
|
}
|
|
|
|
a.keyManager, err = kms.New(context.Background(), options)
|
2020-01-10 02:42:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 22:27:20 +00:00
|
|
|
// Initialize the X.509 CA Service if it has not been set in the options.
|
|
|
|
if a.x509CAService == nil {
|
|
|
|
var options casapi.Options
|
2020-10-20 01:08:51 +00:00
|
|
|
if a.config.AuthorityConfig.Options != nil {
|
|
|
|
options = *a.config.AuthorityConfig.Options
|
2020-09-21 22:27:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read intermediate and create X509 signer for default CAS.
|
|
|
|
if options.Is(casapi.SoftCAS) {
|
2020-12-24 04:41:10 +00:00
|
|
|
options.CertificateChain, err = pemutil.ReadCertificateBundle(a.config.IntermediateCert)
|
2020-09-21 22:27:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
options.Signer, err = a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
|
|
|
SigningKey: a.config.IntermediateKey,
|
|
|
|
Password: []byte(a.config.Password),
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.x509CAService, err = cas.New(context.Background(), options)
|
|
|
|
if err != nil {
|
2019-05-10 22:58:37 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-09-21 22:27:20 +00:00
|
|
|
|
|
|
|
// Get root certificate from CAS.
|
|
|
|
if srv, ok := a.x509CAService.(casapi.CertificateAuthorityGetter); ok {
|
|
|
|
resp, err := srv.GetCertificateAuthority(&casapi.GetCertificateAuthorityRequest{
|
2020-10-20 01:08:51 +00:00
|
|
|
Name: options.CertificateAuthority,
|
2020-09-21 22:27:20 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
a.rootX509Certs = append(a.rootX509Certs, resp.RootCertificate)
|
2020-09-22 20:23:48 +00:00
|
|
|
sum := sha256.Sum256(resp.RootCertificate.Raw)
|
|
|
|
log.Printf("Using root fingerprint '%s'", hex.EncodeToString(sum[:]))
|
2020-09-21 22:27:20 +00:00
|
|
|
}
|
2019-03-05 08:07:13 +00:00
|
|
|
}
|
|
|
|
|
2020-01-11 02:33:48 +00:00
|
|
|
// Read root certificates and store them in the certificates map.
|
|
|
|
if len(a.rootX509Certs) == 0 {
|
|
|
|
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
|
|
|
for i, path := range a.config.Root {
|
|
|
|
crt, err := pemutil.ReadCertificate(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
a.rootX509Certs[i] = crt
|
2019-01-07 23:30:28 +00:00
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
}
|
|
|
|
for _, crt := range a.rootX509Certs {
|
2019-01-07 23:30:28 +00:00
|
|
|
sum := sha256.Sum256(crt.Raw)
|
|
|
|
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
|
|
|
}
|
2018-10-05 21:48:36 +00:00
|
|
|
|
2020-01-11 02:33:48 +00:00
|
|
|
// Read federated certificates and store them in the certificates map.
|
|
|
|
if len(a.federatedX509Certs) == 0 {
|
|
|
|
a.federatedX509Certs = make([]*x509.Certificate, len(a.config.FederatedRoots))
|
|
|
|
for i, path := range a.config.FederatedRoots {
|
|
|
|
crt, err := pemutil.ReadCertificate(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
a.federatedX509Certs[i] = crt
|
2019-01-05 01:51:32 +00:00
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
}
|
|
|
|
for _, crt := range a.federatedX509Certs {
|
2019-01-05 01:51:32 +00:00
|
|
|
sum := sha256.Sum256(crt.Raw)
|
|
|
|
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
|
|
|
}
|
|
|
|
|
2019-08-01 22:04:56 +00:00
|
|
|
// Decrypt and load SSH keys
|
2020-06-17 00:26:18 +00:00
|
|
|
var tmplVars templates.Step
|
2019-08-01 22:04:56 +00:00
|
|
|
if a.config.SSH != nil {
|
|
|
|
if a.config.SSH.HostKey != "" {
|
2020-01-10 02:42:26 +00:00
|
|
|
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
|
|
|
SigningKey: a.config.SSH.HostKey,
|
2020-01-15 02:47:05 +00:00
|
|
|
Password: []byte(a.config.Password),
|
2020-01-10 02:42:26 +00:00
|
|
|
})
|
2019-08-01 22:04:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-03 13:26:46 +00:00
|
|
|
// If our signer is from sshagentkms, just unwrap it instead of
|
|
|
|
// wrapping it in another layer, and this prevents crypto from
|
|
|
|
// erroring out with: ssh: unsupported key type *agent.Key
|
|
|
|
switch s := signer.(type) {
|
|
|
|
case *sshagentkms.WrappedSSHSigner:
|
|
|
|
a.sshCAHostCertSignKey = s.Sshsigner
|
|
|
|
case crypto.Signer:
|
|
|
|
a.sshCAHostCertSignKey, err = ssh.NewSignerFromSigner(s)
|
|
|
|
default:
|
|
|
|
return errors.Errorf("unsupported signer type %T", signer)
|
|
|
|
}
|
2019-09-25 02:12:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating ssh signer")
|
|
|
|
}
|
2019-10-09 01:09:41 +00:00
|
|
|
// Append public key to list of host certs
|
|
|
|
a.sshCAHostCerts = append(a.sshCAHostCerts, a.sshCAHostCertSignKey.PublicKey())
|
|
|
|
a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey())
|
2019-08-01 22:04:56 +00:00
|
|
|
}
|
|
|
|
if a.config.SSH.UserKey != "" {
|
2020-01-10 02:42:26 +00:00
|
|
|
signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{
|
|
|
|
SigningKey: a.config.SSH.UserKey,
|
2020-01-15 02:47:05 +00:00
|
|
|
Password: []byte(a.config.Password),
|
2020-01-10 02:42:26 +00:00
|
|
|
})
|
2019-08-01 22:04:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-03 13:26:46 +00:00
|
|
|
// If our signer is from sshagentkms, just unwrap it instead of
|
|
|
|
// wrapping it in another layer, and this prevents crypto from
|
|
|
|
// erroring out with: ssh: unsupported key type *agent.Key
|
|
|
|
switch s := signer.(type) {
|
|
|
|
case *sshagentkms.WrappedSSHSigner:
|
|
|
|
a.sshCAUserCertSignKey = s.Sshsigner
|
|
|
|
case crypto.Signer:
|
|
|
|
a.sshCAUserCertSignKey, err = ssh.NewSignerFromSigner(s)
|
|
|
|
default:
|
|
|
|
return errors.Errorf("unsupported signer type %T", signer)
|
|
|
|
}
|
2019-09-25 02:12:13 +00:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating ssh signer")
|
|
|
|
}
|
2019-10-09 01:09:41 +00:00
|
|
|
// Append public key to list of user certs
|
2019-10-25 01:36:02 +00:00
|
|
|
a.sshCAUserCerts = append(a.sshCAUserCerts, a.sshCAUserCertSignKey.PublicKey())
|
2019-10-09 01:09:41 +00:00
|
|
|
a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, a.sshCAUserCertSignKey.PublicKey())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append other public keys
|
|
|
|
for _, key := range a.config.SSH.Keys {
|
|
|
|
switch key.Type {
|
|
|
|
case provisioner.SSHHostCert:
|
|
|
|
if key.Federated {
|
|
|
|
a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, key.PublicKey())
|
|
|
|
} else {
|
|
|
|
a.sshCAHostCerts = append(a.sshCAHostCerts, key.PublicKey())
|
|
|
|
}
|
|
|
|
case provisioner.SSHUserCert:
|
|
|
|
if key.Federated {
|
|
|
|
a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, key.PublicKey())
|
|
|
|
} else {
|
|
|
|
a.sshCAUserCerts = append(a.sshCAUserCerts, key.PublicKey())
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return errors.Errorf("unsupported type %s", key.Type)
|
|
|
|
}
|
2019-08-01 22:04:56 +00:00
|
|
|
}
|
2020-06-23 18:14:26 +00:00
|
|
|
|
|
|
|
// Configure template variables.
|
|
|
|
tmplVars.SSH.HostKey = a.sshCAHostCertSignKey.PublicKey()
|
|
|
|
tmplVars.SSH.UserKey = a.sshCAUserCertSignKey.PublicKey()
|
|
|
|
// On the templates we skip the first one because there's a distinction
|
|
|
|
// between the main key and federated keys.
|
|
|
|
tmplVars.SSH.HostFederatedKeys = append(tmplVars.SSH.HostFederatedKeys, a.sshCAHostFederatedCerts[1:]...)
|
|
|
|
tmplVars.SSH.UserFederatedKeys = append(tmplVars.SSH.UserFederatedKeys, a.sshCAUserFederatedCerts[1:]...)
|
2019-08-01 22:04:56 +00:00
|
|
|
}
|
2019-07-24 01:46:43 +00:00
|
|
|
|
2019-10-28 18:50:43 +00:00
|
|
|
// Merge global and configuration claims
|
2021-05-03 19:48:20 +00:00
|
|
|
claimer, err := provisioner.NewClaimer(a.config.AuthorityConfig.Claims, config.GlobalProvisionerClaims)
|
2019-10-28 18:50:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// TODO: should we also be combining the ssh federated roots here?
|
|
|
|
// If we rotate ssh roots keys, sshpop provisioner will lose ability to
|
|
|
|
// validate old SSH certificates, unless they are added as federated certs.
|
2020-03-11 02:01:45 +00:00
|
|
|
sshKeys, err := a.GetSSHRoots(context.Background())
|
2019-10-28 18:50:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Initialize provisioners
|
2021-05-03 19:48:20 +00:00
|
|
|
audiences := a.config.GetAudiences()
|
2020-05-06 20:00:42 +00:00
|
|
|
a.provisioners = provisioner.NewCollection(audiences)
|
2019-10-28 18:50:43 +00:00
|
|
|
config := provisioner.Config{
|
2021-02-12 11:03:08 +00:00
|
|
|
// TODO: this probably shouldn't happen like this; via SignAuth instead?
|
|
|
|
IntermediateCert: a.config.IntermediateCert,
|
|
|
|
SigningKey: a.config.IntermediateKey,
|
|
|
|
CACertificates: a.rootX509Certs,
|
|
|
|
Claims: claimer.Claims(),
|
|
|
|
Audiences: audiences,
|
|
|
|
DB: a.db,
|
2019-10-28 18:50:43 +00:00
|
|
|
SSHKeys: &provisioner.SSHKeys{
|
|
|
|
UserKeys: sshKeys.UserKeys,
|
|
|
|
HostKeys: sshKeys.HostKeys,
|
|
|
|
},
|
2019-11-16 00:57:51 +00:00
|
|
|
GetIdentityFunc: a.getIdentityFunc,
|
2019-10-28 18:50:43 +00:00
|
|
|
}
|
2019-03-06 23:00:23 +00:00
|
|
|
// Store all the provisioners
|
2018-10-05 21:48:36 +00:00
|
|
|
for _, p := range a.config.AuthorityConfig.Provisioners {
|
2019-10-28 18:50:43 +00:00
|
|
|
if err := p.Init(config); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-03-06 23:00:23 +00:00
|
|
|
if err := a.provisioners.Store(p); err != nil {
|
|
|
|
return err
|
2018-10-05 21:48:36 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-18 04:07:25 +00:00
|
|
|
// Store all the admins
|
2021-05-18 23:50:54 +00:00
|
|
|
a.admins = admin.NewCollection(a.provisioners)
|
2021-05-18 04:07:25 +00:00
|
|
|
for _, adm := range a.config.AuthorityConfig.Admins {
|
|
|
|
if err := a.admins.Store(adm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 21:48:36 +00:00
|
|
|
|
2020-06-17 00:26:18 +00:00
|
|
|
// Configure templates, currently only ssh templates are supported.
|
|
|
|
if a.sshCAHostCertSignKey != nil || a.sshCAUserCertSignKey != nil {
|
2020-06-17 00:57:35 +00:00
|
|
|
a.templates = a.config.Templates
|
|
|
|
if a.templates == nil {
|
|
|
|
a.templates = templates.DefaultTemplates()
|
2019-10-04 02:03:38 +00:00
|
|
|
}
|
2020-06-17 00:57:35 +00:00
|
|
|
if a.templates.Data == nil {
|
|
|
|
a.templates.Data = make(map[string]interface{})
|
2019-10-04 02:03:38 +00:00
|
|
|
}
|
2020-06-17 00:57:35 +00:00
|
|
|
a.templates.Data["Step"] = tmplVars
|
2019-10-04 02:03:38 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 01:14:20 +00:00
|
|
|
// JWT numeric dates are seconds.
|
|
|
|
a.startTime = time.Now().Truncate(time.Second)
|
2018-10-05 21:48:36 +00:00
|
|
|
// Set flag indicating that initialization has been completed, and should
|
|
|
|
// not be repeated.
|
|
|
|
a.initOnce = true
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2019-03-05 08:07:13 +00:00
|
|
|
|
2019-05-11 00:54:18 +00:00
|
|
|
// GetDatabase returns the authority database. If the configuration does not
|
|
|
|
// define a database, GetDatabase will return a db.SimpleDB instance.
|
2019-05-10 22:58:37 +00:00
|
|
|
func (a *Authority) GetDatabase() db.AuthDB {
|
|
|
|
return a.db
|
|
|
|
}
|
|
|
|
|
2021-05-20 20:01:58 +00:00
|
|
|
// GetAdminDatabase returns the mgmt database, if one exists.
|
|
|
|
func (a *Authority) GetAdminDatabase() mgmt.DB {
|
|
|
|
return a.adminDB
|
2021-05-07 00:03:12 +00:00
|
|
|
}
|
|
|
|
|
2021-05-18 04:07:25 +00:00
|
|
|
// GetAdminCollection returns the admin collection.
|
|
|
|
func (a *Authority) GetAdminCollection() *admin.Collection {
|
|
|
|
return a.admins
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetProvisionerCollection returns the admin collection.
|
|
|
|
func (a *Authority) GetProvisionerCollection() *provisioner.Collection {
|
|
|
|
return a.provisioners
|
|
|
|
}
|
|
|
|
|
2019-03-05 08:07:13 +00:00
|
|
|
// Shutdown safely shuts down any clients, databases, etc. held by the Authority.
|
|
|
|
func (a *Authority) Shutdown() error {
|
2020-05-08 01:59:30 +00:00
|
|
|
if err := a.keyManager.Close(); err != nil {
|
|
|
|
log.Printf("error closing the key manager: %v", err)
|
|
|
|
}
|
2019-03-05 08:07:13 +00:00
|
|
|
return a.db.Shutdown()
|
|
|
|
}
|
2021-02-02 01:14:44 +00:00
|
|
|
|
|
|
|
// CloseForReload closes internal services, to allow a safe reload.
|
|
|
|
func (a *Authority) CloseForReload() {
|
|
|
|
if err := a.keyManager.Close(); err != nil {
|
|
|
|
log.Printf("error closing the key manager: %v", err)
|
|
|
|
}
|
|
|
|
}
|