2019-11-14 23:29:04 +00:00
|
|
|
package authority
|
|
|
|
|
|
|
|
import (
|
2019-12-10 07:14:56 +00:00
|
|
|
"context"
|
2020-01-10 02:42:26 +00:00
|
|
|
"crypto"
|
2019-11-20 20:59:48 +00:00
|
|
|
"crypto/x509"
|
2020-01-11 02:33:48 +00:00
|
|
|
"encoding/pem"
|
2019-11-20 20:59:48 +00:00
|
|
|
|
2020-01-15 02:38:29 +00:00
|
|
|
"github.com/pkg/errors"
|
2019-11-15 04:38:07 +00:00
|
|
|
"github.com/smallstep/certificates/authority/provisioner"
|
2019-11-14 23:29:04 +00:00
|
|
|
"github.com/smallstep/certificates/db"
|
2020-01-10 02:42:26 +00:00
|
|
|
"github.com/smallstep/certificates/kms"
|
2019-11-21 01:23:51 +00:00
|
|
|
"github.com/smallstep/certificates/sshutil"
|
2020-01-10 02:42:26 +00:00
|
|
|
"golang.org/x/crypto/ssh"
|
2019-11-14 23:29:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Option sets options to the Authority.
|
2020-01-11 02:33:48 +00:00
|
|
|
type Option func(*Authority) error
|
2019-11-14 23:29:04 +00:00
|
|
|
|
|
|
|
// 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 {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2019-11-14 23:29:04 +00:00
|
|
|
a.db = db
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2019-11-14 23:29:04 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-15 02:24:58 +00:00
|
|
|
|
2019-11-20 19:32:27 +00:00
|
|
|
// 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 {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2019-11-20 19:32:27 +00:00
|
|
|
a.getIdentityFunc = fn
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2019-11-20 19:32:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-15 04:38:07 +00:00
|
|
|
// WithSSHBastionFunc sets a custom function to get the bastion for a
|
2019-11-15 02:24:58 +00:00
|
|
|
// given user-host pair.
|
|
|
|
func WithSSHBastionFunc(fn func(user, host string) (*Bastion, error)) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2019-11-15 02:24:58 +00:00
|
|
|
a.sshBastionFunc = fn
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2019-11-15 02:24:58 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-15 04:38:07 +00:00
|
|
|
|
2019-11-20 19:32:27 +00:00
|
|
|
// WithSSHGetHosts sets a custom function to get the bastion for a
|
|
|
|
// given user-host pair.
|
2019-11-21 01:23:51 +00:00
|
|
|
func WithSSHGetHosts(fn func(cert *x509.Certificate) ([]sshutil.Host, error)) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2019-11-20 19:32:27 +00:00
|
|
|
a.sshGetHostsFunc = fn
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2019-11-15 04:38:07 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-10 07:14:56 +00:00
|
|
|
|
|
|
|
// WithSSHCheckHost sets a custom function to check whether a given host is
|
|
|
|
// 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 {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2019-12-10 07:14:56 +00:00
|
|
|
a.sshCheckHostFunc = fn
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2019-12-10 07:14:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-10 02:42:26 +00:00
|
|
|
|
|
|
|
// WithKeyManager defines the key manager used to get and create keys, and sign
|
|
|
|
// certificates.
|
|
|
|
func WithKeyManager(k kms.KeyManager) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2020-01-10 02:42:26 +00:00
|
|
|
a.keyManager = k
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2020-01-10 02:42:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithX509Signer defines the signer used to sign X509 certificates.
|
|
|
|
func WithX509Signer(crt *x509.Certificate, s crypto.Signer) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2020-01-10 02:42:26 +00:00
|
|
|
a.x509Issuer = crt
|
|
|
|
a.x509Signer = s
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2020-01-10 02:42:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithSSHUserSigner defines the signer used to sign SSH user certificates.
|
2020-01-15 02:38:29 +00:00
|
|
|
func WithSSHUserSigner(s crypto.Signer) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2020-01-15 02:38:29 +00:00
|
|
|
signer, err := ssh.NewSignerFromSigner(s)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating ssh user signer")
|
|
|
|
}
|
|
|
|
a.sshCAUserCertSignKey = signer
|
|
|
|
// Append public key to list of user certs
|
|
|
|
pub := signer.PublicKey()
|
|
|
|
a.sshCAUserCerts = append(a.sshCAUserCerts, pub)
|
|
|
|
a.sshCAUserFederatedCerts = append(a.sshCAUserFederatedCerts, pub)
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2020-01-10 02:42:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithSSHHostSigner defines the signer used to sign SSH host certificates.
|
2020-01-15 02:38:29 +00:00
|
|
|
func WithSSHHostSigner(s crypto.Signer) Option {
|
2020-01-11 02:33:48 +00:00
|
|
|
return func(a *Authority) error {
|
2020-01-15 02:38:29 +00:00
|
|
|
signer, err := ssh.NewSignerFromSigner(s)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "error creating ssh host signer")
|
|
|
|
}
|
|
|
|
a.sshCAHostCertSignKey = signer
|
|
|
|
// Append public key to list of host certs
|
|
|
|
pub := signer.PublicKey()
|
|
|
|
a.sshCAHostCerts = append(a.sshCAHostCerts, pub)
|
|
|
|
a.sshCAHostFederatedCerts = append(a.sshCAHostFederatedCerts, pub)
|
2020-01-11 02:33:48 +00:00
|
|
|
return nil
|
2020-01-10 02:42:26 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-11 02:33:48 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|