acmeserver

package module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

README

go-acme-server

GitHub Release Go Reference License Tests Interoperability Supply chain OpenSSF Scorecard

go-acme-server is a Go library for embedding an ACME server into CA and PKI applications.

The library owns the protocol: accounts, orders, authorizations, challenges, finalization, certificate retrieval and revocation as specified in RFC 8555. The host application supplies the parts that differ between deployments:

  • a Store that persists resources and background work, with memstore for tests and examples
  • an Issuer and a Revoker that call the host CA
  • one Validator per challenge type, with HTTP-01, DNS-01, TLS-ALPN-01 and tkauth-01 implementations in challenge
  • optional policy hooks, a rate limiter, a recorder for refused finalize and revocation requests, external account binding keys and directory metadata

The result is an http.Handler mounted behind the host's HTTPS origin and a worker that runs next to it. Standard ACME clients such as Certbot, lego, acmez and cert-manager then obtain certificates from the host CA.

Contents

Install

go get github.com/misiektoja/go-acme-server

The module needs Go 1.27.3 or newer. The root package is imported as acmeserver, the bundled validators live in challenge, the in-memory store in memstore and the single-process nonce manager in nonce:

import (
	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"
)

Scope

Standard Coverage Enabled by
RFC 8555 ACME Accounts, external account binding, key rollover, orders, HTTP-01, DNS-01, finalization, certificates, revocation Always
RFC 8737 TLS-ALPN-01 Challenge validation A ChallengeTLSALPN01 validator
RFC 8738 IP identifiers IPv4 and IPv6 identifiers through HTTP-01 and TLS-ALPN-01 Config.IPIdentifiers
RFC 9447 and RFC 9448 tkauth-01 with TNAuthList identifiers, including the ATIS-1000080 atc field for STIR/SHAKEN Config.TNAuthListIdentifiers
3GPP TS 33.310 Annex J tkauth-01 with NfInstanceId identifiers for 5G network functions Config.NfInstanceIDIdentifiers
RFC 9773 Renewal information and replaces Config.RenewalInfo
draft-ietf-acme-profiles Certificate profiles in the directory and on new orders Config.Profiles

Account keys may use ES256, ES384, ES512, RS256 or EdDSA. External account bindings use HS256, HS384 or HS512. Independent clients verify the behavior, see Tested clients. The API may still change before v1.0.0 as described in the Compatibility policy.

Certificate requests may carry post-quantum ML-DSA-44, ML-DSA-65 or ML-DSA-87 keys, or composite ML-DSA keys from draft-ietf-lamps-pq-composite-sigs-19, which pair ML-DSA with a classical algorithm. SLH-DSA keys from RFC 9909 are accepted in all 12 FIPS 205 parameter sets. The host CA may also sign with a composite ML-DSA or SLH-DSA key. The server checks composite and SLH-DSA signatures in requests and returned chains itself. Account keys stay classical.

Getting started

Create a Server, mount it under its base URL and run its worker. Both the handler and Run are required. Without Run, challenges are never validated and orders are never issued. Ready reports work that has waited too long while no worker holds a lease.

srv, err := acmeserver.New(acmeserver.Config{
	BaseURL:    "https://ca.example.com/acme/",
	Store:      memstore.New(),
	Nonces:     nonce.New(nonce.Options{}),
	Issuer:     myCA,
	Revoker:    myCA,
	Validators: map[acmeserver.ChallengeType]acmeserver.Validator{acmeserver.ChallengeHTTP01: myHTTP01},
})
if err != nil {
	log.Fatal(err)
}
go srv.Run(ctx)
http.Handle("/acme/", srv)

BaseURL must use https unless AllowInsecureBaseURL is set for local tests. Signed request URLs must match it exactly, so run the handler behind the public origin it advertises.

Getting started builds a complete local server in one file, issues a certificate with lego and names what to replace before production. That program is in examples/quickstart and runs with go run ./examples/quickstart. The package example in example_test.go shows the same wiring in Go documentation form.

Documentation

Full documentation is at misiektoja.github.io/go-acme-server.

The package documentation on pkg.go.dev describes every exported type. Repository files:

Limitations

  • No pre-authorization. The directory omits newAuthz and every order receives fresh authorizations, so clients validate each identifier again for every order. A PreApprover can skip validation for names the host approved in advance.
  • No alternate chains. The certificate response carries one chain and no rel="alternate" link.
  • No dns-account-01, short-term automatic renewal, delegation, subdomain authorizations or email, onion and device identifiers.
  • No built-in rate limits beyond a bound on the validations one account may have in progress. The host supplies a Limiter for nonces, new accounts, new orders, challenge responses and renewal information lookups. Policy and IssuancePolicy can refuse other work.
  • No TLS termination or CA. The host serves the handler behind its HTTPS origin, signs with its own CA and supplies durable storage. memstore keeps everything in memory.

Known limitations explains the consequences of each gap.

Support

SUPPORT.md directs usage questions, bug reports, feature requests and security reports. Check Troubleshooting and gather the versions, the problem document and the server log before posting.

License

Licensed under Apache-2.0.

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. A RefusalRecorder learns about the finalize and revokeCert requests the server refuses itself.

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
}

Index

Examples

Constants

View Source
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.

View Source
const DefaultRenewalRetryAfter = 6 * time.Hour

The Retry-After of renewalInfo responses when the advisor sets none.

View Source
const ProblemNamespace = "urn:ietf:params:acme:error:"

Prefixes every ACME error type in a problem document.

View Source
const TKAuthTypeATC = "atc"

The only Authority Token subtype this server offers, see RFC 9447 section 4.

Variables

View Source
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

func ImmediateRenewal added in v0.6.0

func ImmediateRenewal(since, now time.Time) (start, end time.Time)

Returns a window that opened at since and ends at now, for a certificate that should be renewed at once. Clients pick a random time inside the window, so a window reaching into the future would let some of them wait. A since that is not before now gives the minute before now.

func Retryable added in v0.6.0

func Retryable(p *Problem, err error) error

Returns a retryable failure that reports p once the attempts are exhausted.

func ValidNFType added in v0.6.0

func ValidNFType(value string) bool

Reports whether value is a valid RFC 9310 NFType: 1 to 32 printable ASCII characters without spaces. 3GPP TS 29.510 clause 6.1.6.3.3 lists the values in use, such as AMF or NRF, and keeps adding new ones, so the value is not compared with a list.

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

func (AllowAll) NewAccount(context.Context, *Account) error

Accepts the account.

func (AllowAll) NewOrder

func (AllowAll) NewOrder(context.Context, *Account, *Order) error

Accepts the order.

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
	// The further names and network function type the successful challenge granted, see
	// ValidationGrant.
	GrantNames  []Identifier
	GrantNFType string
	// Marks an authorization the Policy granted without a challenge, see PreApprover. It never
	// lets the account revoke another account's certificate.
	PreApproved bool
	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. It is cleared with the
	// reason when the CA refuses the revocation.
	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 Clock

type Clock interface {
	Now() time.Time
}

Supplies the current time.

func SystemClock

func SystemClock() Clock

Returns a Clock backed by the wall clock.

type ClockFunc

type ClockFunc func() time.Time

Adapts a function to the Clock interface.

func (ClockFunc) Now

func (f ClockFunc) Now() time.Time

Returns the time reported by the function.

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 RFC 5280 reason codes clients may request. Empty allows unspecified (0),
	// keyCompromise (1), affiliationChanged (3), superseded (4) and cessationOfOperation (5).
	// Only 0 to 5, 9 and 10 may be listed, because an ACME revocation is final and cannot be a
	// certificateHold (6).
	RevocationReasons []int
	// Learns about authenticated finalize and revokeCert requests the server refuses. Optional.
	Refusals RefusalRecorder
	// Limits how often clients may get nonces, create accounts and place orders. Optional. Without
	// it nothing is limited.
	Limits Limiter
	// 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
	// Lets a certificate issued for a TNAuthList identifier outlive the Authority Token, as
	// ATIS-1000080 clause 6.3.8 specifies for STIR/SHAKEN. The token must still be valid when
	// signing starts. By default the token expiry bounds the certificate, as RFC 9447 section 7
	// recommends.
	TNAuthListCertificatesOutliveTokens bool
	// Accepts NfInstanceId identifiers of 5G network functions as described in 3GPP TS 33.310
	// Annex J. It needs a tkauth-01 validator.
	NfInstanceIDIdentifiers 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 the validation tasks one account may have waiting or running at the same time. A
	// challenge response beyond the bound is refused with rateLimited and a Retry-After header
	// until earlier validations finish, so one account cannot occupy every worker for long. Zero
	// selects the MaxIdentifiers in effect, which lets one full order validate at once.
	MaxPendingValidations 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
	// The certificate profiles advertised in the directory meta and accepted on new orders, see
	// draft-ietf-acme-profiles. Empty leaves the extension off. A new order that names a profile is
	// then refused with invalidProfile.
	Profiles []Profile
	// The profile an order gets when the client names none. It is required when Profiles is set
	// and must be one of them.
	DefaultProfile string
	// 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"
	ErrorInvalidProfile          ErrorType = "invalidProfile"
	ErrorMalformed               ErrorType = "malformed"
	ErrorOrderNotReady           ErrorType = "orderNotReady"
	ErrorRateLimited             ErrorType = "rateLimited"
	ErrorRejectedIdentifier      ErrorType = "rejectedIdentifier"
	ErrorServerInternal          ErrorType = "serverInternal"
	ErrorTLS                     ErrorType = "tls"
	ErrorUnauthorized            ErrorType = "unauthorized"
	ErrorUnsupportedContact      ErrorType = "unsupportedContact"
	ErrorUnsupportedIdentifier   ErrorType = "unsupportedIdentifier"
	ErrorUserActionRequired      ErrorType = "userActionRequired"
)

Error types registered by RFC 8555 section 6.7 and RFC 9773 section 5, plus invalidProfile from draft-ietf-acme-profiles, which the draft asks IANA to register.

func (ErrorType) URN

func (t ErrorType) URN() string

Returns the fully qualified problem type.

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.

func (Identifier) URI added in v0.6.0

func (id Identifier) URI() string

Returns the URI name a certificate carries for an NfInstanceId identifier, or an https URI identifier's value. Other identifier types have no URI form and return an empty string.

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"
	// The UUID of a 5G network function instance, see 3GPP TS 33.310 clause J.3.3.2. A certificate
	// carries it as the URI name urn:uuid:<value>.
	IdentifierNfInstanceID IdentifierType = "NfInstanceId"
	// An https URI name that an authority token adds to a certificate, such as the API root of a
	// network function. No ACME specification registers it, so orders cannot request it.
	IdentifierURI IdentifierType = "uri"
)

Identifier types registered by RFC 8555 section 9.7.7, RFC 8738, RFC 9448 and 3GPP TS 33.310 Annex J, plus the uri type that only authority tokens grant.

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.PublicKey is nil for a composite ML-DSA
	// key. Read that key with compositex509.ParsePKIXPublicKey from CSR.RawSubjectPublicKeyInfo
	// and certify it with compositex509.CreateCertificate. CSR.PublicKey is also nil for an
	// SLH-DSA key (RFC 9909), which the issuer certifies with RawSubjectPublicKeyInfo unchanged.
	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, authorization or authority token expiry, after which new signing is
	// forbidden.
	Deadline time.Time
	// Allows only recovery of an existing result, never a new signing operation.
	RecoveryOnly bool
	// The Order.Profile name the certificate is issued under. It is empty when the server offered
	// no profiles when the order was created.
	Profile string
}

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
	// Dates the window of a revoked certificate. Defaults to the system clock.
	Clock Clock
}

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 and ends at the time of the request, so clients renew at once.

type LimitKind added in v0.6.0

type LimitKind string

Names the request a Limiter is asked about.

const (
	// A request that gets a nonce without having spent one: a newNonce request or an error
	// answer to a request that spent no valid nonce, such as one for an unknown resource, with a
	// wrong method, an unparsable JWS or a bad nonce. An error answer to a request that spent a
	// nonce carries its replacement without asking. LimitRequest.Account is nil.
	LimitNewNonce LimitKind = "newNonce"
	// A newAccount request that creates an account, asked after the contacts, the terms of service
	// agreement and any external account binding were checked. A request for an existing account
	// is not asked about.
	LimitNewAccount LimitKind = "newAccount"
	// A newOrder request, asked after its identifiers, profile and replaces member were checked and
	// before Policy.NewOrder.
	LimitNewOrder LimitKind = "newOrder"
	// A challenge response that starts a validation, asked after the response and the
	// authorization were checked and the account's validations were counted against
	// Config.MaxPendingValidations, before the validation is enqueued. A response to a challenge
	// that is no longer pending is not asked about.
	LimitChallengeResponse LimitKind = "challengeResponse"
	// An unauthenticated renewalInfo request with a well-formed identifier, asked before the
	// certificate is looked up. LimitRequest.Account is nil.
	LimitRenewalInfo LimitKind = "renewalInfo"
)

The requests a Limiter is asked about. More kinds may be added, so a Limiter should allow kinds it does not know.

type LimitRequest added in v0.6.0

type LimitRequest struct {
	Kind LimitKind
	// The address of the client connection, taken from http.Request.RemoteAddr with IPv4-mapped
	// IPv6 addresses unmapped. It is the zero Addr when RemoteAddr holds no IP address. Forwarded
	// headers are never read, because any client can set them. A host behind a trusted reverse
	// proxy can rewrite RemoteAddr before the request reaches the handler.
	Client netip.Addr
	// For newAccount the account the request would create, not yet stored. For newOrder and
	// challengeResponse the account making the request.
	Account *Account
	// For newOrder the normalized identifiers of the order. For challengeResponse the identifier
	// of the authorization, with its wildcard prefix.
	Identifiers []Identifier
	// The certificate a new order replaces, checked against the account and the identifiers as
	// RFC 9773 section 5 requires. Empty when the order replaces nothing.
	Replaces string
	// For challengeResponse the validations of the account that are waiting or running before
	// this one. The server has already checked that it is below Config.MaxPendingValidations.
	PendingValidations int
}

Describes a request a Limiter may refuse. The Limiter must not change Account.

type Limiter added in v0.6.0

type Limiter interface {
	Allow(ctx context.Context, req LimitRequest) error
}

Limits how often clients may get nonces, create accounts, place orders, start validations and look up renewal information. Allow is asked once for each such request and should count the request when it returns nil. A returned *Problem is sent to the client, normally rateLimited with WithRetryAfter so the client knows when to try again. Any other error is logged and answered with serverInternal, so a host that prefers to serve clients while its limit store is unavailable returns nil instead.

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
	// The certificate profile selected when the order was created, see draft-ietf-acme-profiles.
	// It is empty when the server offered no profiles at that time.
	Profile   string
	CreatedAt time.Time
	Revision  uint64
}

A stored order resource.

func (*Order) StatusAt

func (o *Order) StatusAt(now time.Time) OrderStatus

Returns the status clients see at now, treating an expired pending or ready order as invalid.

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
	// with Error set to InvalidAuthorizationProblem() unless issuance was already dispatched.
	UpdateAuthorization(ctx context.Context, authz *Authorization) error
	// Reports whether the account holds a valid unexpired authorization that is not PreApproved
	// 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. Any other change, or a NotAfter no later than NotBefore, fails the request with
	// serverInternal.
	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 PreApprover added in v0.6.0

type PreApprover interface {
	// Returns the identifiers of a new order that need no challenge. It runs after NewOrder has
	// accepted the order, which it must not change. Each returned identifier must be one of
	// order.Identifiers, wildcard names included. A returned *Problem refuses the order and any
	// other error, or an identifier the order does not contain, is reported as serverInternal.
	PreApprove(ctx context.Context, account *Account, order *Order) ([]Identifier, error)
}

An optional extension of Policy that authorizes identifiers without a challenge, for example names an administrator approved in advance for an external account binding. Their authorizations start valid with no challenges and expire with the order. An order whose identifiers are all pre-approved starts ready.

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 AsProblem

func AsProblem(err error) (*Problem, bool)

Returns the Problem in the error chain of err, if there is one.

func Compound

func Compound(detail string, subproblems ...*Problem) *Problem

Returns a compound problem that carries the given subproblems.

func InvalidAuthorizationProblem added in v0.5.0

func InvalidAuthorizationProblem() *Problem

Returns the order error for an authorization that stopped being valid before issuance was dispatched. UpdateAuthorization stores it on the order it invalidates.

func NewProblem

func NewProblem(t ErrorType, detail string) *Problem

Returns a problem of the given type with the default HTTP status of that type.

func Problemf

func Problemf(t ErrorType, format string, args ...any) *Problem

Returns a problem whose detail is formatted from the arguments.

func (*Problem) Error

func (p *Problem) Error() string

Returns the fully qualified type followed by the detail.

func (*Problem) HTTPStatus

func (p *Problem) HTTPStatus() int

Returns the status the problem is written with.

func (*Problem) MarshalJSON

func (p *Problem) MarshalJSON() ([]byte, error)

Writes the problem document with fully qualified types.

func (*Problem) UnmarshalJSON

func (p *Problem) UnmarshalJSON(data []byte) error

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

func (p *Problem) WithRetryAfter(d time.Duration) *Problem

Returns a copy that asks the client to wait before retrying.

func (*Problem) WithStatus

func (p *Problem) WithStatus(status int) *Problem

Returns a copy that overrides the HTTP status.

type Profile added in v0.6.0

type Profile struct {
	// The name clients send in the newOrder profile member. It has 1 to 64 ASCII letters, digits,
	// dots, underscores or hyphens and starts with a letter or digit.
	Name string
	// A human-readable description the server publishes as plain text at <BaseURL>profiles/<Name>.
	Description string
	// An absolute https URL of a description published elsewhere. Exactly one of Description and
	// URL is set.
	URL string
}

A certificate profile offered to clients, see draft-ietf-acme-profiles. The host decides what a profile means and receives the selected name in IssueRequest.Profile.

type ProfileChooser added in v0.6.0

type ProfileChooser interface {
	// Returns the profile for an order whose client named none, or "" for the default.
	DefaultProfile(ctx context.Context, account *Account) (string, error)
}

An optional extension of Policy that picks the profile of a new order whose client named none, for example the profile an account's external account binding allows. Policy.NewOrder sees the chosen profile. An empty answer keeps Config.DefaultProfile. A returned *Problem refuses the order. Any other error, or a profile the server does not offer, is reported as serverInternal.

type Refusal added in v0.6.0

type Refusal struct {
	Kind RefusalKind
	// The requesting account. It is empty for a revocation signed with the certificate key.
	AccountID string
	// The order a finalize request was for and the identifiers it covers.
	OrderID     string
	Identifiers []Identifier
	// The DER certificate request of a finalize request, when it was valid base64url.
	CSR []byte
	// The DER certificate of a revocation request, when it was valid base64url, and the reason
	// code it asked for.
	Certificate []byte
	Reason      int
	// The answer the client receives. The recorder must not change it.
	Problem *Problem
}

Describes an authenticated finalize or revokeCert request that was answered with a problem. The server refuses these requests itself, so without a recorder the host never learns about them.

type RefusalKind added in v0.6.0

type RefusalKind string

Names the request a Refusal answered.

const (
	// A finalize request whose CSR or order state the server refused.
	RefusalFinalize RefusalKind = "finalize"
	// A revokeCert request the server or the Revoker refused.
	RefusalRevocation RefusalKind = "revocation"
)

The requests whose refusals reach a RefusalRecorder.

type RefusalRecorder added in v0.6.0

type RefusalRecorder interface {
	RecordRefusal(ctx context.Context, refusal Refusal) error
}

Learns about refused finalize and revokeCert requests, for example to keep an audit trail of why a client got no certificate. Requests that fail authentication and server faults are not reported. RecordRefusal runs before the answer is sent, detached from the client request so a disconnect does not cancel it. A returned error is logged and does not change the answer.

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 RetryableError added in v0.6.0

type RetryableError struct {
	Problem *Problem
	Err     error
}

A validation failure worth retrying, such as a refused connection or an unanswered DNS query. The worker retries it like any other error. When the attempts run out, Problem becomes the challenge error instead of serverInternal. Err is the cause and must not be a *Problem, because a *Problem in the chain ends validation at once.

func (*RetryableError) Error added in v0.6.0

func (e *RetryableError) Error() string

Returns the problem followed by the cause.

func (*RetryableError) Unwrap added in v0.6.0

func (e *RetryableError) Unwrap() error

Returns the cause, never the reported problem.

type RevokeRequest

type RevokeRequest struct {
	// Stable across retries until the CA accepts or refuses the revocation. It is stored before the
	// first call and discarded when Revoke returns a *Problem.
	OperationID string
	Certificate *x509.Certificate
	DER         []byte
	// The CRL reason code recorded with the operation. A retry keeps the 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 a final refusal sent to the client. Any other error is reported as serverInternal and the next request repeats the same operation. 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 New

func New(cfg Config) (*Server, error)

Validates the configuration and returns a Server. It starts no background work.

func (*Server) Ready

func (s *Server) Ready(ctx context.Context) error

Reports whether accepted work is being picked up. It returns an error when tasks have waited longer than WorkerConfig.StaleAfter without a claim while no task is leased, which means no Run is active on the store or every worker is stuck. A queue that is deep because clients keep the workers busy is not an error, so client load cannot fail a readiness probe. A worker that died holding leases is reported once they expire.

func (*Server) Run

func (s *Server) Run(ctx context.Context) error

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 up to WorkerConfig.Lease 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.

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

Routes a request to the resource named by its path.

type Store

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 TaskKind

type TaskKind string

Names what a background task does.

const (
	// Validates the challenge named by Task.TargetID.
	TaskValidate TaskKind = "validate"
	// Issues the certificate for the order named by Task.TargetID.
	TaskIssue TaskKind = "issue"
)

Task kinds.

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
	// Further names the certificate may carry and the network function type, both granted by an
	// authority token, see ValidationGrant.
	Names  []Identifier
	NFType string
	// Reports that the Policy authorized the identifier without a challenge. Type is then empty
	// and Validated is when the order was created.
	PreApproved bool
}

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
	// Further names the certificate may carry on this proof, such as the sans of a 3GPP NF
	// Certificate Authority Token. Each is a normalized dns, ip or uri identifier.
	Names []Identifier
	// The RFC 9310 network function type the proof attests, empty when it attests none.
	NFType string
}

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
	// Stores the challenge when its revision matches and releases the lease with task.RunAt for
	// a later claim, all in one operation. It records the error of a validation attempt that
	// will be retried.
	RetryValidation(ctx context.Context, task *Task, challenge *Challenge) 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)
	// Counts tasks that hold a lease past the given time.
	LeasedTasks(ctx context.Context, now time.Time) (int, error)
	// Counts the validation tasks of an account that have not finished, waiting or leased.
	AccountValidations(ctx context.Context, accountID string) (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. A task run never continues past its lease, so no other
	// worker can take the task while it runs. It must be at least twice TaskTimeout and defaults to
	// two minutes or twice TaskTimeout, whichever is longer.
	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 within its lease. Defaults to 30 seconds or half the lease, whichever is shorter.
	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.

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.
examples
quickstart command
A local ACME server for trying go-acme-server: in-memory storage, a throwaway CA and HTTP-01.
A local ACME server for trying go-acme-server: in-memory storage, a throwaway CA and HTTP-01.
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.
slhdsaspki
Package slhdsaspki encodes SLH-DSA public keys as the SubjectPublicKeyInfo of RFC 9909.
Package slhdsaspki encodes SLH-DSA public keys as the SubjectPublicKeyInfo of RFC 9909.
slhdsatest
Package slhdsatest creates SLH-DSA certificate requests and certificates for tests, which crypto/x509 cannot create.
Package slhdsatest creates SLH-DSA certificate requests and certificates for tests, which crypto/x509 cannot create.
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 noncetest checks an acmeserver.NonceManager against the replay protection contract.
Package noncetest checks an acmeserver.NonceManager against the replay protection contract.
Package storetest checks an acmeserver.Store against the persistence contract.
Package storetest checks an acmeserver.Store against the persistence contract.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL