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/templates"
|
||||
"github.com/smallstep/cli/crypto/pemutil"
|
||||
"github.com/smallstep/cli/crypto/x509util"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
@ -27,24 +26,30 @@ const (
|
|||
|
||||
// Authority implements the Certificate Authority internal interface.
|
||||
type Authority struct {
|
||||
config *Config
|
||||
rootX509Certs []*x509.Certificate
|
||||
intermediateIdentity *x509util.Identity
|
||||
keyManager kms.KeyManager
|
||||
x509Signer crypto.Signer
|
||||
x509Issuer *x509.Certificate
|
||||
config *Config
|
||||
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
|
||||
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
|
||||
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
|
||||
// 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Add federated roots
|
||||
for _, path := range a.config.FederatedRoots {
|
||||
crt, err := pemutil.ReadCertificate(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, crt := range a.rootX509Certs {
|
||||
sum := sha256.Sum256(crt.Raw)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -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
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-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=
|
||||
|
|
Loading…
Reference in a new issue