forked from TrueCloudLab/certificates
Add wip support for kms.
This commit is contained in:
parent
d13754166a
commit
c62526b39f
4 changed files with 102 additions and 26 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue