Add options to read the roots and federated roots from a bundle.

This commit is contained in:
Mariano Cano 2020-01-10 18:33:48 -08:00
parent 44eccc6bd8
commit e98d7832b9
3 changed files with 124 additions and 60 deletions

View file

@ -17,7 +17,6 @@ import (
"github.com/smallstep/certificates/sshutil"
"github.com/smallstep/certificates/templates"
"github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/crypto/x509util"
"golang.org/x/crypto/ssh"
)
@ -28,23 +27,29 @@ const (
// Authority implements the Certificate Authority internal interface.
type Authority struct {
config *Config
rootX509Certs []*x509.Certificate
intermediateIdentity *x509util.Identity
keyManager kms.KeyManager
provisioners *provisioner.Collection
db db.AuthDB
// X509 CA
rootX509Certs []*x509.Certificate
federatedX509Certs []*x509.Certificate
x509Signer crypto.Signer
x509Issuer *x509.Certificate
certificates *sync.Map
// SSH CA
sshCAUserCertSignKey ssh.Signer
sshCAHostCertSignKey ssh.Signer
sshCAUserCerts []ssh.PublicKey
sshCAHostCerts []ssh.PublicKey
sshCAUserFederatedCerts []ssh.PublicKey
sshCAHostFederatedCerts []ssh.PublicKey
certificates *sync.Map
startTime time.Time
provisioners *provisioner.Collection
db db.AuthDB
// Do not re-initialize
initOnce bool
startTime time.Time
// Custom functions
sshBastionFunc func(user, hostname string) (*Bastion, error)
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)
@ -64,12 +69,19 @@ func New(config *Config, opts ...Option) (*Authority, error) {
certificates: new(sync.Map),
provisioners: provisioner.NewCollection(config.getAudiences()),
}
for _, opt := range opts {
opt(a)
// Apply options.
for _, fn := range opts {
if err := fn(a); err != nil {
return nil, err
}
}
// Initialize authority from options or configuration.
if err := a.init(); err != nil {
return nil, err
}
return a, nil
}
@ -82,6 +94,7 @@ func (a *Authority) init() error {
var err error
// Initialize key manager if it has not been set in the options.
if a.keyManager == nil {
a.keyManager, err = kms.New(context.Background(), *a.config.KMS)
if err != nil {
@ -97,29 +110,39 @@ func (a *Authority) init() error {
}
}
// Load the root certificates and add them to the certificate store
// 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
}
// Add root certificate to the certificate map
sum := sha256.Sum256(crt.Raw)
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
a.rootX509Certs[i] = crt
}
}
for _, crt := range a.rootX509Certs {
sum := sha256.Sum256(crt.Raw)
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
}
// Add federated roots
for _, path := range a.config.FederatedRoots {
// 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
}
}
for _, crt := range a.federatedX509Certs {
sum := sha256.Sum256(crt.Raw)
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
}
// Read intermediate and create X509 signer.
if a.x509Signer == nil {
crt, err := pemutil.ReadCertificate(a.config.IntermediateCert)
if err != nil {
@ -134,23 +157,6 @@ func (a *Authority) init() error {
}
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
@ -160,7 +166,6 @@ func (a *Authority) init() error {
SigningKey: a.config.SSH.HostKey,
Password: a.config.Password,
})
// signer, err := parseCryptoSigner(a.config.SSH.HostKey, a.config.Password)
if err != nil {
return err
}
@ -177,7 +182,6 @@ func (a *Authority) init() error {
SigningKey: a.config.SSH.UserKey,
Password: a.config.Password,
})
// signer, err := parseCryptoSigner(a.config.SSH.UserKey, a.config.Password)
if err != nil {
return err
}

View file

@ -4,6 +4,7 @@ import (
"context"
"crypto"
"crypto/x509"
"encoding/pem"
"github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db"
@ -13,37 +14,41 @@ import (
)
// Option sets options to the Authority.
type Option func(*Authority)
type Option func(*Authority) error
// WithDatabase sets an already initialized authority database to a new
// authority. This option is intended to be use on graceful reloads.
func WithDatabase(db db.AuthDB) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.db = db
return nil
}
}
// WithGetIdentityFunc sets a custom function to retrieve the identity from
// an external resource.
func WithGetIdentityFunc(fn func(p provisioner.Interface, email string) (*provisioner.Identity, error)) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.getIdentityFunc = fn
return nil
}
}
// WithSSHBastionFunc sets a custom function to get the bastion for a
// given user-host pair.
func WithSSHBastionFunc(fn func(user, host string) (*Bastion, error)) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.sshBastionFunc = fn
return nil
}
}
// WithSSHGetHosts sets a custom function to get the bastion for a
// given user-host pair.
func WithSSHGetHosts(fn func(cert *x509.Certificate) ([]sshutil.Host, error)) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.sshGetHostsFunc = fn
return nil
}
}
@ -51,37 +56,91 @@ func WithSSHGetHosts(fn func(cert *x509.Certificate) ([]sshutil.Host, error)) Op
// step ssh enabled. The token is used to validate the request, while the roots
// are used to validate the token.
func WithSSHCheckHost(fn func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.sshCheckHostFunc = fn
return nil
}
}
// WithKeyManager defines the key manager used to get and create keys, and sign
// certificates.
func WithKeyManager(k kms.KeyManager) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.keyManager = k
return nil
}
}
// WithX509Signer defines the signer used to sign X509 certificates.
func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.x509Issuer = crt
a.x509Signer = s
return nil
}
}
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
func WithSSHUserSigner(s ssh.Signer) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.sshCAUserCertSignKey = s
return nil
}
}
// WithSSHHostSigner defines the signer used to sign SSH host certificates.
func WithSSHHostSigner(s ssh.Signer) Option {
return func(a *Authority) {
return func(a *Authority) error {
a.sshCAHostCertSignKey = s
return nil
}
}
// WithX509RootBundle is an option that allows to define the list of root
// certificates.
func WithX509RootBundle(pemCerts []byte) Option {
return func(a *Authority) error {
certs, err := readCertificateBundle(pemCerts)
if err != nil {
return err
}
x509.NewCertPool()
a.rootX509Certs = certs
return nil
}
}
// WithX509FederatedBundle is an option that allows to define the list of
// federated certificates.
func WithX509FederatedBundle(pemCerts []byte) Option {
return func(a *Authority) error {
certs, err := readCertificateBundle(pemCerts)
if err != nil {
return err
}
a.federatedX509Certs = certs
return nil
}
}
func readCertificateBundle(pemCerts []byte) ([]*x509.Certificate, error) {
var block *pem.Block
var certs []*x509.Certificate
for len(pemCerts) > 0 {
block, pemCerts = pem.Decode(pemCerts)
if block == nil {
break
}
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
continue
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
certs = append(certs, cert)
}
return certs, nil
}

1
go.sum
View file

@ -576,6 +576,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=