7731edd816
* Store and verify account location on acme requests Co-authored-by: Herman Slatman <hslatman@users.noreply.github.com> Co-authored-by: Mariano Cano <mariano@smallstep.com>
134 lines
3.8 KiB
Go
134 lines
3.8 KiB
Go
package acme
|
|
|
|
import (
|
|
"crypto"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"go.step.sm/crypto/jose"
|
|
|
|
"github.com/smallstep/certificates/authority/policy"
|
|
)
|
|
|
|
// Account is a subset of the internal account type containing only those
|
|
// attributes required for responses in the ACME protocol.
|
|
type Account struct {
|
|
ID string `json:"-"`
|
|
Key *jose.JSONWebKey `json:"-"`
|
|
Contact []string `json:"contact,omitempty"`
|
|
Status Status `json:"status"`
|
|
OrdersURL string `json:"orders"`
|
|
ExternalAccountBinding interface{} `json:"externalAccountBinding,omitempty"`
|
|
LocationPrefix string `json:"-"`
|
|
ProvisionerName string `json:"-"`
|
|
}
|
|
|
|
// GetLocation returns the URL location of the given account.
|
|
func (a *Account) GetLocation() string {
|
|
if a.LocationPrefix == "" {
|
|
return ""
|
|
}
|
|
return a.LocationPrefix + a.ID
|
|
}
|
|
|
|
// ToLog enables response logging.
|
|
func (a *Account) ToLog() (interface{}, error) {
|
|
b, err := json.Marshal(a)
|
|
if err != nil {
|
|
return nil, WrapErrorISE(err, "error marshaling account for logging")
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
// IsValid returns true if the Account is valid.
|
|
func (a *Account) IsValid() bool {
|
|
return a.Status == StatusValid
|
|
}
|
|
|
|
// KeyToID converts a JWK to a thumbprint.
|
|
func KeyToID(jwk *jose.JSONWebKey) (string, error) {
|
|
kid, err := jwk.Thumbprint(crypto.SHA256)
|
|
if err != nil {
|
|
return "", WrapErrorISE(err, "error generating jwk thumbprint")
|
|
}
|
|
return base64.RawURLEncoding.EncodeToString(kid), nil
|
|
}
|
|
|
|
// PolicyNames contains ACME account level policy names
|
|
type PolicyNames struct {
|
|
DNSNames []string `json:"dns"`
|
|
IPRanges []string `json:"ips"`
|
|
}
|
|
|
|
// X509Policy contains ACME account level X.509 policy
|
|
type X509Policy struct {
|
|
Allowed PolicyNames `json:"allow"`
|
|
Denied PolicyNames `json:"deny"`
|
|
AllowWildcardNames bool `json:"allowWildcardNames"`
|
|
}
|
|
|
|
// Policy is an ACME Account level policy
|
|
type Policy struct {
|
|
X509 X509Policy `json:"x509"`
|
|
}
|
|
|
|
func (p *Policy) GetAllowedNameOptions() *policy.X509NameOptions {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return &policy.X509NameOptions{
|
|
DNSDomains: p.X509.Allowed.DNSNames,
|
|
IPRanges: p.X509.Allowed.IPRanges,
|
|
}
|
|
}
|
|
|
|
func (p *Policy) GetDeniedNameOptions() *policy.X509NameOptions {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return &policy.X509NameOptions{
|
|
DNSDomains: p.X509.Denied.DNSNames,
|
|
IPRanges: p.X509.Denied.IPRanges,
|
|
}
|
|
}
|
|
|
|
// AreWildcardNamesAllowed returns if wildcard names
|
|
// like *.example.com are allowed to be signed.
|
|
// Defaults to false.
|
|
func (p *Policy) AreWildcardNamesAllowed() bool {
|
|
if p == nil {
|
|
return false
|
|
}
|
|
return p.X509.AllowWildcardNames
|
|
}
|
|
|
|
// ExternalAccountKey is an ACME External Account Binding key.
|
|
type ExternalAccountKey struct {
|
|
ID string `json:"id"`
|
|
ProvisionerID string `json:"provisionerID"`
|
|
Reference string `json:"reference"`
|
|
AccountID string `json:"-"`
|
|
HmacKey []byte `json:"-"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
BoundAt time.Time `json:"boundAt,omitempty"`
|
|
Policy *Policy `json:"policy,omitempty"`
|
|
}
|
|
|
|
// AlreadyBound returns whether this EAK is already bound to
|
|
// an ACME Account or not.
|
|
func (eak *ExternalAccountKey) AlreadyBound() bool {
|
|
return !eak.BoundAt.IsZero()
|
|
}
|
|
|
|
// BindTo binds the EAK to an Account.
|
|
// It returns an error if it's already bound.
|
|
func (eak *ExternalAccountKey) BindTo(account *Account) error {
|
|
if eak.AlreadyBound() {
|
|
return NewError(ErrorUnauthorizedType, "external account binding key with id '%s' was already bound to account '%s' on %s", eak.ID, eak.AccountID, eak.BoundAt)
|
|
}
|
|
eak.AccountID = account.ID
|
|
eak.BoundAt = time.Now()
|
|
eak.HmacKey = []byte{} // clearing the key bytes; can only be used once
|
|
return nil
|
|
}
|