forked from TrueCloudLab/certificates
Add options to read the roots and federated roots from a bundle.
This commit is contained in:
parent
44eccc6bd8
commit
e98d7832b9
3 changed files with 124 additions and 60 deletions
|
@ -17,7 +17,6 @@ import (
|
||||||
"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"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,24 +26,30 @@ const (
|
||||||
|
|
||||||
// Authority implements the Certificate Authority internal interface.
|
// Authority implements the Certificate Authority internal interface.
|
||||||
type Authority struct {
|
type Authority struct {
|
||||||
config *Config
|
config *Config
|
||||||
rootX509Certs []*x509.Certificate
|
keyManager kms.KeyManager
|
||||||
intermediateIdentity *x509util.Identity
|
provisioners *provisioner.Collection
|
||||||
keyManager kms.KeyManager
|
db db.AuthDB
|
||||||
x509Signer crypto.Signer
|
|
||||||
x509Issuer *x509.Certificate
|
// X509 CA
|
||||||
|
rootX509Certs []*x509.Certificate
|
||||||
|
federatedX509Certs []*x509.Certificate
|
||||||
|
x509Signer crypto.Signer
|
||||||
|
x509Issuer *x509.Certificate
|
||||||
|
certificates *sync.Map
|
||||||
|
|
||||||
|
// SSH CA
|
||||||
sshCAUserCertSignKey ssh.Signer
|
sshCAUserCertSignKey ssh.Signer
|
||||||
sshCAHostCertSignKey ssh.Signer
|
sshCAHostCertSignKey ssh.Signer
|
||||||
sshCAUserCerts []ssh.PublicKey
|
sshCAUserCerts []ssh.PublicKey
|
||||||
sshCAHostCerts []ssh.PublicKey
|
sshCAHostCerts []ssh.PublicKey
|
||||||
sshCAUserFederatedCerts []ssh.PublicKey
|
sshCAUserFederatedCerts []ssh.PublicKey
|
||||||
sshCAHostFederatedCerts []ssh.PublicKey
|
sshCAHostFederatedCerts []ssh.PublicKey
|
||||||
certificates *sync.Map
|
|
||||||
startTime time.Time
|
|
||||||
provisioners *provisioner.Collection
|
|
||||||
db db.AuthDB
|
|
||||||
// Do not re-initialize
|
// Do not re-initialize
|
||||||
initOnce bool
|
initOnce bool
|
||||||
|
startTime time.Time
|
||||||
|
|
||||||
// Custom functions
|
// Custom functions
|
||||||
sshBastionFunc func(user, hostname string) (*Bastion, error)
|
sshBastionFunc func(user, hostname string) (*Bastion, error)
|
||||||
sshCheckHostFunc func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, 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),
|
certificates: new(sync.Map),
|
||||||
provisioners: provisioner.NewCollection(config.getAudiences()),
|
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 {
|
if err := a.init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +94,7 @@ func (a *Authority) init() error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Initialize key manager if it has not been set in the options.
|
||||||
if a.keyManager == nil {
|
if a.keyManager == nil {
|
||||||
a.keyManager, err = kms.New(context.Background(), *a.config.KMS)
|
a.keyManager, err = kms.New(context.Background(), *a.config.KMS)
|
||||||
if err != nil {
|
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.
|
||||||
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
if len(a.rootX509Certs) == 0 {
|
||||||
for i, path := range a.config.Root {
|
a.rootX509Certs = make([]*x509.Certificate, len(a.config.Root))
|
||||||
crt, err := pemutil.ReadCertificate(path)
|
for i, path := range a.config.Root {
|
||||||
if err != nil {
|
crt, err := pemutil.ReadCertificate(path)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.rootX509Certs[i] = crt
|
||||||
}
|
}
|
||||||
// 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 {
|
||||||
// Add federated roots
|
|
||||||
for _, path := range a.config.FederatedRoots {
|
|
||||||
crt, err := pemutil.ReadCertificate(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sum := sha256.Sum256(crt.Raw)
|
sum := sha256.Sum256(crt.Raw)
|
||||||
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
a.certificates.Store(hex.EncodeToString(sum[:]), crt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
if a.x509Signer == nil {
|
||||||
crt, err := pemutil.ReadCertificate(a.config.IntermediateCert)
|
crt, err := pemutil.ReadCertificate(a.config.IntermediateCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -134,23 +157,6 @@ func (a *Authority) init() error {
|
||||||
}
|
}
|
||||||
a.x509Signer = signer
|
a.x509Signer = signer
|
||||||
a.x509Issuer = crt
|
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
|
// Decrypt and load SSH keys
|
||||||
|
@ -160,7 +166,6 @@ func (a *Authority) init() error {
|
||||||
SigningKey: a.config.SSH.HostKey,
|
SigningKey: a.config.SSH.HostKey,
|
||||||
Password: a.config.Password,
|
Password: a.config.Password,
|
||||||
})
|
})
|
||||||
// signer, err := parseCryptoSigner(a.config.SSH.HostKey, a.config.Password)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -177,7 +182,6 @@ func (a *Authority) init() error {
|
||||||
SigningKey: a.config.SSH.UserKey,
|
SigningKey: a.config.SSH.UserKey,
|
||||||
Password: a.config.Password,
|
Password: a.config.Password,
|
||||||
})
|
})
|
||||||
// signer, err := parseCryptoSigner(a.config.SSH.UserKey, a.config.Password)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
"github.com/smallstep/certificates/db"
|
"github.com/smallstep/certificates/db"
|
||||||
|
@ -13,37 +14,41 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option sets options to the Authority.
|
// 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
|
// WithDatabase sets an already initialized authority database to a new
|
||||||
// authority. This option is intended to be use on graceful reloads.
|
// authority. This option is intended to be use on graceful reloads.
|
||||||
func WithDatabase(db db.AuthDB) Option {
|
func WithDatabase(db db.AuthDB) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.db = db
|
a.db = db
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithGetIdentityFunc sets a custom function to retrieve the identity from
|
// WithGetIdentityFunc sets a custom function to retrieve the identity from
|
||||||
// an external resource.
|
// an external resource.
|
||||||
func WithGetIdentityFunc(fn func(p provisioner.Interface, email string) (*provisioner.Identity, error)) Option {
|
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
|
a.getIdentityFunc = fn
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSSHBastionFunc sets a custom function to get the bastion for a
|
// WithSSHBastionFunc sets a custom function to get the bastion for a
|
||||||
// given user-host pair.
|
// given user-host pair.
|
||||||
func WithSSHBastionFunc(fn func(user, host string) (*Bastion, error)) Option {
|
func WithSSHBastionFunc(fn func(user, host string) (*Bastion, error)) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.sshBastionFunc = fn
|
a.sshBastionFunc = fn
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSSHGetHosts sets a custom function to get the bastion for a
|
// WithSSHGetHosts sets a custom function to get the bastion for a
|
||||||
// given user-host pair.
|
// given user-host pair.
|
||||||
func WithSSHGetHosts(fn func(cert *x509.Certificate) ([]sshutil.Host, error)) Option {
|
func WithSSHGetHosts(fn func(cert *x509.Certificate) ([]sshutil.Host, error)) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.sshGetHostsFunc = fn
|
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
|
// step ssh enabled. The token is used to validate the request, while the roots
|
||||||
// are used to validate the token.
|
// are used to validate the token.
|
||||||
func WithSSHCheckHost(fn func(ctx context.Context, principal string, tok string, roots []*x509.Certificate) (bool, error)) Option {
|
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
|
a.sshCheckHostFunc = fn
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithKeyManager defines the key manager used to get and create keys, and sign
|
// WithKeyManager defines the key manager used to get and create keys, and sign
|
||||||
// certificates.
|
// certificates.
|
||||||
func WithKeyManager(k kms.KeyManager) Option {
|
func WithKeyManager(k kms.KeyManager) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.keyManager = k
|
a.keyManager = k
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithX509Signer defines the signer used to sign X509 certificates.
|
// WithX509Signer defines the signer used to sign X509 certificates.
|
||||||
func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option {
|
func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.x509Issuer = crt
|
a.x509Issuer = crt
|
||||||
a.x509Signer = s
|
a.x509Signer = s
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
|
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
|
||||||
func WithSSHUserSigner(s ssh.Signer) Option {
|
func WithSSHUserSigner(s ssh.Signer) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.sshCAUserCertSignKey = s
|
a.sshCAUserCertSignKey = s
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithSSHHostSigner defines the signer used to sign SSH host certificates.
|
// WithSSHHostSigner defines the signer used to sign SSH host certificates.
|
||||||
func WithSSHHostSigner(s ssh.Signer) Option {
|
func WithSSHHostSigner(s ssh.Signer) Option {
|
||||||
return func(a *Authority) {
|
return func(a *Authority) error {
|
||||||
a.sshCAHostCertSignKey = s
|
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
1
go.sum
|
@ -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-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 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc=
|
||||||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
|
Loading…
Reference in a new issue