From c62526b39f0ce9bfe3fc389be1c5e52a03073b83 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 9 Jan 2020 18:42:26 -0800 Subject: [PATCH] Add wip support for kms. --- authority/authority.go | 59 ++++++++++++++++++++++++++++++++++-------- authority/config.go | 7 +++++ authority/options.go | 33 +++++++++++++++++++++++ authority/tls.go | 29 ++++++++++----------- 4 files changed, 102 insertions(+), 26 deletions(-) diff --git a/authority/authority.go b/authority/authority.go index 25b40350..85bb436d 100644 --- a/authority/authority.go +++ b/authority/authority.go @@ -12,6 +12,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/db" + "github.com/smallstep/certificates/kms" + kmsapi "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/sshutil" "github.com/smallstep/certificates/templates" "github.com/smallstep/cli/crypto/pemutil" @@ -28,6 +30,9 @@ type Authority struct { config *Config rootX509Certs []*x509.Certificate intermediateIdentity *x509util.Identity + keyManager kms.KeyManager + x509Signer crypto.Signer + x509Issuer *x509.Certificate sshCAUserCertSignKey ssh.Signer sshCAHostCertSignKey ssh.Signer sshCAUserCerts []ssh.PublicKey @@ -76,6 +81,14 @@ func (a *Authority) init() error { } var err error + + if a.keyManager == nil { + a.keyManager, err = kms.New(context.Background(), *a.config.KMS) + if err != nil { + return err + } + } + // 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 { @@ -107,27 +120,47 @@ func (a *Authority) init() error { a.certificates.Store(hex.EncodeToString(sum[:]), crt) } - // Decrypt and load intermediate public / private key pair. - if len(a.config.Password) > 0 { - a.intermediateIdentity, err = x509util.LoadIdentityFromDisk( - a.config.IntermediateCert, - a.config.IntermediateKey, - pemutil.WithPassword([]byte(a.config.Password)), - ) + if a.x509Signer == nil { + crt, err := pemutil.ReadCertificate(a.config.IntermediateCert) if err != nil { return err } - } else { - a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey) + signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.IntermediateKey, + Password: a.config.Password, + }) if err != nil { return err } + a.x509Signer = signer + a.x509Issuer = crt + + // Decrypt and load intermediate public / private key pair. + // if len(a.config.Password) > 0 { + // a.intermediateIdentity, err = x509util.LoadIdentityFromDisk( + // a.config.IntermediateCert, + // a.config.IntermediateKey, + // pemutil.WithPassword([]byte(a.config.Password)), + // ) + // if err != nil { + // return err + // } + // } else { + // a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey) + // if err != nil { + // return err + // } + // } } // Decrypt and load SSH keys if a.config.SSH != nil { if a.config.SSH.HostKey != "" { - signer, err := parseCryptoSigner(a.config.SSH.HostKey, a.config.Password) + signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.SSH.HostKey, + Password: a.config.Password, + }) + // signer, err := parseCryptoSigner(a.config.SSH.HostKey, a.config.Password) if err != nil { return err } @@ -140,7 +173,11 @@ func (a *Authority) init() error { a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) } if a.config.SSH.UserKey != "" { - signer, err := parseCryptoSigner(a.config.SSH.UserKey, a.config.Password) + signer, err := a.keyManager.CreateSigner(&kmsapi.CreateSignerRequest{ + SigningKey: a.config.SSH.UserKey, + Password: a.config.Password, + }) + // signer, err := parseCryptoSigner(a.config.SSH.UserKey, a.config.Password) if err != nil { return err } diff --git a/authority/config.go b/authority/config.go index 75f55a12..ceb2ea89 100644 --- a/authority/config.go +++ b/authority/config.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/db" + kms "github.com/smallstep/certificates/kms/apiv1" "github.com/smallstep/certificates/templates" "github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/x509util" @@ -54,6 +55,7 @@ type Config struct { IntermediateKey string `json:"key"` Address string `json:"address"` DNSNames []string `json:"dnsNames"` + KMS *kms.Options `json:"kms,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"` Logger json.RawMessage `json:"logger,omitempty"` DB *db.Config `json:"db,omitempty"` @@ -179,6 +181,11 @@ func (c *Config) Validate() error { c.TLS.Renegotiation = c.TLS.Renegotiation || DefaultTLSOptions.Renegotiation } + // Validate KMS options, nil is ok. + if err := c.KMS.Validate(); err != nil { + return err + } + // Validate ssh: nil is ok if err := c.SSH.Validate(); err != nil { return err diff --git a/authority/options.go b/authority/options.go index 10f0ec1a..9891e258 100644 --- a/authority/options.go +++ b/authority/options.go @@ -2,11 +2,14 @@ package authority import ( "context" + "crypto" "crypto/x509" "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/db" + "github.com/smallstep/certificates/kms" "github.com/smallstep/certificates/sshutil" + "golang.org/x/crypto/ssh" ) // Option sets options to the Authority. @@ -52,3 +55,33 @@ func WithSSHCheckHost(fn func(ctx context.Context, principal string, tok string, a.sshCheckHostFunc = fn } } + +// WithKeyManager defines the key manager used to get and create keys, and sign +// certificates. +func WithKeyManager(k kms.KeyManager) Option { + return func(a *Authority) { + a.keyManager = k + } +} + +// WithX509Signer defines the signer used to sign X509 certificates. +func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option { + return func(a *Authority) { + a.x509Issuer = crt + a.x509Signer = s + } +} + +// WithSSHUserSigner defines the signer used to sign SSH user certificates. +func WithSSHUserSigner(s ssh.Signer) Option { + return func(a *Authority) { + a.sshCAUserCertSignKey = s + } +} + +// WithSSHHostSigner defines the signer used to sign SSH host certificates. +func WithSSHHostSigner(s ssh.Signer) Option { + return func(a *Authority) { + a.sshCAHostCertSignKey = s + } +} diff --git a/authority/tls.go b/authority/tls.go index eb7cb86a..0eb8f280 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -63,7 +63,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti errContext = apiCtx{"csr": csr, "signOptions": signOpts} mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)} certValidators = []provisioner.CertificateValidator{} - issIdentity = a.intermediateIdentity + // issIdentity = a.intermediateIdentity ) // Set backdate with the configured value @@ -90,7 +90,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti http.StatusBadRequest, errContext} } - leaf, err := x509util.NewLeafProfileWithCSR(csr, issIdentity.Crt, issIdentity.Key, mods...) + leaf, err := x509util.NewLeafProfileWithCSR(csr, a.x509Issuer, a.x509Signer, mods...) if err != nil { return nil, &apiError{errors.Wrapf(err, "sign"), http.StatusInternalServerError, errContext} } @@ -113,11 +113,11 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti http.StatusInternalServerError, errContext} } - caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw) - if err != nil { - return nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"), - http.StatusInternalServerError, errContext} - } + // caCert, err := x509.ParseCertificate(a.x509SignerCert.Raw) + // if err != nil { + // return nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"), + // http.StatusInternalServerError, errContext} + // } if err = a.db.StoreCertificate(serverCert); err != nil { if err != db.ErrNotImplemented { @@ -126,7 +126,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti } } - return []*x509.Certificate{serverCert, caCert}, nil + return []*x509.Certificate{serverCert, a.x509Issuer}, nil } // Renew creates a new Certificate identical to the old certificate, except @@ -138,7 +138,7 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error } // Issuer - issIdentity := a.intermediateIdentity + // issIdentity := a.intermediateIdentity // Durations backdate := a.config.AuthorityConfig.Backdate.Duration @@ -147,7 +147,7 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error newCert := &x509.Certificate{ PublicKey: oldCert.PublicKey, - Issuer: issIdentity.Crt.Subject, + Issuer: a.x509Issuer.Subject, Subject: oldCert.Subject, NotBefore: now.Add(-1 * backdate), NotAfter: now.Add(duration - backdate), @@ -187,8 +187,7 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error } } - leaf, err := x509util.NewLeafProfileWithTemplate(newCert, - issIdentity.Crt, issIdentity.Key) + leaf, err := x509util.NewLeafProfileWithTemplate(newCert, a.x509Issuer, a.x509Signer) if err != nil { return nil, &apiError{err, http.StatusInternalServerError, apiCtx{}} } @@ -203,7 +202,7 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error return nil, &apiError{errors.Wrap(err, "error parsing new server certificate"), http.StatusInternalServerError, apiCtx{}} } - caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw) + caCert, err := x509.ParseCertificate(a.x509Issuer.Raw) if err != nil { return nil, &apiError{errors.Wrap(err, "error parsing intermediate certificate"), http.StatusInternalServerError, apiCtx{}} @@ -327,7 +326,7 @@ func (a *Authority) Revoke(ctx context.Context, opts *RevokeOptions) error { // GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server. func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { profile, err := x509util.NewLeafProfile("Step Online CA", - a.intermediateIdentity.Crt, a.intermediateIdentity.Key, + a.x509Issuer, a.x509Signer, x509util.WithHosts(strings.Join(a.config.DNSNames, ","))) if err != nil { return nil, err @@ -350,7 +349,7 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { // Load the x509 key pair (combining server and intermediate blocks) // to a tls.Certificate. - intermediatePEM, err := pemutil.Serialize(a.intermediateIdentity.Crt) + intermediatePEM, err := pemutil.Serialize(a.x509Issuer) if err != nil { return nil, err }