Documentation
¶
Overview ¶
Package acmeserver provides the ACME protocol layer for embedding an ACME server into CA and PKI applications. Certificate issuance, persistence and policy decisions are supplied by the host application through interfaces, so the package depends on no particular CA, database or deployment.
Embedding ¶
New validates a Config and returns a Server that implements http.Handler. Mount it at the path of Config.BaseURL behind the HTTPS origin that URL names, because signed request URLs must match it exactly. Run processes the persisted validation and issuance work and must run in this or another process on the same Store, otherwise challenges are never validated and orders are never issued. Ready reports work that has waited too long for a worker. The package example shows a complete host.
Host interfaces ¶
A Store persists accounts, orders, authorizations, challenges, certificates and background tasks. Its operations are atomic and guarded by resource revisions and task fences, which is what lets several workers share one store. The memstore package is the in-memory implementation for tests and examples and the storetest package checks a host adapter against the contract.
An Issuer signs certificates and a Revoker revokes them. Both receive a durable OperationID that is stored before the first call and repeated on every retry, so the host CA deduplicates. An Issuer must enforce IssueRequest.Deadline and RecoveryOnly. A chain that fails publication checks is kept in Order.UnpublishedResult for host reconciliation.
A Validator checks one challenge type. The challenge package implements HTTP-01, DNS-01, TLS-ALPN-01 and tkauth-01. A GrantingValidator reports what a proof authorizes beyond control of the identifier, which tkauth-01 uses for CA certificates and validity bounds.
Policy reviews new accounts and orders, IssuancePolicy reviews the first issuance dispatch and ExternalAccountKeys verifies external account bindings.
Extensions ¶
RFC 8738 IP identifiers, RFC 9448 TNAuthList identifiers with the RFC 9447 tkauth-01 challenge and RFC 9773 renewal information are off until the matching Config field enables them.
Documentation ¶
Step-by-step guides for hosts are at https://misiektoja.github.io/go-acme-server/.
Example ¶
Shows how a host mounts the handler and runs the worker. Both are required.
package main
import (
"context"
"errors"
"log"
"net/http"
"net/netip"
"os"
"os/signal"
acmeserver "github.com/misiektoja/go-acme-server"
"github.com/misiektoja/go-acme-server/challenge"
"github.com/misiektoja/go-acme-server/memstore"
"github.com/misiektoja/go-acme-server/nonce"
)
// A placeholder for the host CA. Real hosts sign with their own CA and deduplicate by OperationID.
type exampleCA struct{}
// Refuses every request in this example.
func (exampleCA) Issue(context.Context, acmeserver.IssueRequest) (acmeserver.IssueResult, error) {
return acmeserver.IssueResult{Rejected: acmeserver.NewProblem(acmeserver.ErrorServerInternal, "not implemented")}, nil
}
// Refuses every request in this example.
func (exampleCA) Revoke(context.Context, acmeserver.RevokeRequest) error {
return errors.New("not implemented")
}
// Shows how a host mounts the handler and runs the worker. Both are required.
func main() {
resolver, err := challenge.NewResolver(challenge.ResolverOptions{
Servers: []netip.AddrPort{netip.MustParseAddrPort("192.0.2.53:53")},
})
if err != nil {
log.Fatal(err)
}
http01, err := challenge.NewHTTP01(challenge.HTTPOptions{
Network: challenge.NetworkOptions{Resolver: resolver},
})
if err != nil {
log.Fatal(err)
}
srv, err := acmeserver.New(acmeserver.Config{
BaseURL: "https://ca.example.com/acme/",
Store: memstore.New(),
Nonces: nonce.New(nonce.Options{}),
Issuer: exampleCA{},
Revoker: exampleCA{},
Validators: map[acmeserver.ChallengeType]acmeserver.Validator{
acmeserver.ChallengeHTTP01: http01,
},
Meta: acmeserver.DirectoryMeta{Website: "https://ca.example.com"},
})
if err != nil {
log.Fatal(err)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
go func() {
if err := srv.Run(ctx); err != nil {
log.Print(err)
}
}()
mux := http.NewServeMux()
mux.Handle("/acme/", srv)
// http.ListenAndServeTLS(":443", "cert.pem", "key.pem", mux) would serve the directory at
// https://ca.example.com/acme/directory.
_ = mux
}
Output:
Index ¶
- Constants
- Variables
- type Account
- type AccountStatus
- type AccountStore
- type AllowAll
- type Authorization
- type AuthorizationStatus
- type Certificate
- type CertificateStore
- type Challenge
- type ChallengeStatus
- type ChallengeType
- type Clock
- type ClockFunc
- type Config
- type DirectoryMeta
- type ErrorType
- type ExternalAccountKeys
- type GrantingValidator
- type Identifier
- type IdentifierType
- type IssuancePolicy
- type IssuanceState
- type IssueRequest
- type IssueResult
- type Issuer
- type LifetimeRenewal
- type NonceManager
- type Order
- type OrderStatus
- type OrderStore
- type Policy
- type Problem
- func (p *Problem) Error() string
- func (p *Problem) HTTPStatus() int
- func (p *Problem) MarshalJSON() ([]byte, error)
- func (p *Problem) UnmarshalJSON(data []byte) error
- func (p *Problem) WithIdentifier(id Identifier) *Problem
- func (p *Problem) WithRetryAfter(d time.Duration) *Problem
- func (p *Problem) WithStatus(status int) *Problem
- type RenewalAdvisor
- type RenewalInfo
- type RevokeRequest
- type Revoker
- type Server
- type Store
- type Task
- type TaskKind
- type Validation
- type ValidationGrant
- type ValidationRequest
- type Validator
- type WorkStore
- type WorkerConfig
Examples ¶
Constants ¶
const ( DefaultMaxRequestBody = 64 << 10 DefaultOrderLifetime = 7 * 24 * time.Hour DefaultAuthorizationLifetime = 30 * 24 * time.Hour DefaultMaxIdentifiers = 100 DefaultDetachedWriteTimeout = 30 * time.Second )
Defaults used when Config leaves a field zero.
const DefaultRenewalRetryAfter = 6 * time.Hour
The Retry-After of renewalInfo responses when the advisor sets none.
const ProblemNamespace = "urn:ietf:params:acme:error:"
Prefixes every ACME error type in a problem document.
const TKAuthTypeATC = "atc"
The only Authority Token subtype this server offers, see RFC 9447 section 4.
Variables ¶
var ( ErrNotFound = errors.New("acmeserver: resource not found") ErrConflict = errors.New("acmeserver: resource already exists") ErrRevisionMismatch = errors.New("acmeserver: resource revision mismatch") // Returned by CreateOrder when order.Replaces names a certificate that another order, which // is not invalid at order.CreatedAt, already replaced. ErrAlreadyReplaced = errors.New("acmeserver: certificate already replaced") )
Errors a Store returns for contract violations. Backend failures must use other errors.
Functions ¶
This section is empty.
Types ¶
type Account ¶
type Account struct {
ID string
Status AccountStatus
// The account key. KeyThumbprint is its RFC 7638 thumbprint, unique among accounts.
Key crypto.PublicKey
KeyThumbprint string
Contact []string
TermsOfServiceAgreed bool
// The identity the host derived from a verified external account binding.
ExternalAccountID string
// The single-use claim on that binding, set when Config.SingleUseExternalAccounts is on.
// Stores keep non-empty claims unique among accounts.
ExternalAccountClaim string
CreatedAt time.Time
// Increases on every successful update and guards concurrent modification.
Revision uint64
}
A stored ACME account. Its ID is opaque and appears only in the account URL.
type AccountStatus ¶
type AccountStatus string
The status of an account resource, see RFC 8555 section 7.1.2.
const ( AccountValid AccountStatus = "valid" AccountDeactivated AccountStatus = "deactivated" AccountRevoked AccountStatus = "revoked" )
Account statuses.
func (AccountStatus) CanTransition ¶
func (s AccountStatus) CanTransition(next AccountStatus) bool
Reports whether RFC 8555 section 7.1.6 allows the status to move to next.
func (AccountStatus) Known ¶
func (s AccountStatus) Known() bool
Reports whether the status is one the protocol defines.
type AccountStore ¶
type AccountStore interface {
// Stores a new account with revision 1. It returns ErrConflict when the ID, the key
// thumbprint or a non-empty ExternalAccountClaim is already in use.
CreateAccount(ctx context.Context, account *Account) error
// Returns the account with the given ID or ErrNotFound.
Account(ctx context.Context, id string) (*Account, error)
// Returns the account whose key has the given thumbprint or ErrNotFound.
AccountByKey(ctx context.Context, thumbprint string) (*Account, error)
// Replaces the stored account when the revisions match and increments
// account.Revision. It returns ErrRevisionMismatch, ErrNotFound or ErrConflict otherwise.
UpdateAccount(ctx context.Context, account *Account) error
}
Persists accounts. Every method is atomic and returns copies the caller owns.
type AllowAll ¶
type AllowAll struct{}
A Policy that accepts every request.
func (AllowAll) NewAccount ¶
Accepts the account.
type Authorization ¶
type Authorization struct {
ID string
AccountID string
OrderID string
Identifier Identifier
Status AuthorizationStatus
Expires time.Time
// Marks a wildcard authorization. Identifier then holds the base name.
Wildcard bool
ChallengeIDs []string
// Records that the successful challenge also authorized a CA certificate for the identifier.
CACertificate bool
// The time after which the proof no longer covers a certificate, zero when unbounded.
GrantExpires time.Time
CreatedAt time.Time
Revision uint64
}
A stored authorization resource. Each authorization belongs to exactly one order.
type AuthorizationStatus ¶
type AuthorizationStatus string
The status of an authorization resource, see RFC 8555 section 7.1.4.
const ( AuthorizationPending AuthorizationStatus = "pending" AuthorizationValid AuthorizationStatus = "valid" AuthorizationInvalid AuthorizationStatus = "invalid" AuthorizationDeactivated AuthorizationStatus = "deactivated" AuthorizationExpired AuthorizationStatus = "expired" AuthorizationRevoked AuthorizationStatus = "revoked" )
Authorization statuses.
func (AuthorizationStatus) CanTransition ¶
func (s AuthorizationStatus) CanTransition(next AuthorizationStatus) bool
Reports whether RFC 8555 section 7.1.6 allows the status to move to next.
func (AuthorizationStatus) Known ¶
func (s AuthorizationStatus) Known() bool
Reports whether the status is one the protocol defines.
func (AuthorizationStatus) Terminal ¶
func (s AuthorizationStatus) Terminal() bool
Reports whether no further transition is possible.
type Certificate ¶
type Certificate struct {
// The base64url SHA-256 digest of the DER leaf, which names the certificate resource.
ID string
AccountID string
OrderID string
// The opaque reference returned by the host CA.
CAReference string
// Holds the DER leaf certificate followed by the DER issuer chain the client receives.
Chain [][]byte
NotBefore time.Time
NotAfter time.Time
Revoked bool
RevokedAt time.Time
// The CRL reason code recorded at revocation.
RevocationReason int
// Identifies the revocation the host CA receives. It is committed with the reason before the
// first CA call and reused by every retry, so the CA can deduplicate.
RevocationOperationID string
// When the revocation was recorded, before the CA call. RevokedAt is set after the CA succeeded.
RevocationRequestedAt time.Time
// The RFC 9773 identifier built from the leaf's authority key identifier and serial number.
// It is empty when the leaf has no authority key identifier. Stores keep non-empty values
// unique among certificates.
RenewalID string
// The order that claimed this certificate as its predecessor through replaces.
ReplacedByOrderID string
// The validation evidence the issuer received, kept for host audit needs.
Validations []Validation
CreatedAt time.Time
Revision uint64
}
A stored issued certificate together with its chain.
type CertificateStore ¶
type CertificateStore interface {
// Returns the certificate with the given ID or ErrNotFound.
Certificate(ctx context.Context, id string) (*Certificate, error)
// Returns the certificate with the given non-empty RenewalID or ErrNotFound.
CertificateByRenewalID(ctx context.Context, renewalID string) (*Certificate, error)
// Replaces the stored certificate when the revisions match and increments cert.Revision.
UpdateCertificate(ctx context.Context, cert *Certificate) error
}
Persists issued certificates.
type Challenge ¶
type Challenge struct {
ID string
AuthorizationID string
AccountID string
Type ChallengeType
Status ChallengeStatus
Token string
// The Authority Token a tkauth-01 response carried, see RFC 9448 section 4.
AuthorityToken string
// The account key thumbprint captured at response time, so a key rollover
// does not change an in-flight validation.
KeyThumbprint string
Validated time.Time
// Records why validation failed.
Error *Problem
Revision uint64
}
A stored challenge resource.
type ChallengeStatus ¶
type ChallengeStatus string
The status of a challenge resource, see RFC 8555 section 7.1.5.
const ( ChallengePending ChallengeStatus = "pending" ChallengeProcessing ChallengeStatus = "processing" ChallengeValid ChallengeStatus = "valid" ChallengeInvalid ChallengeStatus = "invalid" )
Challenge statuses.
func (ChallengeStatus) CanTransition ¶
func (s ChallengeStatus) CanTransition(next ChallengeStatus) bool
Reports whether the status may move to next, including pending to invalid per erratum 5732.
func (ChallengeStatus) Known ¶
func (s ChallengeStatus) Known() bool
Reports whether the status is one the protocol defines.
func (ChallengeStatus) Terminal ¶
func (s ChallengeStatus) Terminal() bool
Reports whether no further transition is possible.
type ChallengeType ¶
type ChallengeType string
Names a challenge mechanism registered with IANA.
const ( ChallengeHTTP01 ChallengeType = "http-01" ChallengeDNS01 ChallengeType = "dns-01" ChallengeTLSALPN01 ChallengeType = "tls-alpn-01" // Answered with an Authority Token instead of a network proof. ChallengeTKAuth01 ChallengeType = "tkauth-01" )
Challenge types from RFC 8555 section 8, RFC 8737 and RFC 9447.
type Config ¶
type Config struct {
// The absolute URL the handler is served under, including any path prefix.
// It must use https unless AllowInsecureBaseURL is set.
BaseURL string
// Accepts an http BaseURL for tests and local examples.
AllowInsecureBaseURL bool
// Persists resources and background work.
Store Store
// Issues and consumes the single-use request nonces.
Nonces NonceManager
// Defaults to the system clock.
Clock Clock
// Receives operational events. It defaults to a logger that discards everything.
Logger *slog.Logger
// Optional directory metadata. Nothing is advertised when it is empty.
Meta DirectoryMeta
// Bounds the size of a request body in bytes. Zero selects DefaultMaxRequestBody.
MaxRequestBody int64
// Signs certificates for finalized orders.
Issuer Issuer
// Revokes issued certificates.
Revoker Revoker
// The challenge types offered to clients. Only configured types appear in authorizations.
Validators map[ChallengeType]Validator
// Verifies external account bindings. Required when Meta.ExternalAccountRequired is set.
ExternalAccounts ExternalAccountKeys
// Binds each external account key identifier to at most one account. The claim is committed
// with the account, and a later newAccount with the same identifier and another key is refused.
SingleUseExternalAccounts bool
// Reviews new accounts and orders. Defaults to AllowAll.
Policy Policy
// Reviews current issuance policy before dispatch, with the accepted CSR and validation evidence.
IssuancePolicy IssuancePolicy
// Refuses new accounts that do not agree to Meta.TermsOfService.
RequireTermsOfServiceAgreed bool
// Accepts IP identifiers as specified in RFC 8738.
IPIdentifiers bool
// Accepts TNAuthList identifiers as specified in RFC 9448. It needs a tkauth-01 validator.
TNAuthListIdentifiers bool
// The https URL offered as token-authority on tkauth-01 challenges, see RFC 9447 section 3.
// It is optional, and clients fall back to their own configuration when it is empty.
TokenAuthority string
// How long a new order and its pending authorizations stay valid.
OrderLifetime time.Duration
// How long a validated authorization stays valid.
AuthorizationLifetime time.Duration
// Bounds the identifiers of one order.
MaxIdentifiers int
// Bounds a store write that has to finish after the client request it belongs to has ended,
// such as recording a revocation the Revoker already carried out. Zero selects
// DefaultDetachedWriteTimeout.
DetachedWriteTimeout time.Duration
// Serves RFC 9773 renewal information and accepts replaces on new orders when set.
// LifetimeRenewal is the built-in advisor. Nil leaves the extension off.
RenewalInfo RenewalAdvisor
// Tunes Run and Ready.
Workers WorkerConfig
}
Configures a Server. Store, Nonces, Issuer, Revoker and at least one validator are required.
type DirectoryMeta ¶
type DirectoryMeta struct {
// The URL of the current terms of service.
TermsOfService string
// The URL of the CA's website.
Website string
// The hostnames the CA recognizes in CAA issue and issuewild records.
CAAIdentities []string
// Tells clients that newAccount needs an external account binding.
ExternalAccountRequired bool
}
The optional metadata object of the directory, see RFC 8555 section 7.1.1.
type ErrorType ¶
type ErrorType string
Names an ACME error without its URN namespace.
const ( ErrorAccountDoesNotExist ErrorType = "accountDoesNotExist" ErrorAlreadyReplaced ErrorType = "alreadyReplaced" ErrorAlreadyRevoked ErrorType = "alreadyRevoked" ErrorBadCSR ErrorType = "badCSR" ErrorBadNonce ErrorType = "badNonce" ErrorBadPublicKey ErrorType = "badPublicKey" ErrorBadRevocationReason ErrorType = "badRevocationReason" ErrorBadSignatureAlgorithm ErrorType = "badSignatureAlgorithm" ErrorCAA ErrorType = "caa" ErrorCompound ErrorType = "compound" ErrorConnection ErrorType = "connection" ErrorDNS ErrorType = "dns" ErrorDNSSEC ErrorType = "dnssec" ErrorExternalAccountRequired ErrorType = "externalAccountRequired" ErrorIncorrectResponse ErrorType = "incorrectResponse" ErrorInvalidContact ErrorType = "invalidContact" ErrorMalformed ErrorType = "malformed" ErrorOrderNotReady ErrorType = "orderNotReady" ErrorRateLimited ErrorType = "rateLimited" ErrorRejectedIdentifier ErrorType = "rejectedIdentifier" ErrorServerInternal ErrorType = "serverInternal" ErrorTLS ErrorType = "tls" ErrorUnsupportedContact ErrorType = "unsupportedContact" ErrorUnsupportedIdentifier ErrorType = "unsupportedIdentifier" ErrorUserActionRequired ErrorType = "userActionRequired" )
Error types registered by RFC 8555 section 6.7 and RFC 9773 section 5.
type ExternalAccountKeys ¶
type ExternalAccountKeys interface {
// Returns the MAC key for the key identifier or ErrNotFound.
MACKey(ctx context.Context, keyID string) ([]byte, error)
}
Supplies the MAC keys that verify external account bindings, see RFC 8555 section 7.3.4.
type GrantingValidator ¶
type GrantingValidator interface {
Validator
ValidateGrant(ctx context.Context, req ValidationRequest) (ValidationGrant, error)
}
Reports what a response authorizes in addition to checking it. A validator that only proves control of an identifier implements Validator alone, and the server then grants nothing.
type Identifier ¶
type Identifier struct {
Type IdentifierType `json:"type"`
Value string `json:"value"`
}
An ACME identifier object.
func NormalizeIdentifiers ¶
func NormalizeIdentifiers(ids []Identifier) ([]Identifier, error)
Normalizes every identifier and rejects duplicates in the normalized set.
func (Identifier) IsWildcard ¶
func (id Identifier) IsWildcard() bool
Reports whether the identifier is a DNS name whose leftmost label is a wildcard.
func (Identifier) Normalize ¶
func (id Identifier) Normalize() (Identifier, error)
Validates the identifier syntax and returns its canonical form. Errors are *Problem values with type unsupportedIdentifier, malformed or rejectedIdentifier. Issuance policy is left to the host.
func (Identifier) String ¶
func (id Identifier) String() string
Returns the type and value joined by a colon.
type IdentifierType ¶
type IdentifierType string
Names the kind of subject an order or authorization refers to.
const ( IdentifierDNS IdentifierType = "dns" IdentifierIP IdentifierType = "ip" // A base64url encoded DER TN Authorization List, see RFC 8226 section 9. IdentifierTNAuthList IdentifierType = "TNAuthList" )
Identifier types registered by RFC 8555 section 9.7.7, RFC 8738 and RFC 9448.
type IssuancePolicy ¶
type IssuancePolicy interface {
AuthorizeIssuance(ctx context.Context, req IssueRequest) error
}
Reviews current policy before the first durable issuance dispatch.
type IssuanceState ¶
type IssuanceState struct {
OperationID string
AuthorizedAt time.Time
Deadline time.Time
Validations []Validation
}
Preserves the authorization deadline and evidence across uncertain issuance attempts.
type IssueRequest ¶
type IssueRequest struct {
OperationID string
AccountID string
AccountURL string
OrderID string
// The parsed certificate request and its DER encoding. The signature and the identifier
// set were already checked against the order.
CSR *x509.CertificateRequest
CSRDER []byte
// The normalized identifiers the order covers.
Identifiers []Identifier
// The requested validity. Zero values mean the host decides. An authority token expiry
// narrows NotAfter. The issuer may return a shorter validity, but the leaf must fit inside
// the requested window and must be valid already unless NotBefore is in the future.
NotBefore time.Time
NotAfter time.Time
// How each identifier was validated.
Validations []Validation
// The earliest order or authorization expiry, after which new signing is forbidden.
Deadline time.Time
// Allows only recovery of an existing result, never a new signing operation.
RecoveryOnly bool
}
What the host CA receives when an order is ready for issuance. The same OperationID is presented on every attempt for one order, so the issuer can deduplicate retries.
type IssueResult ¶
type IssueResult struct {
// The DER leaf certificate followed by the DER issuer chain.
Chain [][]byte
// An opaque host CA reference retained with issued or unpublished results.
CAReference string
// Reports that the CA has not decided yet. The worker asks again after RetryAfter.
Pending bool
RetryAfter time.Duration
// A final refusal that becomes the order error.
Rejected *Problem
}
The outcome of an issuance attempt. Exactly one of Chain, Pending and Rejected is set.
type Issuer ¶
type Issuer interface {
Issue(ctx context.Context, req IssueRequest) (IssueResult, error)
}
Issues or recovers one durable result per operation ID, enforcing Deadline and RecoveryOnly for new signing.
type LifetimeRenewal ¶
type LifetimeRenewal struct {
// Fractions of the lifetime at which the window starts and ends. Zero values mean two
// thirds and five sixths.
Start float64
End float64
// Copied into every response.
ExplanationURL string
RetryAfter time.Duration
}
A RenewalAdvisor that places the window at fixed fractions of the certificate lifetime and asks for immediate renewal of revoked certificates.
func (LifetimeRenewal) RenewalInfo ¶
func (l LifetimeRenewal) RenewalInfo(_ context.Context, cert *Certificate) (RenewalInfo, error)
Returns the window for the certificate. A revoked certificate gets a window that opened at its revocation, so clients renew at once.
type NonceManager ¶
type NonceManager interface {
// Returns a fresh nonce in base64url form.
Issue(ctx context.Context) (string, error)
// Invalidates the nonce and reports whether it was valid. A nonce is valid exactly once.
Consume(ctx context.Context, nonce string) (bool, error)
}
Issues and consumes the single-use nonces that protect requests against replay.
type Order ¶
type Order struct {
ID string
AccountID string
Status OrderStatus
Expires time.Time
Identifiers []Identifier
// The requested validity. Zero values mean unset.
NotBefore time.Time
NotAfter time.Time
AuthorizationIDs []string
// Records why the order became invalid.
Error *Problem
// The DER request accepted at finalization. It never changes once set.
CSR []byte
CertificateID string
// The durable authorization decision made before the first CA call.
Issuance *IssuanceState
// A CA result withheld from clients and retained for host reconciliation.
UnpublishedResult *IssueResult
// The RFC 9773 identifier of the certificate this order replaces, set when the client sent
// replaces and Config.RenewalInfo is enabled. Stores mark that certificate replaced by this
// order when the order is created.
Replaces string
CreatedAt time.Time
Revision uint64
}
A stored order resource.
type OrderStatus ¶
type OrderStatus string
The status of an order resource, see RFC 8555 section 7.1.3.
const ( OrderPending OrderStatus = "pending" OrderReady OrderStatus = "ready" OrderProcessing OrderStatus = "processing" OrderValid OrderStatus = "valid" OrderInvalid OrderStatus = "invalid" )
Order statuses.
func (OrderStatus) CanTransition ¶
func (s OrderStatus) CanTransition(next OrderStatus) bool
Reports whether RFC 8555 section 7.1.6 allows the status to move to next.
func (OrderStatus) Known ¶
func (s OrderStatus) Known() bool
Reports whether the status is one the protocol defines.
func (OrderStatus) Terminal ¶
func (s OrderStatus) Terminal() bool
Reports whether no further transition is possible.
type OrderStore ¶
type OrderStore interface {
// Stores an order together with its authorizations and challenges, all at revision 1.
// It returns ErrConflict when any ID is already in use. When order.Replaces is set, the
// same operation marks the certificate with that RenewalID as replaced by the order,
// returning ErrNotFound when no such certificate exists and ErrAlreadyReplaced when an order
// that is not invalid at order.CreatedAt already replaced it, see RFC 9773 section 5.
CreateOrder(ctx context.Context, order *Order, authzs []*Authorization, challenges []*Challenge) error
// Returns the order with the given ID or ErrNotFound.
Order(ctx context.Context, id string) (*Order, error)
// Returns up to limit order IDs of the account in creation order, starting after the
// given ID. An empty after starts at the beginning.
OrderIDs(ctx context.Context, accountID, after string, limit int) ([]string, error)
// Returns the authorization with the given ID or ErrNotFound.
Authorization(ctx context.Context, id string) (*Authorization, error)
// Replaces the stored authorization when the revisions match and increments authz.Revision.
// When the authorization reaches a terminal status, the same operation marks its order invalid
// unless issuance was already dispatched.
UpdateAuthorization(ctx context.Context, authz *Authorization) error
// Reports whether the account holds a valid unexpired authorization for every identifier,
// read in one consistent snapshot. Revocation by another account relies on it.
AuthorizedFor(ctx context.Context, accountID string, identifiers []Identifier, now time.Time) (bool, error)
// Returns the challenge with the given ID or ErrNotFound.
Challenge(ctx context.Context, id string) (*Challenge, error)
}
Persists orders with their authorizations and challenges.
type Policy ¶
type Policy interface {
// Reviews a new account. The key, contacts and external account identity are set.
NewAccount(ctx context.Context, account *Account) error
// Reviews a new order after identifier normalization. The policy may change NotBefore and
// NotAfter but not the identifiers.
NewOrder(ctx context.Context, account *Account, order *Order) error
}
Reviews requests before the server stores their result. A returned *Problem is sent to the client, any other error is reported as serverInternal. Nil methods are not allowed, embed AllowAll to accept everything.
type Problem ¶
type Problem struct {
Type ErrorType
Detail string
// Overrides the default HTTP status of the type when it is not zero.
Status int
// Names the identifier a subproblem refers to.
Identifier *Identifier
Subproblems []*Problem
// Lists the supported signature algorithms in a badSignatureAlgorithm problem.
Algorithms []string
// Sets the Retry-After header. It is not serialized.
RetryAfter time.Duration
}
An ACME problem document, see RFC 7807 and RFC 8555 section 6.7. It implements error.
func NewProblem ¶
Returns a problem of the given type with the default HTTP status of that type.
func (*Problem) HTTPStatus ¶
Returns the status the problem is written with.
func (*Problem) MarshalJSON ¶
Writes the problem document with fully qualified types.
func (*Problem) UnmarshalJSON ¶
Reads a problem document and strips the ACME namespace from its types.
func (*Problem) WithIdentifier ¶
func (p *Problem) WithIdentifier(id Identifier) *Problem
Returns a copy that names the affected identifier.
func (*Problem) WithRetryAfter ¶
Returns a copy that asks the client to wait before retrying.
func (*Problem) WithStatus ¶
Returns a copy that overrides the HTTP status.
type RenewalAdvisor ¶
type RenewalAdvisor interface {
RenewalInfo(ctx context.Context, cert *Certificate) (RenewalInfo, error)
}
Suggests when a certificate should be renewed, see RFC 9773. Setting Config.RenewalInfo advertises the renewalInfo resource and accepts replaces on new orders.
type RenewalInfo ¶
type RenewalInfo struct {
// The window in which the client should renew. End must be later than Start.
Start time.Time
End time.Time
// An optional page explaining the window.
ExplanationURL string
// How long clients wait before asking again. Zero means DefaultRenewalRetryAfter.
RetryAfter time.Duration
}
The renewalInfo object of RFC 9773 section 4.2 with the Retry-After the response carries.
type RevokeRequest ¶
type RevokeRequest struct {
// Stable across every attempt to revoke one certificate. It is stored before the first call.
OperationID string
Certificate *x509.Certificate
DER []byte
// The CRL reason code recorded with the operation. A retry keeps the first recorded reason.
Reason int
// The account that requested the revocation. Empty when the certificate key signed the request.
AccountID string
}
What the host CA receives for a revocation.
type Revoker ¶
type Revoker interface {
Revoke(ctx context.Context, req RevokeRequest) error
}
Revokes certificates. Revoke returns nil only after the CA recorded the revocation durably. A returned *Problem is sent to the client, any other error is reported as serverInternal. The CA must deduplicate by OperationID because a client retries after an uncertain answer.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Serves the ACME resources below Config.BaseURL. Mount it at the base path, for example http.Handle("/acme/", srv), and run Run in the same or another process. Without Run, challenges are never validated and orders are never issued.
func (*Server) Ready ¶
Reports whether accepted work is being picked up. It returns an error when tasks have waited longer than WorkerConfig.StaleAfter without a claim, which means no Run is active.
func (*Server) Run ¶
Processes persisted validation and issuance work until ctx is canceled. Without a worker in this process or another process on the same store, challenges and orders never progress. Canceling ctx stops new claims but does not interrupt a task already in flight, so Run can take several WorkerConfig.TaskTimeout budgets to return when a validator, issuer or store stops responding. Waiting for it is optional: a claimed task is leased and its commit is fenced, so another worker picks it up once the lease lapses.
type Store ¶
type Store interface {
AccountStore
OrderStore
CertificateStore
WorkStore
}
The persistence contract a host supplies. Operations must be free of side effects a retry would repeat.
type Task ¶
type Task struct {
ID string
Kind TaskKind
TargetID string
AccountID string
// The earliest time a worker may claim the task.
RunAt time.Time
// The number of claims so far.
Attempts int
// The end of the current lease. Zero when no worker holds the task.
LeaseUntil time.Time
// Increases on every claim. Completion calls must present the current value.
Fence uint64
CreatedAt time.Time
}
A unit of persisted background work. Run claims tasks with a lease and a fence so a worker that lost its lease cannot commit a stale result.
type Validation ¶
type Validation struct {
Identifier Identifier
Type ChallengeType
Validated time.Time
// Reports that the validation authorized a CA certificate rather than an end-entity one.
CACertificate bool
// The latest acceptable certificate expiry under this proof, zero when unbounded.
GrantExpires time.Time
}
Records how one identifier of an order was validated.
type ValidationGrant ¶
type ValidationGrant struct {
// Allows the order to be finalized with a certificate request that asks for a CA
// certificate, see RFC 9448 section 6.
CACertificate bool
// Bounds the validity of certificates issued on this proof, see RFC 9447 section 7. The zero
// value leaves the validity to the order and the issuer.
Expires time.Time
}
What a successful challenge response authorizes beyond control of the identifier.
type ValidationRequest ¶
type ValidationRequest struct {
Challenge Challenge
Identifier Identifier
// Marks a wildcard authorization. Identifier then holds the base name.
Wildcard bool
// The token joined with the account key thumbprint, see RFC 8555 section 8.1.
KeyAuthorization string
// The RFC 7638 thumbprint of the account key captured when the client responded.
AccountKeyThumbprint string
// The Authority Token of a tkauth-01 response, empty for every other challenge type.
AuthorityToken string
}
What a validator receives for one challenge.
type Validator ¶
type Validator interface {
Validate(ctx context.Context, req ValidationRequest) error
}
Checks a challenge response. A nil result marks the challenge valid, a returned *Problem marks it invalid and any other error is retried until the attempt limit.
type WorkStore ¶
type WorkStore interface {
// Stores the challenge when its revision matches and enqueues the task in the same
// operation. It returns ErrConflict when the task ID is in use.
AcceptChallenge(ctx context.Context, challenge *Challenge, task *Task) error
// Stores the order when its revision matches and enqueues the task in the same operation.
FinalizeOrder(ctx context.Context, order *Order, task *Task) error
// Stores order.Issuance as the durable dispatch decision after checking the task fence and the
// revision and status of the order, the account and every authorization.
BeginIssuance(ctx context.Context, task *Task, order *Order, account *Account, authzs []*Authorization) error
// Leases the runnable task with the earliest RunAt, increments its fence and attempts
// and returns a copy. It returns ErrNotFound when no task is runnable at now.
ClaimTask(ctx context.Context, now, leaseUntil time.Time) (*Task, error)
// Releases the lease and stores task.RunAt for a later claim.
RescheduleTask(ctx context.Context, task *Task) error
// Removes the task without touching any resource.
FinishTask(ctx context.Context, task *Task) error
// Stores the challenge, authorization and order when every revision matches and removes the
// task, all in one operation. The order revision advances even when its fields are unchanged.
CompleteValidation(ctx context.Context, task *Task, challenge *Challenge, authz *Authorization, order *Order) error
// Stores the order when its revision matches, creates the certificate when it is not nil
// and removes the task, all in one operation. It returns ErrConflict when the certificate
// ID or a non-empty RenewalID is in use.
CompleteIssuance(ctx context.Context, task *Task, order *Order, cert *Certificate) error
// Counts tasks that were runnable at or before the given time and hold no lease past it.
PendingTasks(ctx context.Context, before time.Time) (int, error)
}
Persists background work together with the resource changes that create or finish it. Every task method that takes a claimed task checks task.Fence against the stored fence and returns ErrRevisionMismatch when another claim superseded it.
type WorkerConfig ¶
type WorkerConfig struct {
// The number of tasks processed at the same time. Defaults to 4.
Concurrency int
// How often an idle worker looks for work. Defaults to one second.
PollInterval time.Duration
// How long a claimed task stays leased. Defaults to two minutes.
Lease time.Duration
// Bounds each phase of a task separately: one validator or issuer call and the store
// operations that record its outcome. A task run uses several of these budgets in
// sequence. Defaults to 30 seconds.
TaskTimeout time.Duration
// Limits validation and pre-dispatch policy retries to five by default without limiting issuance recovery.
MaxAttempts int
// The first retry delay, doubled on every further attempt. Defaults to five seconds.
RetryDelay time.Duration
// How long accepted work may wait unclaimed before Ready reports a problem. Defaults to
// one minute.
StaleAfter time.Duration
// Marks that another process runs Run against the same store, which silences the warning
// logged when work is accepted while Run is inactive here.
External bool
}
Configures Run. Zero values select the defaults.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package challenge provides ACME network validators with explicit resolver and egress policy.
|
Package challenge provides ACME network validators with explicit resolver and egress policy. |
|
internal
|
|
|
jws
Package jws parses and verifies the restricted JWS profile of RFC 8555 section 6.2.
|
Package jws parses and verifies the restricted JWS profile of RFC 8555 section 6.2. |
|
Package memstore provides an in-memory acmeserver.Store for tests and examples.
|
Package memstore provides an in-memory acmeserver.Store for tests and examples. |
|
Package nonce provides a bounded single-process implementation of acmeserver.NonceManager.
|
Package nonce provides a bounded single-process implementation of acmeserver.NonceManager. |
|
Package storetest checks an acmeserver.Store against the persistence contract.
|
Package storetest checks an acmeserver.Store against the persistence contract. |