TLS certificates: Jarl obtains and renews them over ACME
Jarl is now an ACME client (lego's core, no DNS providers). It answers DNS-01 challenges in a zone it serves, or through Porkbun's DNS API while Porkbun serves the domain; "auto" decides from the domain's public name servers at every renewal, and a CNAME on _acme-challenge is followed. Certificates are used for jarld's HTTPS (served without a restart; each port speaks HTTPS and plain HTTP, plain HTTP from loopback and trusted proxies still served) and/or written for named's DNS over TLS/HTTPS, with rndc reconfig on renewal. Renewal follows the CA's ARI window or two thirds of the lifetime, with back-off. Certificates you renew yourself can be used too. Certificates page and dashboard card, jarl cert, /api/v1/certificates, Prometheus gauges, docs/certificates.md, and an e2e section against Pebble. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88965096bb
commit
88f63ffb0a
43 files changed
+4584
-45
No files matched your search
@@ -0,0 +1,71 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Certificate sources.
|
||||
const (
|
||||
CertSourceACME = "acme" // Jarl obtains and renews it (docs/certificates.md)
|
||||
CertSourceFile = "file" // you renew it; Jarl reads it from cert_file and key_file
|
||||
)
|
||||
|
||||
// Certificate uses.
|
||||
const (
|
||||
CertUseWeb = "web" // jarld's own HTTPS
|
||||
CertUseDNS = "dns" // written where named can read it, for DNS over TLS/HTTPS
|
||||
)
|
||||
|
||||
// Challenge routes: where Jarl publishes the DNS-01 challenge.
|
||||
const (
|
||||
ChallengeAuto = "auto" // decided at every issue from the domain's public name servers
|
||||
ChallengeJarl = "jarl" // in a zone Jarl serves (following a CNAME on _acme-challenge)
|
||||
ChallengePorkbun = "porkbun" // through Porkbun's DNS API
|
||||
)
|
||||
|
||||
// Certificate is a TLS certificate Jarl manages. Certificates are not
|
||||
// named configuration: they are kept outside changesets, like users.
|
||||
type Certificate struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
Domains []string `json:"domains"` // acme: the names to ask for; file: the names in the certificate
|
||||
CertFile string `json:"cert_file,omitempty"` // file source
|
||||
KeyFile string `json:"key_file,omitempty"` // file source
|
||||
Uses []string `json:"uses"`
|
||||
Challenge string `json:"challenge,omitempty"` // acme source
|
||||
KeyType string `json:"key_type,omitempty"` // acme source: ec256, ec384, rsa2048, rsa3072, rsa4096
|
||||
Comment string `json:"comment"`
|
||||
|
||||
// The current certificate: acme source only (a file source's is read
|
||||
// from its files). The key never leaves the server through the API.
|
||||
CertPEM string `json:"-"`
|
||||
KeyPEM string `json:"-"`
|
||||
|
||||
// What the current certificate says (both sources).
|
||||
Names []string `json:"names,omitempty"` // the names in it
|
||||
NotBefore *time.Time `json:"not_before,omitempty"`
|
||||
NotAfter *time.Time `json:"not_after,omitempty"`
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
Serial string `json:"serial,omitempty"`
|
||||
|
||||
// Renewal (acme source). RenewAt is the CA's suggested time (ARI) or,
|
||||
// without one, two thirds into the certificate's lifetime.
|
||||
RenewAt *time.Time `json:"renew_at,omitempty"`
|
||||
ARICheckedAt *time.Time `json:"-"`
|
||||
LastAttempt *time.Time `json:"last_attempt,omitempty"`
|
||||
LastSuccess *time.Time `json:"last_success,omitempty"`
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
Failures int `json:"failures,omitempty"`
|
||||
// LastRoutes says where the challenges went the last time ("adbraun.lol: porkbun").
|
||||
LastRoutes []string `json:"last_routes,omitempty"`
|
||||
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
UpdatedBy string `json:"updated_by"`
|
||||
}
|
||||
|
||||
// HasUse reports whether c is used for use.
|
||||
func (c *Certificate) HasUse(use string) bool { return slices.Contains(c.Uses, use) }
|
||||
Reference in new issue
Block a user