Add wip support for kms.

This commit is contained in:
Mariano Cano 2020-01-09 18:42:26 -08:00
parent d13754166a
commit c62526b39f
4 changed files with 102 additions and 26 deletions

View file

@ -12,6 +12,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db" "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/sshutil"
"github.com/smallstep/certificates/templates" "github.com/smallstep/certificates/templates"
"github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/crypto/pemutil"
@ -28,6 +30,9 @@ type Authority struct {
config *Config config *Config
rootX509Certs []*x509.Certificate rootX509Certs []*x509.Certificate
intermediateIdentity *x509util.Identity intermediateIdentity *x509util.Identity
keyManager kms.KeyManager
x509Signer crypto.Signer
x509Issuer *x509.Certificate
sshCAUserCertSignKey ssh.Signer sshCAUserCertSignKey ssh.Signer
sshCAHostCertSignKey ssh.Signer sshCAHostCertSignKey ssh.Signer
sshCAUserCerts []ssh.PublicKey sshCAUserCerts []ssh.PublicKey
@ -76,6 +81,14 @@ func (a *Authority) init() error {
} }
var err 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. // 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.config.DB is nil then a simple, barebones in memory DB will be used.
if a.db == nil { if a.db == nil {
@ -107,27 +120,47 @@ func (a *Authority) init() error {
a.certificates.Store(hex.EncodeToString(sum[:]), crt) a.certificates.Store(hex.EncodeToString(sum[:]), crt)
} }
if a.x509Signer == nil {
crt, err := pemutil.ReadCertificate(a.config.IntermediateCert)
if err != nil {
return err
}
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. // Decrypt and load intermediate public / private key pair.
if len(a.config.Password) > 0 { // if len(a.config.Password) > 0 {
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk( // a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(
a.config.IntermediateCert, // a.config.IntermediateCert,
a.config.IntermediateKey, // a.config.IntermediateKey,
pemutil.WithPassword([]byte(a.config.Password)), // pemutil.WithPassword([]byte(a.config.Password)),
) // )
if err != nil { // if err != nil {
return err // return err
} // }
} else { // } else {
a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey) // a.intermediateIdentity, err = x509util.LoadIdentityFromDisk(a.config.IntermediateCert, a.config.IntermediateKey)
if err != nil { // if err != nil {
return err // return err
} // }
// }
} }
// Decrypt and load SSH keys // Decrypt and load SSH keys
if a.config.SSH != nil { if a.config.SSH != nil {
if a.config.SSH.HostKey != "" { 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 { if err != nil {
return err return err
} }
@ -140,7 +173,11 @@ func (a *Authority) init() error {
a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey()) a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, a.sshCAHostCertSignKey.PublicKey())
} }
if a.config.SSH.UserKey != "" { 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 { if err != nil {
return err return err
} }

View file

@ -10,6 +10,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
kms "github.com/smallstep/certificates/kms/apiv1"
"github.com/smallstep/certificates/templates" "github.com/smallstep/certificates/templates"
"github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/tlsutil"
"github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/cli/crypto/x509util"
@ -54,6 +55,7 @@ type Config struct {
IntermediateKey string `json:"key"` IntermediateKey string `json:"key"`
Address string `json:"address"` Address string `json:"address"`
DNSNames []string `json:"dnsNames"` DNSNames []string `json:"dnsNames"`
KMS *kms.Options `json:"kms,omitempty"`
SSH *SSHConfig `json:"ssh,omitempty"` SSH *SSHConfig `json:"ssh,omitempty"`
Logger json.RawMessage `json:"logger,omitempty"` Logger json.RawMessage `json:"logger,omitempty"`
DB *db.Config `json:"db,omitempty"` DB *db.Config `json:"db,omitempty"`
@ -179,6 +181,11 @@ func (c *Config) Validate() error {
c.TLS.Renegotiation = c.TLS.Renegotiation || DefaultTLSOptions.Renegotiation 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 // Validate ssh: nil is ok
if err := c.SSH.Validate(); err != nil { if err := c.SSH.Validate(); err != nil {
return err return err

View file

@ -2,11 +2,14 @@ package authority
import ( import (
"context" "context"
"crypto"
"crypto/x509" "crypto/x509"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/kms"
"github.com/smallstep/certificates/sshutil" "github.com/smallstep/certificates/sshutil"
"golang.org/x/crypto/ssh"
) )
// Option sets options to the Authority. // Option sets options to the Authority.
@ -52,3 +55,33 @@ func WithSSHCheckHost(fn func(ctx context.Context, principal string, tok string,
a.sshCheckHostFunc = fn 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
}
}

View file

@ -63,7 +63,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
errContext = apiCtx{"csr": csr, "signOptions": signOpts} errContext = apiCtx{"csr": csr, "signOptions": signOpts}
mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)} mods = []x509util.WithOption{withDefaultASN1DN(a.config.AuthorityConfig.Template)}
certValidators = []provisioner.CertificateValidator{} certValidators = []provisioner.CertificateValidator{}
issIdentity = a.intermediateIdentity // issIdentity = a.intermediateIdentity
) )
// Set backdate with the configured value // Set backdate with the configured value
@ -90,7 +90,7 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Opti
http.StatusBadRequest, errContext} 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 { if err != nil {
return nil, &apiError{errors.Wrapf(err, "sign"), http.StatusInternalServerError, errContext} 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} http.StatusInternalServerError, errContext}
} }
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw) // caCert, err := x509.ParseCertificate(a.x509SignerCert.Raw)
if err != nil { // if err != nil {
return nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"), // return nil, &apiError{errors.Wrap(err, "sign: error parsing intermediate certificate"),
http.StatusInternalServerError, errContext} // http.StatusInternalServerError, errContext}
} // }
if err = a.db.StoreCertificate(serverCert); err != nil { if err = a.db.StoreCertificate(serverCert); err != nil {
if err != db.ErrNotImplemented { 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 // 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 // Issuer
issIdentity := a.intermediateIdentity // issIdentity := a.intermediateIdentity
// Durations // Durations
backdate := a.config.AuthorityConfig.Backdate.Duration backdate := a.config.AuthorityConfig.Backdate.Duration
@ -147,7 +147,7 @@ func (a *Authority) Renew(oldCert *x509.Certificate) ([]*x509.Certificate, error
newCert := &x509.Certificate{ newCert := &x509.Certificate{
PublicKey: oldCert.PublicKey, PublicKey: oldCert.PublicKey,
Issuer: issIdentity.Crt.Subject, Issuer: a.x509Issuer.Subject,
Subject: oldCert.Subject, Subject: oldCert.Subject,
NotBefore: now.Add(-1 * backdate), NotBefore: now.Add(-1 * backdate),
NotAfter: now.Add(duration - 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, leaf, err := x509util.NewLeafProfileWithTemplate(newCert, a.x509Issuer, a.x509Signer)
issIdentity.Crt, issIdentity.Key)
if err != nil { if err != nil {
return nil, &apiError{err, http.StatusInternalServerError, apiCtx{}} 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"), return nil, &apiError{errors.Wrap(err, "error parsing new server certificate"),
http.StatusInternalServerError, apiCtx{}} http.StatusInternalServerError, apiCtx{}}
} }
caCert, err := x509.ParseCertificate(issIdentity.Crt.Raw) caCert, err := x509.ParseCertificate(a.x509Issuer.Raw)
if err != nil { if err != nil {
return nil, &apiError{errors.Wrap(err, "error parsing intermediate certificate"), return nil, &apiError{errors.Wrap(err, "error parsing intermediate certificate"),
http.StatusInternalServerError, apiCtx{}} 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. // GetTLSCertificate creates a new leaf certificate to be used by the CA HTTPS server.
func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) { func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
profile, err := x509util.NewLeafProfile("Step Online CA", profile, err := x509util.NewLeafProfile("Step Online CA",
a.intermediateIdentity.Crt, a.intermediateIdentity.Key, a.x509Issuer, a.x509Signer,
x509util.WithHosts(strings.Join(a.config.DNSNames, ","))) x509util.WithHosts(strings.Join(a.config.DNSNames, ",")))
if err != nil { if err != nil {
return nil, err return nil, err
@ -350,7 +349,7 @@ func (a *Authority) GetTLSCertificate() (*tls.Certificate, error) {
// Load the x509 key pair (combining server and intermediate blocks) // Load the x509 key pair (combining server and intermediate blocks)
// to a tls.Certificate. // to a tls.Certificate.
intermediatePEM, err := pemutil.Serialize(a.intermediateIdentity.Crt) intermediatePEM, err := pemutil.Serialize(a.x509Issuer)
if err != nil { if err != nil {
return nil, err return nil, err
} }