2018-10-05 21:48:36 +00:00
package authority
import (
2019-12-10 07:14:56 +00:00
"context"
2020-11-03 13:26:46 +00:00
"crypto"
2018-10-05 21:48:36 +00:00
"crypto/sha256"
2019-01-05 01:51:32 +00:00
"crypto/x509"
2018-10-05 21:48:36 +00:00
"encoding/hex"
2022-03-29 00:51:39 +00:00
"fmt"
2020-05-08 01:59:30 +00:00
"log"
2021-07-20 02:28:06 +00:00
"strings"
2018-10-05 21:48:36 +00:00
"sync"
"time"
2019-08-01 22:04:56 +00:00
"github.com/pkg/errors"
2022-03-31 14:12:29 +00:00
"golang.org/x/crypto/ssh"
2022-08-09 00:58:18 +00:00
"go.step.sm/crypto/kms"
kmsapi "go.step.sm/crypto/kms/apiv1"
"go.step.sm/crypto/kms/sshagentkms"
2022-03-31 14:12:29 +00:00
"go.step.sm/crypto/pemutil"
"go.step.sm/linkedca"
2021-05-18 04:07:25 +00:00
"github.com/smallstep/certificates/authority/admin"
2021-05-03 19:48:20 +00:00
adminDBNosql "github.com/smallstep/certificates/authority/admin/db/nosql"
"github.com/smallstep/certificates/authority/administrator"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/certificates/authority/config"
2022-03-08 12:26:07 +00:00
"github.com/smallstep/certificates/authority/policy"
2019-03-06 23:00:23 +00:00
"github.com/smallstep/certificates/authority/provisioner"
2021-08-11 00:30:33 +00:00
"github.com/smallstep/certificates/cas"
2020-09-09 02:26:32 +00:00
casapi "github.com/smallstep/certificates/cas/apiv1"
2019-03-05 08:07:13 +00:00
"github.com/smallstep/certificates/db"
2021-08-11 00:30:33 +00:00
"github.com/smallstep/certificates/scep"
2019-11-21 01:01:31 +00:00
"github.com/smallstep/certificates/templates"
2021-05-03 19:48:20 +00:00
"github.com/smallstep/nosql"
2018-10-05 21:48:36 +00:00
)
// Authority implements the Certificate Authority internal interface.
type Authority struct {
2021-07-20 02:28:06 +00:00
config * config . Config
keyManager kms . KeyManager
provisioners * provisioner . Collection
admins * administrator . Collection
db db . AuthDB
adminDB admin . DB
templates * templates . Templates
linkedCAToken string
2020-01-11 02:33:48 +00:00
// X509 CA
2021-09-16 18:55:41 +00:00
password [ ] byte
issuerPassword [ ] byte
2020-09-09 02:26:32 +00:00
x509CAService cas . CertificateAuthorityService
2020-01-11 02:33:48 +00:00
rootX509Certs [ ] * x509 . Certificate
2021-05-03 19:48:20 +00:00
rootX509CertPool * x509 . CertPool
2020-01-11 02:33:48 +00:00
federatedX509Certs [ ] * x509 . Certificate
certificates * sync . Map
2022-02-02 22:36:58 +00:00
x509Enforcers [ ] provisioner . CertificateEnforcer
2020-01-11 02:33:48 +00:00
2021-02-25 23:32:21 +00:00
// SCEP CA
scepService * scep . Service
2020-01-11 02:33:48 +00:00
// SSH CA
2021-09-16 18:55:41 +00:00
sshHostPassword [ ] byte
sshUserPassword [ ] byte
2019-10-09 01:09:41 +00:00
sshCAUserCertSignKey ssh . Signer
sshCAHostCertSignKey ssh . Signer
sshCAUserCerts [ ] ssh . PublicKey
sshCAHostCerts [ ] ssh . PublicKey
sshCAUserFederatedCerts [ ] ssh . PublicKey
sshCAHostFederatedCerts [ ] ssh . PublicKey
2020-01-11 02:33:48 +00:00
2021-11-04 06:05:07 +00:00
// CRL vars
2022-04-05 03:19:13 +00:00
crlTicker * time . Ticker
2021-11-04 06:05:07 +00:00
2018-10-05 21:48:36 +00:00
// Do not re-initialize
2020-01-11 02:33:48 +00:00
initOnce bool
startTime time . Time
2019-11-15 02:24:58 +00:00
// Custom functions
2022-03-10 21:01:08 +00:00
sshBastionFunc func ( ctx context . Context , user , hostname string ) ( * config . Bastion , error )
sshCheckHostFunc func ( ctx context . Context , principal string , tok string , roots [ ] * x509 . Certificate ) ( bool , error )
sshGetHostsFunc func ( ctx context . Context , cert * x509 . Certificate ) ( [ ] config . Host , error )
getIdentityFunc provisioner . GetIdentityFunc
authorizeRenewFunc provisioner . AuthorizeRenewFunc
authorizeSSHRenewFunc provisioner . AuthorizeSSHRenewFunc
2021-05-03 19:48:20 +00:00
2022-03-08 12:26:07 +00:00
// Policy engines
2022-04-26 11:12:16 +00:00
policyEngine * policy . Engine
2022-03-08 12:26:07 +00:00
2021-05-03 19:48:20 +00:00
adminMutex sync . RWMutex
2022-05-12 00:04:43 +00:00
// Do Not initialize the authority
skipInit bool
2018-10-05 21:48:36 +00:00
}
2022-05-12 00:04:43 +00:00
// Info contains information about the authority.
2022-04-05 17:59:25 +00:00
type Info struct {
2022-03-30 23:08:10 +00:00
StartTime time . Time
RootX509Certs [ ] * x509 . Certificate
SSHCAUserPublicKey [ ] byte
SSHCAHostPublicKey [ ] byte
2022-04-04 19:16:37 +00:00
DNSNames [ ] string
2022-03-30 22:48:42 +00:00
}
2018-10-05 21:48:36 +00:00
// New creates and initiates a new Authority type.
2021-10-08 18:59:57 +00:00
func New ( cfg * config . Config , opts ... Option ) ( * Authority , error ) {
err := cfg . Validate ( )
2018-10-25 22:40:12 +00:00
if err != nil {
2018-10-05 21:48:36 +00:00
return nil , err
}
2018-10-25 22:40:12 +00:00
2018-10-05 21:48:36 +00:00
var a = & Authority {
2021-10-08 18:59:57 +00:00
config : cfg ,
2019-03-06 23:00:23 +00:00
certificates : new ( sync . Map ) ,
2018-10-05 21:48:36 +00:00
}
2020-01-11 02:33:48 +00:00
// Apply options.
for _ , fn := range opts {
if err := fn ( a ) ; err != nil {
return nil , err
}
2019-05-10 22:58:37 +00:00
}
2020-01-11 02:33:48 +00:00
2022-05-12 00:04:43 +00:00
if ! a . skipInit {
// Initialize authority from options or configuration.
if err := a . init ( ) ; err != nil {
2022-04-25 17:23:07 +00:00
return nil , err
}
2018-10-05 21:48:36 +00:00
}
2020-01-11 02:33:48 +00:00
2018-10-05 21:48:36 +00:00
return a , nil
}
2020-05-06 00:46:22 +00:00
// NewEmbedded initializes an authority that can be embedded in a different
// project without the limitations of the config.
func NewEmbedded ( opts ... Option ) ( * Authority , error ) {
2020-05-05 01:52:18 +00:00
a := & Authority {
2021-05-03 19:48:20 +00:00
config : & config . Config { } ,
2020-05-05 01:52:18 +00:00
certificates : new ( sync . Map ) ,
}
// Apply options.
for _ , fn := range opts {
if err := fn ( a ) ; err != nil {
return nil , err
}
}
// Validate required options
switch {
2020-05-06 20:00:42 +00:00
case a . config == nil :
return nil , errors . New ( "cannot create an authority without a configuration" )
2020-05-05 01:52:18 +00:00
case len ( a . rootX509Certs ) == 0 && a . config . Root . HasEmpties ( ) :
return nil , errors . New ( "cannot create an authority without a root certificate" )
2020-09-16 20:31:26 +00:00
case a . x509CAService == nil && a . config . IntermediateCert == "" :
2020-05-05 01:52:18 +00:00
return nil , errors . New ( "cannot create an authority without an issuer certificate" )
2020-09-16 20:31:26 +00:00
case a . x509CAService == nil && a . config . IntermediateKey == "" :
2020-05-05 01:52:18 +00:00
return nil , errors . New ( "cannot create an authority without an issuer signer" )
}
2020-05-06 20:00:42 +00:00
// Initialize config required fields.
2021-05-03 19:48:20 +00:00
a . config . Init ( )
2020-05-06 20:00:42 +00:00
2022-05-12 00:04:43 +00:00
if ! a . skipInit {
// Initialize authority from options or configuration.
if err := a . init ( ) ; err != nil {
return nil , err
}
2020-05-05 01:52:18 +00:00
}
return a , nil
}
2022-04-26 19:54:54 +00:00
type authorityKey struct { }
// NewContext adds the given authority to the context.
func NewContext ( ctx context . Context , a * Authority ) context . Context {
return context . WithValue ( ctx , authorityKey { } , a )
}
// FromContext returns the current authority from the given context.
func FromContext ( ctx context . Context ) ( a * Authority , ok bool ) {
a , ok = ctx . Value ( authorityKey { } ) . ( * Authority )
return
}
2022-04-27 18:38:06 +00:00
// MustFromContext returns the current authority from the given context. It will
// panic if the authority is not in the context.
func MustFromContext ( ctx context . Context ) * Authority {
if a , ok := FromContext ( ctx ) ; ! ok {
panic ( "authority is not in the context" )
} else {
return a
}
2022-04-26 19:54:54 +00:00
}
2022-04-25 17:23:07 +00:00
// ReloadAdminResources reloads admins and provisioners from the DB.
func ( a * Authority ) ReloadAdminResources ( ctx context . Context ) error {
2021-05-03 19:48:20 +00:00
var (
provList provisioner . List
adminList [ ] * linkedca . Admin
)
if a . config . AuthorityConfig . EnableAdmin {
provs , err := a . adminDB . GetProvisioners ( ctx )
if err != nil {
return admin . WrapErrorISE ( err , "error getting provisioners to initialize authority" )
}
provList , err = provisionerListToCertificates ( provs )
if err != nil {
return admin . WrapErrorISE ( err , "error converting provisioner list to certificates" )
}
adminList , err = a . adminDB . GetAdmins ( ctx )
if err != nil {
return admin . WrapErrorISE ( err , "error getting admins to initialize authority" )
}
} else {
provList = a . config . AuthorityConfig . Provisioners
adminList = a . config . AuthorityConfig . Admins
2021-05-18 04:07:25 +00:00
}
2021-05-03 19:48:20 +00:00
provisionerConfig , err := a . generateProvisionerConfig ( ctx )
2021-05-18 04:07:25 +00:00
if err != nil {
2021-05-03 19:48:20 +00:00
return admin . WrapErrorISE ( err , "error generating provisioner config" )
2021-05-18 04:07:25 +00:00
}
2021-05-03 19:48:20 +00:00
// Create provisioner collection.
provClxn := provisioner . NewCollection ( provisionerConfig . Audiences )
for _ , p := range provList {
2022-02-28 19:04:40 +00:00
if err := p . Init ( provisionerConfig ) ; err != nil {
2021-05-18 04:07:25 +00:00
return err
}
2021-05-03 19:48:20 +00:00
if err := provClxn . Store ( p ) ; err != nil {
2021-05-18 04:07:25 +00:00
return err
}
}
2021-05-03 19:48:20 +00:00
// Create admin collection.
adminClxn := administrator . NewCollection ( provClxn )
for _ , adm := range adminList {
p , ok := provClxn . Load ( adm . ProvisionerId )
if ! ok {
return admin . NewErrorISE ( "provisioner %s not found when loading admin %s" ,
adm . ProvisionerId , adm . Id )
}
if err := adminClxn . Store ( adm , p ) ; err != nil {
2021-05-18 04:07:25 +00:00
return err
}
}
2021-05-03 19:48:20 +00:00
a . config . AuthorityConfig . Provisioners = provList
a . provisioners = provClxn
a . config . AuthorityConfig . Admins = adminList
a . admins = adminClxn
2022-03-15 14:51:45 +00:00
2021-05-18 04:07:25 +00:00
return nil
}
2018-10-05 21:48:36 +00:00
// init performs validation and initializes the fields of an Authority struct.
func ( a * Authority ) init ( ) error {
// Check if handler has already been validated/initialized.
if a . initOnce {
return nil
}
var err error
2022-05-10 23:51:09 +00:00
ctx := NewContext ( context . Background ( ) , a )
2020-01-10 02:42:26 +00:00
2021-09-16 18:55:41 +00:00
// Set password if they are not set.
var configPassword [ ] byte
if a . config . Password != "" {
configPassword = [ ] byte ( a . config . Password )
}
if configPassword != nil && a . password == nil {
a . password = configPassword
}
if a . sshHostPassword == nil {
a . sshHostPassword = a . password
}
if a . sshUserPassword == nil {
a . sshUserPassword = a . password
}
2021-08-02 19:13:39 +00:00
// Automatically enable admin for all linked cas.
if a . linkedCAToken != "" {
a . config . AuthorityConfig . EnableAdmin = true
}
2020-09-21 22:27:20 +00:00
// 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 . db == nil {
if a . db , err = db . New ( a . config . DB ) ; err != nil {
return err
}
}
2020-01-11 02:33:48 +00:00
// Initialize key manager if it has not been set in the options.
2020-01-10 02:42:26 +00:00
if a . keyManager == nil {
2020-01-15 02:47:05 +00:00
var options kmsapi . Options
if a . config . KMS != nil {
options = * a . config . KMS
}
2022-05-10 23:51:09 +00:00
a . keyManager , err = kms . New ( ctx , options )
2020-01-10 02:42:26 +00:00
if err != nil {
return err
}
}
2022-03-23 21:56:39 +00:00
// Initialize linkedca client if necessary. On a linked RA, the issuer
// configuration might come from majordomo.
var linkedcaClient * linkedCaClient
if a . config . AuthorityConfig . EnableAdmin && a . linkedCAToken != "" && a . adminDB == nil {
linkedcaClient , err = newLinkedCAClient ( a . linkedCAToken )
2020-01-10 02:42:26 +00:00
if err != nil {
return err
}
2022-03-23 21:56:39 +00:00
// If authorityId is configured make sure it matches the one in the token
if id := a . config . AuthorityConfig . AuthorityID ; id != "" && ! strings . EqualFold ( id , linkedcaClient . authorityID ) {
return errors . New ( "error initializing linkedca: token authority and configured authority do not match" )
}
2022-08-03 02:28:49 +00:00
a . config . AuthorityConfig . AuthorityID = linkedcaClient . authorityID
2022-03-23 21:56:39 +00:00
linkedcaClient . Run ( )
2020-01-10 02:42:26 +00:00
}
2020-09-21 22:27:20 +00:00
// Initialize the X.509 CA Service if it has not been set in the options.
if a . x509CAService == nil {
var options casapi . Options
2020-10-20 01:08:51 +00:00
if a . config . AuthorityConfig . Options != nil {
options = * a . config . AuthorityConfig . Options
2020-09-21 22:27:20 +00:00
}
2022-08-03 02:28:49 +00:00
// AuthorityID might be empty. It's always available linked CAs/RAs.
options . AuthorityID = a . config . AuthorityConfig . AuthorityID
2022-03-23 21:56:39 +00:00
// Configure linked RA
if linkedcaClient != nil && options . CertificateAuthority == "" {
2022-05-10 23:51:09 +00:00
conf , err := linkedcaClient . GetConfiguration ( ctx )
2022-03-23 21:56:39 +00:00
if err != nil {
return err
}
if conf . RaConfig != nil {
options . CertificateAuthority = conf . RaConfig . CaUrl
options . CertificateAuthorityFingerprint = conf . RaConfig . Fingerprint
options . CertificateIssuer = & casapi . CertificateIssuer {
Type : conf . RaConfig . Provisioner . Type . String ( ) ,
Provisioner : conf . RaConfig . Provisioner . Name ,
}
2022-08-16 21:39:02 +00:00
// Configure the RA authority type if needed
if options . Type == "" {
options . Type = casapi . StepCAS
}
}
// Remote configuration is currently only supported on a linked RA
if sc := conf . ServerConfig ; sc != nil {
if a . config . Address == "" {
a . config . Address = sc . Address
}
if len ( a . config . DNSNames ) == 0 {
a . config . DNSNames = sc . DnsNames
}
2022-03-23 21:56:39 +00:00
}
}
2021-09-16 18:55:41 +00:00
// Set the issuer password if passed in the flags.
if options . CertificateIssuer != nil && a . issuerPassword != nil {
options . CertificateIssuer . Password = string ( a . issuerPassword )
}
2020-09-21 22:27:20 +00:00
// Read intermediate and create X509 signer for default CAS.
if options . Is ( casapi . SoftCAS ) {
2020-12-24 04:41:10 +00:00
options . CertificateChain , err = pemutil . ReadCertificateBundle ( a . config . IntermediateCert )
2020-09-21 22:27:20 +00:00
if err != nil {
return err
}
options . Signer , err = a . keyManager . CreateSigner ( & kmsapi . CreateSignerRequest {
SigningKey : a . config . IntermediateKey ,
2021-09-16 18:55:41 +00:00
Password : [ ] byte ( a . password ) ,
2020-09-21 22:27:20 +00:00
} )
if err != nil {
return err
}
}
2022-05-10 23:51:09 +00:00
a . x509CAService , err = cas . New ( ctx , options )
2020-09-21 22:27:20 +00:00
if err != nil {
2019-05-10 22:58:37 +00:00
return err
}
2020-09-21 22:27:20 +00:00
// Get root certificate from CAS.
if srv , ok := a . x509CAService . ( casapi . CertificateAuthorityGetter ) ; ok {
resp , err := srv . GetCertificateAuthority ( & casapi . GetCertificateAuthorityRequest {
2020-10-20 01:08:51 +00:00
Name : options . CertificateAuthority ,
2020-09-21 22:27:20 +00:00
} )
if err != nil {
return err
}
a . rootX509Certs = append ( a . rootX509Certs , resp . RootCertificate )
}
2019-03-05 08:07:13 +00:00
}
2020-01-11 02:33:48 +00:00
// 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
2019-01-07 23:30:28 +00:00
}
2020-01-11 02:33:48 +00:00
}
for _ , crt := range a . rootX509Certs {
2019-01-07 23:30:28 +00:00
sum := sha256 . Sum256 ( crt . Raw )
a . certificates . Store ( hex . EncodeToString ( sum [ : ] ) , crt )
}
2018-10-05 21:48:36 +00:00
2021-05-03 19:48:20 +00:00
a . rootX509CertPool = x509 . NewCertPool ( )
for _ , cert := range a . rootX509Certs {
a . rootX509CertPool . AddCert ( cert )
}
2020-01-11 02:33:48 +00:00
// 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
2019-01-05 01:51:32 +00:00
}
2020-01-11 02:33:48 +00:00
}
for _ , crt := range a . federatedX509Certs {
2019-01-05 01:51:32 +00:00
sum := sha256 . Sum256 ( crt . Raw )
a . certificates . Store ( hex . EncodeToString ( sum [ : ] ) , crt )
}
2019-08-01 22:04:56 +00:00
// Decrypt and load SSH keys
2020-06-17 00:26:18 +00:00
var tmplVars templates . Step
2019-08-01 22:04:56 +00:00
if a . config . SSH != nil {
if a . config . SSH . HostKey != "" {
2020-01-10 02:42:26 +00:00
signer , err := a . keyManager . CreateSigner ( & kmsapi . CreateSignerRequest {
SigningKey : a . config . SSH . HostKey ,
2021-09-16 18:55:41 +00:00
Password : [ ] byte ( a . sshHostPassword ) ,
2020-01-10 02:42:26 +00:00
} )
2019-08-01 22:04:56 +00:00
if err != nil {
return err
}
2020-11-03 13:26:46 +00:00
// If our signer is from sshagentkms, just unwrap it instead of
// wrapping it in another layer, and this prevents crypto from
// erroring out with: ssh: unsupported key type *agent.Key
switch s := signer . ( type ) {
case * sshagentkms . WrappedSSHSigner :
2022-08-09 00:58:18 +00:00
a . sshCAHostCertSignKey = s . Signer
2020-11-03 13:26:46 +00:00
case crypto . Signer :
a . sshCAHostCertSignKey , err = ssh . NewSignerFromSigner ( s )
default :
return errors . Errorf ( "unsupported signer type %T" , signer )
}
2019-09-25 02:12:13 +00:00
if err != nil {
return errors . Wrap ( err , "error creating ssh signer" )
}
2019-10-09 01:09:41 +00:00
// Append public key to list of host certs
a . sshCAHostCerts = append ( a . sshCAHostCerts , a . sshCAHostCertSignKey . PublicKey ( ) )
a . sshCAHostFederatedCerts = append ( a . sshCAHostFederatedCerts , a . sshCAHostCertSignKey . PublicKey ( ) )
2019-08-01 22:04:56 +00:00
}
if a . config . SSH . UserKey != "" {
2020-01-10 02:42:26 +00:00
signer , err := a . keyManager . CreateSigner ( & kmsapi . CreateSignerRequest {
SigningKey : a . config . SSH . UserKey ,
2021-09-16 18:55:41 +00:00
Password : [ ] byte ( a . sshUserPassword ) ,
2020-01-10 02:42:26 +00:00
} )
2019-08-01 22:04:56 +00:00
if err != nil {
return err
}
2020-11-03 13:26:46 +00:00
// If our signer is from sshagentkms, just unwrap it instead of
// wrapping it in another layer, and this prevents crypto from
// erroring out with: ssh: unsupported key type *agent.Key
switch s := signer . ( type ) {
case * sshagentkms . WrappedSSHSigner :
2022-08-09 00:58:18 +00:00
a . sshCAUserCertSignKey = s . Signer
2020-11-03 13:26:46 +00:00
case crypto . Signer :
a . sshCAUserCertSignKey , err = ssh . NewSignerFromSigner ( s )
default :
return errors . Errorf ( "unsupported signer type %T" , signer )
}
2019-09-25 02:12:13 +00:00
if err != nil {
return errors . Wrap ( err , "error creating ssh signer" )
}
2019-10-09 01:09:41 +00:00
// Append public key to list of user certs
2019-10-25 01:36:02 +00:00
a . sshCAUserCerts = append ( a . sshCAUserCerts , a . sshCAUserCertSignKey . PublicKey ( ) )
2019-10-09 01:09:41 +00:00
a . sshCAUserFederatedCerts = append ( a . sshCAUserFederatedCerts , a . sshCAUserCertSignKey . PublicKey ( ) )
}
2021-09-28 22:07:09 +00:00
// Append other public keys and add them to the template variables.
2019-10-09 01:09:41 +00:00
for _ , key := range a . config . SSH . Keys {
2021-09-28 22:07:09 +00:00
publicKey := key . PublicKey ( )
2019-10-09 01:09:41 +00:00
switch key . Type {
case provisioner . SSHHostCert :
if key . Federated {
2021-09-28 22:07:09 +00:00
a . sshCAHostFederatedCerts = append ( a . sshCAHostFederatedCerts , publicKey )
2019-10-09 01:09:41 +00:00
} else {
2021-09-28 22:07:09 +00:00
a . sshCAHostCerts = append ( a . sshCAHostCerts , publicKey )
2019-10-09 01:09:41 +00:00
}
case provisioner . SSHUserCert :
if key . Federated {
2021-09-28 22:07:09 +00:00
a . sshCAUserFederatedCerts = append ( a . sshCAUserFederatedCerts , publicKey )
2019-10-09 01:09:41 +00:00
} else {
2021-09-28 22:07:09 +00:00
a . sshCAUserCerts = append ( a . sshCAUserCerts , publicKey )
2019-10-09 01:09:41 +00:00
}
default :
return errors . Errorf ( "unsupported type %s" , key . Type )
}
2019-08-01 22:04:56 +00:00
}
}
2019-07-24 01:46:43 +00:00
2021-09-29 01:50:45 +00:00
// Configure template variables. On the template variables HostFederatedKeys
// and UserFederatedKeys we will skip the actual CA that will be available
// in HostKey and UserKey.
//
// We cannot do it in the previous blocks because this configuration can be
// injected using options.
if a . sshCAHostCertSignKey != nil {
tmplVars . SSH . HostKey = a . sshCAHostCertSignKey . PublicKey ( )
tmplVars . SSH . HostFederatedKeys = append ( tmplVars . SSH . HostFederatedKeys , a . sshCAHostFederatedCerts [ 1 : ] ... )
} else {
tmplVars . SSH . HostFederatedKeys = append ( tmplVars . SSH . HostFederatedKeys , a . sshCAHostFederatedCerts ... )
}
if a . sshCAUserCertSignKey != nil {
tmplVars . SSH . UserKey = a . sshCAUserCertSignKey . PublicKey ( )
tmplVars . SSH . UserFederatedKeys = append ( tmplVars . SSH . UserFederatedKeys , a . sshCAUserFederatedCerts [ 1 : ] ... )
} else {
tmplVars . SSH . UserFederatedKeys = append ( tmplVars . SSH . UserFederatedKeys , a . sshCAUserFederatedCerts ... )
}
2021-03-12 13:18:36 +00:00
// Check if a KMS with decryption capability is required and available
if a . requiresDecrypter ( ) {
2021-03-12 15:27:26 +00:00
if _ , ok := a . keyManager . ( kmsapi . Decrypter ) ; ! ok {
2021-03-12 13:18:36 +00:00
return errors . New ( "keymanager doesn't provide crypto.Decrypter" )
}
}
2021-03-12 15:58:52 +00:00
// TODO: decide if this is a good approach for providing the SCEP functionality
// It currently mirrors the logic for the x509CAService
if a . requiresSCEPService ( ) && a . scepService == nil {
2021-03-21 15:42:41 +00:00
var options scep . Options
2021-03-12 15:58:52 +00:00
// Read intermediate and create X509 signer and decrypter for default CAS.
2021-03-21 15:42:41 +00:00
options . CertificateChain , err = pemutil . ReadCertificateBundle ( a . config . IntermediateCert )
if err != nil {
return err
}
2022-01-14 09:48:23 +00:00
options . CertificateChain = append ( options . CertificateChain , a . rootX509Certs ... )
2021-03-21 15:42:41 +00:00
options . Signer , err = a . keyManager . CreateSigner ( & kmsapi . CreateSignerRequest {
SigningKey : a . config . IntermediateKey ,
2021-09-16 18:55:41 +00:00
Password : [ ] byte ( a . password ) ,
2021-03-21 15:42:41 +00:00
} )
if err != nil {
return err
}
if km , ok := a . keyManager . ( kmsapi . Decrypter ) ; ok {
options . Decrypter , err = km . CreateDecrypter ( & kmsapi . CreateDecrypterRequest {
DecryptionKey : a . config . IntermediateKey ,
2021-09-16 18:55:41 +00:00
Password : [ ] byte ( a . password ) ,
2021-03-12 15:58:52 +00:00
} )
if err != nil {
return err
}
}
2022-05-10 23:51:09 +00:00
a . scepService , err = scep . NewService ( ctx , options )
2021-03-12 15:58:52 +00:00
if err != nil {
return err
}
// TODO: mimick the x509CAService GetCertificateAuthority here too?
}
2021-08-02 19:13:39 +00:00
if a . config . AuthorityConfig . EnableAdmin {
2021-05-03 19:48:20 +00:00
// Initialize step-ca Admin Database if it's not already initialized using
// WithAdminDB.
if a . adminDB == nil {
2022-03-23 21:56:39 +00:00
if linkedcaClient != nil {
a . adminDB = linkedcaClient
2021-07-12 16:11:00 +00:00
} else {
2022-03-23 21:56:39 +00:00
a . adminDB , err = adminDBNosql . New ( a . db . ( nosql . DB ) , admin . DefaultAuthorityID )
2021-07-12 16:11:00 +00:00
if err != nil {
return err
}
2021-05-03 19:48:20 +00:00
}
2019-10-28 18:50:43 +00:00
}
2021-05-03 19:48:20 +00:00
2022-05-10 23:51:09 +00:00
provs , err := a . adminDB . GetProvisioners ( ctx )
2021-05-03 19:48:20 +00:00
if err != nil {
return admin . WrapErrorISE ( err , "error loading provisioners to initialize authority" )
2018-10-05 21:48:36 +00:00
}
2021-08-11 00:30:33 +00:00
if len ( provs ) == 0 && ! strings . EqualFold ( a . config . AuthorityConfig . DeploymentType , "linked" ) {
2021-05-03 19:48:20 +00:00
// Create First Provisioner
2022-05-10 23:51:09 +00:00
prov , err := CreateFirstProvisioner ( ctx , a . adminDB , string ( a . password ) )
2021-05-03 19:48:20 +00:00
if err != nil {
return admin . WrapErrorISE ( err , "error creating first provisioner" )
}
// Create first admin
2022-05-10 23:51:09 +00:00
if err := a . adminDB . CreateAdmin ( ctx , & linkedca . Admin {
2021-05-03 19:48:20 +00:00
ProvisionerId : prov . Id ,
Subject : "step" ,
Type : linkedca . Admin_SUPER_ADMIN ,
} ) ; err != nil {
return admin . WrapErrorISE ( err , "error creating first admin" )
}
2021-05-18 04:07:25 +00:00
}
}
2018-10-05 21:48:36 +00:00
2021-05-03 19:48:20 +00:00
// Load Provisioners and Admins
2022-05-23 22:31:43 +00:00
if err := a . ReloadAdminResources ( ctx ) ; err != nil {
2021-05-03 19:48:20 +00:00
return err
}
2022-03-21 14:53:59 +00:00
// Load x509 and SSH Policy Engines
2022-05-10 23:51:09 +00:00
if err := a . reloadPolicyEngines ( ctx ) ; err != nil {
2021-05-03 19:48:20 +00:00
return err
}
2020-06-17 00:26:18 +00:00
// Configure templates, currently only ssh templates are supported.
if a . sshCAHostCertSignKey != nil || a . sshCAUserCertSignKey != nil {
2020-06-17 00:57:35 +00:00
a . templates = a . config . Templates
if a . templates == nil {
a . templates = templates . DefaultTemplates ( )
2019-10-04 02:03:38 +00:00
}
2020-06-17 00:57:35 +00:00
if a . templates . Data == nil {
a . templates . Data = make ( map [ string ] interface { } )
2019-10-04 02:03:38 +00:00
}
2020-06-17 00:57:35 +00:00
a . templates . Data [ "Step" ] = tmplVars
2019-10-04 02:03:38 +00:00
}
2019-03-12 01:14:20 +00:00
// JWT numeric dates are seconds.
a . startTime = time . Now ( ) . Truncate ( time . Second )
2018-10-05 21:48:36 +00:00
// Set flag indicating that initialization has been completed, and should
// not be repeated.
a . initOnce = true
2021-11-04 06:05:07 +00:00
// Start the CRL generator
2022-09-14 18:16:47 +00:00
if a . config . CRL != nil && a . config . CRL . Generate {
if v := a . config . CRL . CacheDuration ; v != nil && v . Duration > 0 {
2021-11-04 06:05:07 +00:00
err := a . startCRLGenerator ( )
if err != nil {
return err
}
}
}
2018-10-05 21:48:36 +00:00
return nil
}
2019-03-05 08:07:13 +00:00
2022-05-10 23:51:09 +00:00
// GetID returns the define authority id or a zero uuid.
func ( a * Authority ) GetID ( ) string {
const zeroUUID = "00000000-0000-0000-0000-000000000000"
if id := a . config . AuthorityConfig . AuthorityID ; id != "" {
return id
}
return zeroUUID
}
2019-05-11 00:54:18 +00:00
// GetDatabase returns the authority database. If the configuration does not
// define a database, GetDatabase will return a db.SimpleDB instance.
2019-05-10 22:58:37 +00:00
func ( a * Authority ) GetDatabase ( ) db . AuthDB {
return a . db
}
2021-05-03 19:48:20 +00:00
// GetAdminDatabase returns the admin database, if one exists.
func ( a * Authority ) GetAdminDatabase ( ) admin . DB {
2021-05-20 20:01:58 +00:00
return a . adminDB
2021-05-07 00:03:12 +00:00
}
2022-04-25 17:23:07 +00:00
// GetConfig returns the config.
func ( a * Authority ) GetConfig ( ) * config . Config {
return a . config
}
// GetInfo returns information about the authority.
2022-04-05 17:59:25 +00:00
func ( a * Authority ) GetInfo ( ) Info {
ai := Info {
2022-03-30 23:08:10 +00:00
StartTime : a . startTime ,
2022-03-30 22:48:42 +00:00
RootX509Certs : a . rootX509Certs ,
2022-04-04 19:16:37 +00:00
DNSNames : a . config . DNSNames ,
2022-03-30 22:48:42 +00:00
}
2022-03-30 23:05:26 +00:00
if a . sshCAUserCertSignKey != nil {
ai . SSHCAUserPublicKey = ssh . MarshalAuthorizedKey ( a . sshCAUserCertSignKey . PublicKey ( ) )
}
if a . sshCAHostCertSignKey != nil {
ai . SSHCAHostPublicKey = ssh . MarshalAuthorizedKey ( a . sshCAHostCertSignKey . PublicKey ( ) )
}
return ai
2022-03-30 22:48:42 +00:00
}
2021-07-07 00:14:13 +00:00
// IsAdminAPIEnabled returns a boolean indicating whether the Admin API has
// been enabled.
func ( a * Authority ) IsAdminAPIEnabled ( ) bool {
return a . config . AuthorityConfig . EnableAdmin
}
2019-03-05 08:07:13 +00:00
// Shutdown safely shuts down any clients, databases, etc. held by the Authority.
func ( a * Authority ) Shutdown ( ) error {
2022-04-05 03:19:13 +00:00
if a . crlTicker != nil {
a . crlTicker . Stop ( )
}
2020-05-08 01:59:30 +00:00
if err := a . keyManager . Close ( ) ; err != nil {
log . Printf ( "error closing the key manager: %v" , err )
}
2019-03-05 08:07:13 +00:00
return a . db . Shutdown ( )
}
2021-02-02 01:14:44 +00:00
// CloseForReload closes internal services, to allow a safe reload.
func ( a * Authority ) CloseForReload ( ) {
2022-04-05 03:19:13 +00:00
if a . crlTicker != nil {
a . crlTicker . Stop ( )
}
2021-02-02 01:14:44 +00:00
if err := a . keyManager . Close ( ) ; err != nil {
log . Printf ( "error closing the key manager: %v" , err )
}
2021-07-20 02:28:06 +00:00
if client , ok := a . adminDB . ( * linkedCaClient ) ; ok {
client . Stop ( )
}
2021-02-02 01:14:44 +00:00
}
2021-02-25 23:32:21 +00:00
2021-11-26 16:27:42 +00:00
// IsRevoked returns whether or not a certificate has been
// revoked before.
func ( a * Authority ) IsRevoked ( sn string ) ( bool , error ) {
// Check the passive revocation table.
if lca , ok := a . adminDB . ( interface {
IsRevoked ( string ) ( bool , error )
} ) ; ok {
return lca . IsRevoked ( sn )
}
return a . db . IsRevoked ( sn )
}
2021-03-12 15:58:52 +00:00
// requiresDecrypter returns whether the Authority
// requires a KMS that provides a crypto.Decrypter
2021-03-26 14:44:45 +00:00
// Currently this is only required when SCEP is
// enabled.
2021-03-12 13:18:36 +00:00
func ( a * Authority ) requiresDecrypter ( ) bool {
2021-03-12 15:58:52 +00:00
return a . requiresSCEPService ( )
}
// requiresSCEPService iterates over the configured provisioners
// and determines if one of them is a SCEP provisioner.
func ( a * Authority ) requiresSCEPService ( ) bool {
2021-03-12 13:18:36 +00:00
for _ , p := range a . config . AuthorityConfig . Provisioners {
if p . GetType ( ) == provisioner . TypeSCEP {
return true
}
}
return false
}
2021-02-25 23:32:21 +00:00
// GetSCEPService returns the configured SCEP Service
// TODO: this function is intended to exist temporarily
// in order to make SCEP work more easily. It can be
// made more correct by using the right interfaces/abstractions
// after it works as expected.
2021-03-12 15:58:52 +00:00
func ( a * Authority ) GetSCEPService ( ) * scep . Service {
return a . scepService
2021-02-25 23:32:21 +00:00
}
2021-11-04 06:05:07 +00:00
func ( a * Authority ) startCRLGenerator ( ) error {
2022-03-29 00:51:39 +00:00
if a . config . CRL . CacheDuration . Duration <= 0 {
return nil
}
2021-11-04 06:05:07 +00:00
2022-03-29 00:51:39 +00:00
// Check that there is a valid CRL in the DB right now. If it doesn't exist
// or is expired, generate one now
_ , ok := a . db . ( db . CertificateRevocationListDB )
if ! ok {
return errors . Errorf ( "CRL Generation requested, but database does not support CRL generation" )
}
2021-11-04 06:05:07 +00:00
2022-03-29 00:51:39 +00:00
// Always create a new CRL on startup in case the CA has been down and the time to next expected CRL
// update is less than the cache duration.
err := a . GenerateCertificateRevocationList ( )
if err != nil {
return errors . Wrap ( err , "could not generate a CRL" )
}
2021-11-04 06:05:07 +00:00
2022-03-29 00:51:39 +00:00
log . Printf ( "CRL will be auto-generated every %v" , a . config . CRL . CacheDuration )
tickerDuration := a . config . CRL . CacheDuration . Duration - time . Minute // generate the new CRL 1 minute before it expires
if tickerDuration <= 0 {
panic ( fmt . Sprintf ( "ERROR: Addition of jitter to CRL generation time %v creates a negative duration (%v). Use a CRL generation time of longer than 1 minute." , a . config . CRL . CacheDuration , tickerDuration ) )
}
2022-04-05 03:19:13 +00:00
a . crlTicker = time . NewTicker ( tickerDuration )
2021-11-04 06:05:07 +00:00
2022-03-29 00:51:39 +00:00
go func ( ) {
for {
2022-09-08 02:09:37 +00:00
<- a . crlTicker . C
log . Println ( "Regenerating CRL" )
err := a . GenerateCertificateRevocationList ( )
if err != nil {
log . Printf ( "ERROR: authority.crlGenerator encountered an error when regenerating the CRL: %v" , err )
2021-11-04 06:05:07 +00:00
}
2022-09-08 02:09:37 +00:00
2022-03-29 00:51:39 +00:00
}
} ( )
2021-11-04 06:05:07 +00:00
return nil
}