package provisioner import ( "context" "crypto/x509" "net" "time" "github.com/pkg/errors" "github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/policy" ) // ACME is the acme provisioner type, an entity that can authorize the ACME // provisioning flow. type ACME struct { *base ID string `json:"-"` Type string `json:"type"` Name string `json:"name"` ForceCN bool `json:"forceCN,omitempty"` // RequireEAB makes the provisioner require ACME EAB to be provided // by clients when creating a new Account. If set to true, the provided // EAB will be verified. If set to false and an EAB is provided, it is // not verified. Defaults to false. RequireEAB bool `json:"requireEAB,omitempty"` Claims *Claims `json:"claims,omitempty"` Options *Options `json:"options,omitempty"` claimer *Claimer x509Policy policy.X509NamePolicyEngine } // GetID returns the provisioner unique identifier. func (p ACME) GetID() string { if p.ID != "" { return p.ID } return p.GetIDForToken() } // GetIDForToken returns an identifier that will be used to load the provisioner // from a token. func (p *ACME) GetIDForToken() string { return "acme/" + p.Name } // GetTokenID returns the identifier of the token. func (p *ACME) GetTokenID(ott string) (string, error) { return "", errors.New("acme provisioner does not implement GetTokenID") } // GetName returns the name of the provisioner. func (p *ACME) GetName() string { return p.Name } // GetType returns the type of provisioner. func (p *ACME) GetType() Type { return TypeACME } // GetEncryptedKey returns the base provisioner encrypted key if it's defined. func (p *ACME) GetEncryptedKey() (string, string, bool) { return "", "", false } // GetOptions returns the configured provisioner options. func (p *ACME) GetOptions() *Options { return p.Options } // DefaultTLSCertDuration returns the default TLS cert duration enforced by // the provisioner. func (p *ACME) DefaultTLSCertDuration() time.Duration { return p.claimer.DefaultTLSCertDuration() } // Init initializes and validates the fields of an ACME type. func (p *ACME) Init(config Config) (err error) { switch { case p.Type == "": return errors.New("provisioner type cannot be empty") case p.Name == "": return errors.New("provisioner name cannot be empty") } // Update claims with global ones if p.claimer, err = NewClaimer(p.Claims, config.Claims); err != nil { return err } // Initialize the x509 allow/deny policy engine // TODO(hs): ensure no race conditions happen when reloading settings and requesting certs? // TODO(hs): implement memoization strategy, so that reloading is not required when no changes were made to allow/deny? if p.x509Policy, err = newX509PolicyEngine(p.Options.GetX509Options()); err != nil { return err } return nil } // ACMEIdentifierType encodes ACME Identifier types type ACMEIdentifierType string const ( // IP is the ACME ip identifier type IP ACMEIdentifierType = "ip" // DNS is the ACME dns identifier type DNS ACMEIdentifierType = "dns" ) // ACMEIdentifier encodes ACME Order Identifiers type ACMEIdentifier struct { Type ACMEIdentifierType Value string } // AuthorizeOrderIdentifiers verifies the provisioner is authorized to issue a // certificate for the Identifiers provided in an Order. func (p *ACME) AuthorizeOrderIdentifier(ctx context.Context, identifier string) error { if p.x509Policy == nil { return nil } // assuming only valid identifiers (IP or DNS) are provided var err error if ip := net.ParseIP(identifier); ip != nil { _, err = p.x509Policy.IsIPAllowed(ip) } else { _, err = p.x509Policy.IsDNSAllowed(identifier) } return err } // AuthorizeSign does not do any validation, because all validation is handled // in the ACME protocol. This method returns a list of modifiers / constraints // on the resulting certificate. func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) { opts := []SignOption{ // modifiers / withOptions newProvisionerExtensionOption(TypeACME, p.Name, ""), newForceCNOption(p.ForceCN), profileDefaultDuration(p.claimer.DefaultTLSCertDuration()), // validators defaultPublicKeyValidator{}, newValidityValidator(p.claimer.MinTLSCertDuration(), p.claimer.MaxTLSCertDuration()), newX509NamePolicyValidator(p.x509Policy), } return opts, nil } // AuthorizeRevoke is called just before the certificate is to be revoked by // the CA. It can be used to authorize revocation of a certificate. It // currently is a no-op. // TODO(hs): add configuration option that toggles revocation? Or change function signature to make it more useful? // Or move certain logic out of the Revoke API to here? Would likely involve some more stuff in the ctx. func (p *ACME) AuthorizeRevoke(ctx context.Context, token string) error { return nil } // AuthorizeRenew returns an error if the renewal is disabled. // NOTE: This method does not actually validate the certificate or check it's // revocation status. Just confirms that the provisioner that created the // certificate was configured to allow renewals. func (p *ACME) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error { if p.claimer.IsDisableRenewal() { return errs.Unauthorized("acme.AuthorizeRenew; renew is disabled for acme provisioner '%s'", p.GetName()) } return nil }