Documentation
¶
Overview ¶
Package certflow decides whether a domain is ready for a public certificate, tells operators the truth about the certificate it has, and retries stuck renewals with backoff. It holds no ACME client: issuance and renewal stay with the embedded ingress.
Index ¶
- Constants
- func DaysLeft(notAfter, now time.Time) int
- func FailDays() int
- func IsRateLimitError(msg string) bool
- func ProofTTLFromEnv() time.Duration
- type Backing
- type Badge
- type Candidate
- type Entry
- type Existing
- type Input
- type Item
- type ItemState
- type Kind
- type Ledger
- func (l *Ledger) IssuedSince(now time.Time, window time.Duration) int
- func (l *Ledger) MarkStarted(domain string, at time.Time)
- func (l *Ledger) RecordFailure(domain string, at, now time.Time)
- func (l *Ledger) RecordIssued(domain string, at, now time.Time)
- func (l *Ledger) Restore(b Backing, entries []Entry, now time.Time)
- func (l *Ledger) Snapshot(domain string) Snapshot
- func (l *Ledger) StartedAt(domain string) time.Time
- type Limits
- type Orderer
- type Outcome
- type Proof
- type Prover
- type RateLimit
- type Renewal
- type RenewalResult
- type ReplaceError
- type Result
- type RetryState
- type Snapshot
- type ValidationError
- type Watchdog
- type WatchdogDeps
Constants ¶
const ( EnvFailedPerHour = "APP_ACME_FAILED_PER_HOUR" EnvDuplicatePerWeek = "APP_ACME_DUPLICATE_PER_WEEK" // LedgerKeep is how long stored attempts matter. LedgerKeep = ledgerKeep )
Env names for the Let's Encrypt limits the ledger reasons about.
const ( KindFailed = "failed" KindIssued = "issued" KindStarted = "started" )
Entry kinds written to a Backing.
const ( ReasonFailedValidations = "failed_validations" ReasonDuplicates = "duplicate_certificates" ReasonCARateLimited = "ca_rate_limited" )
Rate limit reasons.
const ( CodeIssuable = "issuable" CodeUpstream = "upstream_proxy" CodeDNS = "dns" CodePorts = "ports" CodeCAA = "caa" CodeEmail = "email" CodeRateLimit = "rate_limit" CodeStaging = "staging" )
Preflight item codes.
const ( EnvWatchdogInterval = "APP_CERT_WATCHDOG_INTERVAL" EnvWatchdogBackoff = "APP_CERT_WATCHDOG_BACKOFF" )
Env names for the renewal watchdog.
const BudgetWindow = duplicateWindow
BudgetWindow is the span the CA's duplicate and new-order limits are counted over.
const CodeReplaces = "replaces_certificate"
CodeReplaces is the preflight item and API error code for a staging request that would replace a working certificate.
const (
EnvFailDays = "APP_CERT_FAIL_DAYS"
)
Env names and defaults for the thresholds this package reads.
const EnvProofTTL = "APP_REACH_PROOF_TTL"
EnvProofTTL is how long a reachability answer is reused before the CA is asked again.
Variables ¶
This section is empty.
Functions ¶
func FailDays ¶
func FailDays() int
FailDays is how close to expiry an automatically renewed certificate turns the doctor red, from APP_CERT_FAIL_DAYS (default 14).
func IsRateLimitError ¶
IsRateLimitError reports whether a CA error text is a rate limit answer.
func ProofTTLFromEnv ¶
ProofTTLFromEnv reads the cache lifetime of an answer.
Types ¶
type Backing ¶
Backing persists ledger entries. Save is best effort: a failed write must not block issuance.
type Badge ¶
type Badge struct {
Kind Kind `json:"kind"`
DaysLeft int `json:"days_left"`
LastRenewal *Renewal `json:"last_renewal,omitempty"`
// NextRetryAt is when the watchdog tries again, set only while a renewal
// is stuck or failing.
NextRetryAt *time.Time `json:"next_retry_at,omitempty"`
}
Badge is the per-domain certificate truth shown on lists and the overview.
type Candidate ¶
type Candidate struct {
Domain string
Reason string
// Expired certificates are cleared before re-issuing; valid ones are left in place.
Expired bool
// Blocked is set while a CA rate limit makes a try pointless.
Blocked bool
}
Candidate is a certificate whose renewal failed or is stuck.
type Input ¶
type Input struct {
Domain string
Email string
DefaultEmail string
Staging bool
TLSUpstream bool
Report domaindoctor.Report
Existing []Existing
History Snapshot
Limits Limits
LastCAError string
LastCAErrorTime time.Time
Now time.Time
}
Input is everything Evaluate decides from. The caller fills Report from the domain doctor with ACME forced on, so CAA and port checks really run.
type Item ¶
type Item struct {
Code string `json:"code"`
State ItemState `json:"state"`
Detail string `json:"detail"`
Fix string `json:"fix,omitempty"`
}
Item is one preflight finding with the next step in plain words.
type Kind ¶
type Kind string
Kind says who signed the certificate a domain serves.
const ( KindNone Kind = "none" KindInternal Kind = "internal" KindStaging Kind = "staging" KindLetsEncrypt Kind = "letsencrypt" KindCustom Kind = "custom" KindProxy Kind = "proxy" KindOther Kind = "other" )
Kind values, from least to most trusted by browsers.
func Classify ¶
Classify names the signer from the leaf's issuer common name, the row's source and who owns TLS for the host.
func (Kind) AutoRenewed ¶
AutoRenewed reports whether the platform renews this kind itself.
type Ledger ¶
type Ledger struct {
// contains filtered or unexported fields
}
Ledger remembers recent production attempts per hostname so the next safe time can be shown before another try burns a limit. Attach a Backing to keep it across restarts; without one it lives in memory and the CA's answer wins.
func (*Ledger) IssuedSince ¶
IssuedSince counts production certificates issued in the last window across every hostname, which is what a shared weekly budget is spent on.
func (*Ledger) MarkStarted ¶
MarkStarted notes when a guided issuance began for domain.
func (*Ledger) RecordFailure ¶
RecordFailure notes a failed production validation; repeats of one timestamp are ignored.
func (*Ledger) RecordIssued ¶
RecordIssued notes a production certificate issued for domain.
func (*Ledger) Restore ¶
Restore replays stored entries into the ledger without writing them back, then sends later changes to b.
type Limits ¶
Limits are the CA limits that matter for one hostname.
func LimitsFromEnv ¶
func LimitsFromEnv() Limits
LimitsFromEnv reads the limits, defaulting to Let's Encrypt's published ones.
type Orderer ¶
Orderer places one staging order for domain and answers the HTTP-01 challenge on port 80. A nil error means the CA validated it. A *ValidationError means the CA ran the check and it failed; any other error means the CA could not be asked.
type Outcome ¶
type Outcome string
Outcome is what the staging order proved about port 80 from the outside.
type Proof ¶
type Proof struct {
Domain string `json:"domain"`
Outcome Outcome `json:"outcome"`
Detail string `json:"detail"`
CAReason string `json:"ca_reason,omitempty"`
NextSafeAt *time.Time `json:"next_safe_at,omitempty"`
At time.Time `json:"at"`
}
Proof is the verdict shown on the ports step.
type Prover ¶
type Prover struct {
// contains filtered or unexported fields
}
Prover asks a staging CA to reach this server, within the ledger and attempt limits.
type RateLimit ¶
type RateLimit struct {
Blocked bool
// NextSafeAt is the earliest time another production try is safe.
NextSafeAt time.Time
// Reason is "failed_validations", "duplicate_certificates" or "ca_rate_limited".
Reason string
}
RateLimit is the verdict on trying again now.
type Renewal ¶
type Renewal struct {
At time.Time `json:"at"`
Result RenewalResult `json:"result"`
Error string `json:"error,omitempty"`
}
Renewal is the last issue or renewal attempt as an operator reads it.
type RenewalResult ¶
type RenewalResult string
RenewalResult is the outcome of the last automatic issue or renewal.
const ( RenewalOK RenewalResult = "ok" RenewalFailed RenewalResult = "failed" )
RenewalResult values.
type ReplaceError ¶
ReplaceError says a staging request would swap a working certificate for an untrusted one.
func StagingConflict ¶
func StagingConflict(domain string, existing []Existing) *ReplaceError
StagingConflict returns the first working certificate a staging request would put at risk, or nil. Switching the CA drops every stored ACME certificate server-wide, so a working Let's Encrypt or public CA certificate on any domain counts; an uploaded or proxy certificate counts only for the requested domain.
func (*ReplaceError) Error ¶
func (e *ReplaceError) Error() string
Error is the plain-language message shown by the API, CLI and dashboard.
type Result ¶
type Result struct {
Domain string `json:"domain"`
Staging bool `json:"staging"`
// Email is the contact that will be used: the typed one or the inferred default.
Email string `json:"email"`
// Ready is true when no item failed.
Ready bool `json:"ready"`
Items []Item `json:"items"`
// NextSafeAt is set when a rate limit is in the way.
NextSafeAt *time.Time `json:"next_safe_at,omitempty"`
}
Result is the whole preflight for one domain.
type RetryState ¶
RetryState is one stuck domain's retry bookkeeping.
type Snapshot ¶
Snapshot is the recent history of one hostname.
func (Snapshot) Check ¶
func (s Snapshot) Check(lim Limits, now time.Time, lastCAErr string, lastCAErrAt time.Time) RateLimit
Check decides whether a production attempt is safe at now. lastCAErr is the CA's latest error text and lastCAErrAt its time, so a "rate limited" answer pauses even when this process never counted the attempts.
type ValidationError ¶
type ValidationError struct{ Detail string }
ValidationError means the CA was reached and its HTTP-01 check failed.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
type Watchdog ¶
type Watchdog struct {
// contains filtered or unexported fields
}
Watchdog retries stuck renewals with exponential backoff per domain.
func NewWatchdog ¶
func NewWatchdog(deps WatchdogDeps) *Watchdog
NewWatchdog builds a watchdog, reading its timings from the environment.