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"
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"
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"
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"
2020-01-10 02:42:26 +00:00
"github.com/smallstep/certificates/kms"
kmsapi "github.com/smallstep/certificates/kms/apiv1"
2020-11-03 13:26:46 +00:00
"github.com/smallstep/certificates/kms/sshagentkms"
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"
2020-08-14 22:33:50 +00:00
"go.step.sm/crypto/pemutil"
2021-08-11 00:30:33 +00:00
"go.step.sm/linkedca"
2019-09-25 02:12:13 +00:00
"golang.org/x/crypto/ssh"
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
crlChannel chan int
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
adminMutex sync . RWMutex
2018-10-05 21:48:36 +00:00
}
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
// Initialize authority from options or configuration.
2018-10-05 21:48:36 +00:00
if err := a . init ( ) ; err != nil {
return nil , err
}
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
2020-05-05 01:52:18 +00:00
// Initialize authority from options or configuration.
if err := a . init ( ) ; err != nil {
return nil , err
}
return a , nil
}
2021-05-03 19:48:20 +00:00
// reloadAdminResources reloads admins and provisioners from the DB.
func ( a * Authority ) reloadAdminResources ( ctx context . Context ) error {
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
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
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
}
a . keyManager , err = kms . New ( context . Background ( ) , options )
2020-01-10 02:42:26 +00:00
if err != nil {
return err
}
}
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
}
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
}
}
a . x509CAService , err = cas . New ( context . Background ( ) , options )
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 :
a . sshCAHostCertSignKey = s . Sshsigner
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 :
a . sshCAUserCertSignKey = s . Sshsigner
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
}
}
a . scepService , err = scep . NewService ( context . Background ( ) , options )
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 {
2021-07-20 02:28:06 +00:00
if a . linkedCAToken == "" {
2021-07-12 16:11:00 +00:00
// Check if AuthConfig already exists
a . adminDB , err = adminDBNosql . New ( a . db . ( nosql . DB ) , admin . DefaultAuthorityID )
if err != nil {
return err
}
} else {
2021-07-20 02:28:06 +00:00
// Use the linkedca client as the admindb.
client , err := newLinkedCAClient ( a . linkedCAToken )
2021-07-12 16:11:00 +00:00
if err != nil {
return err
}
2021-07-20 02:28:06 +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 , client . authorityID ) {
return errors . New ( "error initializing linkedca: token authority and configured authority do not match" )
}
client . Run ( )
a . adminDB = client
2021-05-03 19:48:20 +00:00
}
2019-10-28 18:50:43 +00:00
}
2021-05-03 19:48:20 +00:00
provs , err := a . adminDB . GetProvisioners ( context . Background ( ) )
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
2021-09-16 18:55:41 +00:00
prov , err := CreateFirstProvisioner ( context . Background ( ) , 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
if err := a . adminDB . CreateAdmin ( context . Background ( ) , & linkedca . Admin {
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
if err := a . reloadAdminResources ( context . Background ( ) ) ; err != nil {
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
if a . config . CRL != nil {
if a . config . CRL . Generate && a . config . CRL . CacheDuration . Duration > time . Duration ( 0 ) {
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
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-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 {
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 ( ) {
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 {
if a . config . CRL . CacheDuration . Duration > time . Duration ( 0 ) {
// Check that there is a valid CRL in the DB right now. If it doesnt exist
// or is expired, generated one now
crlDB , ok := a . db . ( db . CertificateRevocationListDB )
if ! ok {
return errors . Errorf ( "CRL Generation requested, but database does not support CRL generation" )
}
crlInfo , err := crlDB . GetCRL ( )
if err != nil {
return errors . Wrap ( err , "could not retrieve CRL from database" )
}
if crlInfo == nil {
log . Println ( "No CRL exists in the DB, generating one now" )
err = a . GenerateCertificateRevocationList ( )
if err != nil {
return errors . Wrap ( err , "could not generate a CRL" )
}
}
if crlInfo . ExpiresAt . Before ( time . Now ( ) . UTC ( ) ) {
log . Printf ( "Existing CRL has expired (at %v), generating a new one" , crlInfo . ExpiresAt )
err = a . GenerateCertificateRevocationList ( )
if err != nil {
return errors . Wrap ( err , "could not generate a CRL" )
}
}
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 {
log . Printf ( "WARNING: Addition of jitter to CRL generation time %v creates a negative duration (%v). Using 1 minute cacheDuration" , a . config . CRL . CacheDuration , tickerDuration )
tickerDuration = time . Minute
}
crlTicker := time . NewTicker ( tickerDuration )
go func ( ) {
for {
select {
case <- crlTicker . C :
log . Println ( "Regenerating CRL" )
err := a . GenerateCertificateRevocationList ( )
if err != nil {
// TODO: log or panic here?
panic ( errors . Wrap ( err , "authority.crlGenerator encountered an error" ) )
}
}
}
} ( )
}
return nil
}