Documentation
¶
Overview ¶
Package ingress is the Phase 0 spike for embedding Caddy as a Go library, driven entirely through its in-process admin API. There is no `caddy` binary anywhere in this package and no Caddyfile on disk. Config goes in as a JSON document built from typed Go structs (never a hand-written Caddyfile string), because Phase 1's real requirement is building that document programmatically from app.yaml (the declarative app spec's contract), not editing config by hand.
This package intentionally defines its own minimal structs mirroring the slice of Caddy's JSON config schema this spike needs (admin, apps.http, apps.tls), rather than importing caddy's own config types directly. Caddy's own types lean heavily on json.RawMessage for every extension point (module namespaces), which makes them awkward to construct directly from Go; a small hand-rolled struct set that marshals to the same wire shape is both easier to build from app.yaml data later and easier to table-test in isolation, without starting Caddy at all.
Index ¶
- Constants
- Variables
- func ACMEIssuable(host string) bool
- func AuthGateTimeoutFromEnv(getenv func(string) string) (time.Duration, error)
- func CASwitchNotice() string
- func CacheTagHeaderName() string
- func ClearListenEnv()
- func DetectPublicIPs(ctx context.Context) []string
- func EnvSet(getenv func(string) string, name string) bool
- func FallbackDomain(publicHost, serviceName string) (domain string, ok bool)
- func FallbackDomainFor(publicHost, serviceName string, c ProviderChoice) (string, bool)
- func HTTPRedirectActive() bool
- func ImportFileCerts(ctx context.Context, certStore CertStore, dir string, logger *slog.Logger) (int, error)
- func IsPubliclyRoutable(ip net.IP) bool
- func IsWildcardDomain(domain string) bool
- func LegacyStorageDir() string
- func NormalizeAuthGateCheckPath(p string) (string, error)
- func NormalizeAuthGateCopyHeaders(names []string) ([]string, error)
- func PurgeCertsFromOtherIssuers(ctx context.Context, s CertPurgeStore, directoryURL string) (int, error)
- func PurgeCertsFromOtherIssuersOnce(ctx context.Context, s CertPurgeMarkerStore, directoryURL string) (int, error)
- func ResolvePublicHost(ctx context.Context) (host, source string)
- func SSLIPHost(publicHost string) (host string, ok bool)
- func SampleHost(publicHost string, c ProviderChoice) (string, bool)
- func SanitizeDNSLabel(s string) string
- func ServedHosts(cfg *Config) []string
- func ServesHTTP(svc store.DesiredService) bool
- func SetActiveCertStorage(storage *SQLiteStorage)
- func SetDefaultGeoResolver(r *GeoResolver)
- func SetHTTPRedirectActive(on bool)
- func TopReasonCount() int
- func ValidateACMEContact(email string) error
- func ValidateAuthGateURL(raw string) (*url.URL, error)
- func ValidateCustomBase(base string) error
- func ValidateTrustedProxies(raw string) error
- func ValidateWildcardDomain(domain string) error
- type ACMEEventsApp
- type ACMEFailure
- type ACMEFailures
- type ACMEIssuer
- type AbuseHandler
- type AbusePolicy
- type AccessFilter
- type AccessLog
- func (l *AccessLog) CaptureIdentity(on bool)
- func (l *AccessLog) FlushVisitorHistory(ctx context.Context) error
- func (l *AccessLog) FlushVisitorHistoryFinal() error
- func (l *AccessLog) IdentityWanted() bool
- func (l *AccessLog) Query(keys []string, f AccessFilter) []AccessRecord
- func (l *AccessLog) Record(key string, rec AccessRecord)
- func (l *AccessLog) RunVisitorHistory(ctx context.Context)
- func (l *AccessLog) SetRetentionSource(fn func() time.Duration)
- func (l *AccessLog) SetVisitorHistory(st VisitorHistoryStore, owner func(string) string, ...)
- func (l *AccessLog) SetVisitorRotationSource(fn func() time.Duration)
- func (l *AccessLog) Since(keys []string, since time.Time) []AccessRecord
- func (l *AccessLog) Subscribe(fn func(key string, rec AccessRecord)) (unsubscribe func())
- func (l *AccessLog) Summarize(keys []string, o VisitorSummaryOptions) VisitorSummary
- func (l *AccessLog) SummarizeWithHistory(ctx context.Context, app string, keys []string, o VisitorSummaryOptions) VisitorSummary
- type AccessRecord
- type ActiveHealthChecks
- type ActiveSplit
- type AdminConfig
- type AdminConfigSettings
- type Apps
- type AuditRecorder
- type AuthGate
- type AutoHTTPSConfig
- type AutoHostOptOutSource
- type Automation
- type AutomationPolicy
- type BasicAuthAccount
- type BasicAuthHandler
- type BasicAuthHash
- type BasicAuthProviders
- type CA
- type CARetryBackoff
- type CacheBucket
- type CacheHandler
- type CacheReason
- type CacheStats
- type CertKeyPEMPair
- type CertPurgeMarkerStore
- type CertPurgeStore
- type CertStore
- type CertificatesConfig
- type ChallengesConfig
- type ClientIPMatcher
- type CloudflareDNSProvider
- type Config
- type CorazaWAFHandler
- type CountedItem
- type DNS01Provider
- type DNSChallengeConfig
- type DNSProvider
- type DomainVisitors
- type Driver
- func (d *Driver) Apply(ctx context.Context, cfg *Config) error
- func (d *Driver) OwnsPort(port int) bool
- func (d *Driver) RestoreSnapshot(ctx context.Context) (bool, error)
- func (d *Driver) SaveSnapshotNow() error
- func (d *Driver) SetCARetryBackoff(f CARetryBackoff)
- func (d *Driver) SetSnapshot(store *SnapshotStore, fingerprint string)
- func (d *Driver) SnapshotStatus() SnapshotStatus
- func (d *Driver) Stop(ctx context.Context) error
- func (d *Driver) VerifySnapshot() (SnapshotCheck, error)
- type EdgeOverrides
- type EncodeHandler
- type ErrorPage
- type ErrorsConfig
- type ExpressionMatcher
- type FallbackResolver
- type FileServerHandler
- type FileStorage
- type FirewallHandler
- type GeoHandler
- type GeoResolver
- type GeoStatus
- type GoogleCloudDNSModule
- func (m *GoogleCloudDNSModule) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error)
- func (*GoogleCloudDNSModule) CaddyModule() caddy.ModuleInfo
- func (m *GoogleCloudDNSModule) DeleteRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error)
- func (m *GoogleCloudDNSModule) Provision(caddy.Context) error
- type GoogleCloudDNSProvider
- type HTTPApp
- type HTTPBasicAuthProvider
- type HTTPChallengeConfig
- type HTTPTransport
- type Hardening
- type HeaderOps
- type HeaderResponse
- type HeadersHandler
- type HealthChecks
- type HoldRoute
- type HoldTracker
- type IPFormat
- type InheritedSockets
- type InternalIssuer
- type LBRoute
- type Layer4App
- type Layer4ProxyHandler
- type Layer4Route
- type Layer4Server
- type Layer4UpstreamDial
- type LoadBalancing
- type LogConfig
- type LoggingConfig
- type MaintenanceGate
- type MaintenancePage
- type MaintenanceRoute
- type Matcher
- type PKIApp
- type PassiveHealthChecks
- type ProviderChoice
- type ProviderSource
- type ProxyHeaders
- type ProxyOptions
- type ProxyRewrite
- type ProxyRoute
- type RateLimitHandler
- type RateLimitZone
- type RedirectRoute
- type RedirectsHandler
- type RequestBodyHandler
- type RequestIdentity
- type RequestObservation
- type RequestStats
- func (s *RequestStats) DrainByApp() map[string]telemetry.RequestWindow
- func (s *RequestStats) KeysForApp(app string) []string
- func (s *RequestStats) Observe(key string, status int, d time.Duration, bytesIn, bytesOut int64, ...)
- func (s *RequestStats) ObservePath(key, path string, status int, d time.Duration, bytesIn, bytesOut int64, ...)
- func (s *RequestStats) ObserveRequest(key string, o RequestObservation)
- func (s *RequestStats) ObserveShield(key string, reason trafficpolicy.ShieldReason)
- func (s *RequestStats) OwnerOf(key string) string
- func (s *RequestStats) SetHostOwners(owners map[string]string)
- func (s *RequestStats) UpstreamTotals() map[string]telemetry.UpstreamStat
- type RequestStatsHandler
- type ResponseCache
- type ResponseHandler
- type ResponseMatcher
- type ReverseProxyHandler
- type RewriteHandler
- type Route
- type Route53DNSProvider
- type RouteCheck
- type RoutePolicy
- type RoutesOptions
- type SQLiteStorage
- func (s *SQLiteStorage) Delete(ctx context.Context, key string) error
- func (s *SQLiteStorage) Exists(ctx context.Context, key string) bool
- func (s *SQLiteStorage) List(ctx context.Context, prefix string, recursive bool) ([]string, error)
- func (s *SQLiteStorage) Load(ctx context.Context, key string) ([]byte, error)
- func (s *SQLiteStorage) Lock(ctx context.Context, name string) error
- func (s *SQLiteStorage) Stat(ctx context.Context, key string) (certmagic.KeyInfo, error)
- func (s *SQLiteStorage) Store(ctx context.Context, key string, value []byte) error
- func (s *SQLiteStorage) Unlock(ctx context.Context, name string) error
- func (s *SQLiteStorage) WithAuditRecorder(ar AuditRecorder) *SQLiteStorage
- type SQLiteStorageRef
- type SelectionPolicy
- type Server
- type ShieldHandler
- type SnapshotCheck
- type SnapshotData
- type SnapshotStatus
- type SnapshotStore
- type SplitTestingHandler
- type Splits
- type StaticResponseHandler
- func NewAssetUnavailableHandler(retryAfter int) StaticResponseHandler
- func NewErrorPageResponse(statusCode int, body string) StaticResponseHandler
- func NewHiddenRobotsResponse() StaticResponseHandler
- func NewMaintenancePageHandler(p MaintenancePage) StaticResponseHandler
- func NewMaintenanceResponseHandler() StaticResponseHandler
- func NewRedirectResponseHandler(targetURL string, statusCode int) StaticResponseHandler
- type StaticRoute
- type StaticSiteHandler
- type StreamRoute
- type SubrouteHandler
- type TLSApp
- type TLSCertificateOverride
- type TrustedProxiesConfig
- type Upstream
- type UpstreamOptions
- type UpstreamTLSConfig
- type VarsHandler
- type VisitorBucket
- type VisitorHistoryStore
- type VisitorSummary
- type VisitorSummaryOptions
- type WAFConfig
- type WakeRoute
Constants ¶
const ( // MaxAuthGateCopyHeaders caps copy_headers per domain. MaxAuthGateCopyHeaders = 16 // MaxAuthGateTimeoutSeconds caps a domain's own timeout override. MaxAuthGateTimeoutSeconds = 120 // DefaultAuthGateCheckPath is the check path when none is configured. DefaultAuthGateCheckPath = "/" )
const ( GateHeaderSecret = "X-Gate-Secret" GateHeaderHost = "X-Gate-Host" GateHeaderProto = "X-Gate-Proto" GateHeaderClientIP = "X-Gate-Client-Ip" )
Subrequest headers the built-in gate provider sets; the check endpoint trusts the others only when GateHeaderSecret matches.
const ( CertEventAbilityIssued = "cert.issued" CertEventAbilityRenewed = "cert.renewed" )
Synthetic actor/caller fields for a Store-triggered audit_log row, mirroring internal/api.ClientKindCLI et al.'s "named caller surface" constants. Ability doubles as the issued-vs-renewed distinction a reader (web/src/lib/auditLabels.ts) keys its friendly label off.
const ( ProviderSSLIP = "sslip.io" ProviderNIP = "nip.io" ProviderTraefik = "traefik.me" ProviderCustom = "custom" DefaultProvider = ProviderSSLIP )
Provider ids. Built-in ids are the provider's domain; ProviderCustom is the operator's own wildcard.
const ( EnvReadHeaderTimeout = "APP_INGRESS_READ_HEADER_TIMEOUT" EnvReadTimeout = "APP_INGRESS_READ_TIMEOUT" EnvWriteTimeout = "APP_INGRESS_WRITE_TIMEOUT" EnvIdleTimeout = "APP_INGRESS_IDLE_TIMEOUT" EnvMaxHeaderBytes = "APP_INGRESS_MAX_HEADER_BYTES" EnvMaxBodyBytes = "APP_INGRESS_MAX_BODY_BYTES" EnvProtocols = "APP_INGRESS_PROTOCOLS" EnvTrustedProxies = "APP_INGRESS_TRUSTED_PROXIES" EnvClientIPHeaders = "APP_INGRESS_CLIENT_IP_HEADERS" EnvRetryWindow = "APP_INGRESS_RETRY_WINDOW" EnvRetryInterval = "APP_INGRESS_RETRY_INTERVAL" EnvDialTimeout = "APP_INGRESS_DIAL_TIMEOUT" EnvFailDuration = "APP_INGRESS_PASSIVE_FAIL_DURATION" EnvRetryAfter = "APP_INGRESS_UNAVAILABLE_RETRY_AFTER" EnvGracePeriod = "APP_INGRESS_GRACE_PERIOD" )
Environment variables that tune Hardening. Durations use Go syntax ("10s"), "0" disables a timeout or limit where noted.
const ( SocketNameHTTPS = "https" SocketNameHTTP = "http" // EnvSocketActivation is auto (default), true or false. EnvSocketActivation = "APP_INGRESS_SOCKET_ACTIVATION" )
Socket names in the systemd .socket unit (FileDescriptorName=).
const ( LBPolicyRoundRobin = "round_robin" LBPolicyLeastConn = "least_conn" LBPolicyIPHash = "ip_hash" LBPolicyClientIPHash = "client_ip_hash" LBPolicyURIHash = "uri_hash" LBPolicyCookie = "cookie" LBPolicyWeighted = "weighted_round_robin" // LBPolicyFirst sends to the first healthy upstream (primary, then standby). LBPolicyFirst = "first" )
Selection policy names accepted by LBRoute.Policy.
const ( DefaultMaintenanceTitle = "Down for maintenance" DefaultMaintenanceMessage = "This site is temporarily unavailable. Please check back soon." DefaultMaintenanceRetryAfter = 300 MaxMaintenanceTitle = 120 MaxMaintenanceMessage = 2000 )
Maintenance page defaults and bounds.
const ( ReasonVaryHeader = "vary_header" ReasonSetCookie = "set_cookie" ReasonPrivate = "private" ReasonNoStore = "no_store" ReasonNoCache = "no_cache" ReasonStatusNotCached = "status_not_cached" ReasonTooLarge = "too_large" ReasonRange = "range" ReasonAuthorization = "authorization" ReasonCookieRequest = "cookie_request" ReasonUpgrade = "upgrade" // ReasonOther collects keys past the per-domain cap. ReasonOther = "other" )
Reason codes for why a request or response stays out of the cache.
const ( PurgeURL = "url" PurgePrefix = "prefix" PurgeAll = "all" PurgeTag = "tag" )
Cache purge scopes.
const ( // EnvCacheTagHeader names the response header an app uses to tag // objects for purge-by-tag. EnvCacheTagHeader = "APP_INGRESS_CACHE_TAG_HEADER" // EnvCacheRevalidateWindow is how long an expired object with an ETag // or Last-Modified is kept so the app can answer 304 instead of resending. EnvCacheRevalidateWindow = "APP_INGRESS_CACHE_REVALIDATE_WINDOW" // EnvCacheCoalesceWait bounds how long a request waits for an identical // in-flight miss before going to the app itself. EnvCacheCoalesceWait = "APP_INGRESS_CACHE_COALESCE_WAIT" // EnvCacheReasonKeys caps the distinct "why not cached" keys kept per // domain; further keys are counted under ReasonOther. EnvCacheReasonKeys = "APP_INGRESS_CACHE_REASON_KEYS" // EnvCacheTopReasons is how many reasons the API lists per app. EnvCacheTopReasons = "APP_INGRESS_CACHE_TOP_REASONS" )
Cache tunables read at first use, so there are no hardcoded limits.
const ( EnvGeoIPDB = "APP_GEOIP_DB" EnvGeoIPCountryHeader = "APP_GEOIP_COUNTRY_HEADER" EnvGeoIPSource = "APP_GEOIP_SOURCE" )
Geo source environment variables.
const ( GeoSourceHeader = "header" GeoSourceMMDB = "mmdb" GeoSourceNone = "none" )
Geo source names, reported by GeoStatus.
const ( PublicHostSourceEnv = "env" PublicHostSourceDetected = "detected" PublicHostSourceDisabled = "disabled" PublicHostSourceNone = "none" )
Public host sources reported alongside the resolved address.
const ( RouteCheckHeader = "X-Ingress-Route-Check" RouteCheckReplyHeader = "X-Ingress-Route-Reply" )
Headers of the route check a verifier sends through a fronting proxy.
const ( // SnapshotKeyPurpose is the secrets.Manager.DeriveKey purpose for the snapshot seal. SnapshotKeyPurpose = "ingress-snapshot" // SnapshotFilename is the snapshot's name inside the data directory. SnapshotFilename = "ingress-snapshot.bin" )
const ( // EnvStaticAssetMaxAge sets the max-age of fingerprinted static assets. EnvStaticAssetMaxAge = "APP_STATIC_ASSET_MAX_AGE" // EnvStaticCompressMinLength is the smallest body the encoder compresses. EnvStaticCompressMinLength = "APP_STATIC_COMPRESS_MIN_LENGTH" )
const ( WakeTokenHeader = "X-Wake-Token" //nolint:gosec // header name, not a credential WakeAppHeader = "X-Wake-App" WakeURIHeader = "X-Wake-Uri" // WakeHeldHeader marks the hook's response so the edge record is flagged // as a cold start; the edge strips it before the client sees it. WakeHeldHeader = "X-Wake-Held" )
Headers the wake route sends so the control plane knows which app to start.
const (
// CacheStatusHeader reports HIT, MISS, STALE or BYPASS on cacheable paths.
CacheStatusHeader = "X-Cache"
)
const EnvAuthGateTimeout = "APP_AUTH_GATE_TIMEOUT"
EnvAuthGateTimeout overrides the auth gate subrequest timeout for domains that do not set their own. Go duration ("3s") or whole seconds ("3").
const EnvCacheMaxBytes = "APP_INGRESS_CACHE_MAX_BYTES"
EnvCacheMaxBytes caps the whole in-process response cache.
const HeaderCFConnectingIP = "CF-Connecting-IP"
HeaderCFConnectingIP carries the visitor address Cloudflare saw.
const LocalHost = "localhost"
LocalHost as APP_PUBLIC_HOST selects <app>.localhost names, which browsers and most OSes resolve to loopback with no DNS.
const NoCertificateNotice = "No HTTPS certificate exists for this address yet, so it is not redirected to https.\n" +
"Open the dashboard on its own port (over an SSH tunnel if your firewall blocks it),\n" +
"then enable HTTPS from Domains or with: levelrail-cli settings ingress https enable\n"
NoCertificateNotice is what port 80 answers for a host with no certificate, so the visitor is told the truth instead of hitting a TLS failure.
const (
// NoIndexHeaderValue is what a hidden domain sends on every response.
NoIndexHeaderValue = "noindex, nofollow, noarchive, nosnippet"
)
const UpstreamHostPlaceholder = "{http.reverse_proxy.upstream.hostport}"
UpstreamHostPlaceholder sends the dialed upstream's host:port as Host.
const WakePath = "/api/v1/hooks/wake"
WakePath is the control plane endpoint a wake route proxies to.
Variables ¶
var ( ErrACMEContactInvalid = errors.New("acme_email is not a valid email address") ErrACMEContactReserved = errors.New("acme_email uses a domain Let's Encrypt rejects (example.com, .test, .invalid, .localhost and similar); use a real, reachable address") )
Reasons ValidateACMEContact rejects an address, so callers can tell them apart.
var ( // ErrNoSnapshot means no snapshot file exists yet. ErrNoSnapshot = errors.New("ingress: no route snapshot") // ErrSnapshotCorrupt means the file is truncated, tampered with or sealed with another key. ErrSnapshotCorrupt = errors.New("ingress: route snapshot is corrupt") // ErrSnapshotStale means the snapshot was taken with a different listener setup. ErrSnapshotStale = errors.New("ingress: route snapshot was taken with different listeners") )
var ErrNoAppliedConfig = errors.New("ingress: no applied config to snapshot")
ErrNoAppliedConfig means nothing has been applied yet, so there is nothing to snapshot.
Functions ¶
func ACMEIssuable ¶ added in v0.2.0
ACMEIssuable reports whether a public CA can plausibly issue for host. Wildcards are judged by their base name.
func AuthGateTimeoutFromEnv ¶ added in v0.2.0
AuthGateTimeoutFromEnv returns the default subrequest timeout.
func CASwitchNotice ¶
func CASwitchNotice() string
CASwitchNotice describes a failed CA switch that is waiting to be retried, or "" when there is none.
func CacheTagHeaderName ¶
func CacheTagHeaderName() string
CacheTagHeaderName is the response header apps use to tag cached objects.
func ClearListenEnv ¶ added in v0.2.0
func ClearListenEnv()
ClearListenEnv removes the sd_listen_fds variables so child processes do not mistake them for their own.
func DetectPublicIPs ¶ added in v0.2.0
DetectPublicIPs returns this host's public IPv4 and IPv6 addresses (each when it has one), so a DNS check can accept an A or an AAAA record.
func FallbackDomain ¶ added in v0.2.0
FallbackDomain synthesizes the zero-config, publicly resolvable HTTPS hostname a fresh deploy with no domain configured gets routed under: "<app>.<public-ip-dash-encoded>.sslip.io". Real DNS, so Caddy's existing automatic-HTTPS path (internal/reconcile/ingress's own ACME vs internal-issuer choice, governed by store.IngressSettings. ACMEEnabled exactly like any operator-configured domain) needs no special case to issue it a certificate, as long as this control plane's own :80/:443 are genuinely reachable at publicHost from the public internet, the same precondition any custom domain already has.
ok is false, meaning "no fallback available, route nothing," when:
- publicHost isn't a literal IP address (APP_PUBLIC_HOST as a hostname has no address to dash-encode; a hostname operator already has a resolvable name and should set a real domain instead)
- the IP is private, loopback, link-local, or unspecified: sslip.io would still resolve it, but Let's Encrypt's HTTP-01 challenge could never reach a non-public address regardless, so producing a URL that can only ever fail to get a trusted certificate is worse than producing none
- serviceName has no characters left after sanitizing into a valid DNS label (lowercased, non-alphanumeric runs collapsed to a single hyphen, leading/trailing hyphens trimmed)
func FallbackDomainFor ¶
func FallbackDomainFor(publicHost, serviceName string, c ProviderChoice) (string, bool)
FallbackDomainFor is FallbackDomain for an explicit provider choice. An unusable choice yields no hostname.
func HTTPRedirectActive ¶ added in v0.2.0
func HTTPRedirectActive() bool
HTTPRedirectActive reports the last value the ingress reconciler set.
func ImportFileCerts ¶ added in v0.2.0
func ImportFileCerts(ctx context.Context, certStore CertStore, dir string, logger *slog.Logger) (int, error)
ImportFileCerts copies a legacy Caddy file-storage tree (dir/certificates, dir/acme) into the database-backed cert storage, skipping keys already present, so switching storage does not force every domain to re-issue. A missing dir is not an error. Returns the number of keys imported.
func IsPubliclyRoutable ¶ added in v0.2.0
IsPubliclyRoutable is isPubliclyRoutable for callers outside this package.
func IsWildcardDomain ¶
IsWildcardDomain reports whether domain is a wildcard hostname (e.g. "*.example.com"). A leading "*." is the whole convention: no separate schema field marks a domain wildcard-eligible.
func LegacyStorageDir ¶ added in v0.2.0
func LegacyStorageDir() string
LegacyStorageDir is where Caddy's default file storage keeps its data when no storage module is configured.
func NormalizeAuthGateCheckPath ¶ added in v0.2.0
NormalizeAuthGateCheckPath defaults an empty path to "/" and rejects anything that is not an absolute path with an optional query.
func NormalizeAuthGateCopyHeaders ¶ added in v0.2.0
NormalizeAuthGateCopyHeaders canonicalizes and dedupes header names, rejecting non-tokens, reserved names and lists over the cap.
func PurgeCertsFromOtherIssuers ¶ added in v0.2.0
func PurgeCertsFromOtherIssuers(ctx context.Context, s CertPurgeStore, directoryURL string) (int, error)
PurgeCertsFromOtherIssuers deletes every stored certificate not issued through directoryURL's CA ("" means Let's Encrypt production). certmagic reuses a still-valid certificate from any issuer, so without this a switch from staging or the internal CA to the real CA would never re-issue until the old certificate expired. Returns the number of keys removed.
func PurgeCertsFromOtherIssuersOnce ¶ added in v0.2.0
func PurgeCertsFromOtherIssuersOnce(ctx context.Context, s CertPurgeMarkerStore, directoryURL string) (int, error)
PurgeCertsFromOtherIssuersOnce runs the purge only when the configured issuer differs from the one last purged for, so a restart never deletes valid certificates again and burns CA rate limits.
func ResolvePublicHost ¶ added in v0.2.0
ResolvePublicHost returns the address apps are reachable at and where it came from: APP_PUBLIC_HOST wins, APP_PUBLIC_IP_DETECT=off disables probing, otherwise the first public IPv4 literal any probe URL returns, falling back to IPv6 only when no IPv4 answer arrives. Never blocks longer than APP_PUBLIC_IP_PROBE_TIMEOUT.
func SSLIPHost ¶ added in v0.2.0
SSLIPHost is the zero-DNS hostname for the server itself: "<dashed-ip>.sslip.io". ok is false when publicHost is not a public IP.
func SampleHost ¶
func SampleHost(publicHost string, c ProviderChoice) (string, bool)
SampleHost is the hostname a resolution test looks up for c.
func SanitizeDNSLabel ¶ added in v0.2.0
SanitizeDNSLabel is sanitizeDNSLabel for callers outside this package.
func ServedHosts ¶ added in v0.2.0
ServedHosts lists every hostname cfg routes or manages a certificate for.
func ServesHTTP ¶
func ServesHTTP(svc store.DesiredService) bool
ServesHTTP reports whether svc has a web port to route. An app whose only ports are published host ports (a game server) has no HTTP route, so it must not get an automatic hostname or a certificate for one.
func SetActiveCertStorage ¶
func SetActiveCertStorage(storage *SQLiteStorage)
SetActiveCertStorage registers storage as the backend the "sqlite" Caddy storage module resolves against (see NewSQLiteStorageRef). internal/reconcile/ingress.Controller's WithCertStore option calls this once per Controller, not once per reconcile: the whole point is that Caddy's repeated config reloads share the one already-open connection rather than each provisioning their own.
func SetDefaultGeoResolver ¶ added in v0.2.0
func SetDefaultGeoResolver(r *GeoResolver)
SetDefaultGeoResolver replaces the process-wide resolver (tests, reload).
func SetHTTPRedirectActive ¶ added in v0.2.0
func SetHTTPRedirectActive(on bool)
SetHTTPRedirectActive records whether the running ingress redirects plain HTTP to HTTPS itself, so the API can explain force HTTPS behavior.
func TopReasonCount ¶
func TopReasonCount() int
TopReasonCount is how many reasons the API lists per app.
func ValidateACMEContact ¶ added in v0.2.0
ValidateACMEContact checks the ACME account contact address a CA will accept.
func ValidateAuthGateURL ¶ added in v0.2.0
ValidateAuthGateURL checks an auth service URL: http or https, a host, no userinfo, query or fragment. Private and internal hosts are allowed.
func ValidateCustomBase ¶
ValidateCustomBase accepts a DNS name under a registrable domain: not an IP, not a wildcard, not a public suffix.
func ValidateTrustedProxies ¶
ValidateTrustedProxies checks a comma or space separated list of IPs, CIDRs and the private_ranges shorthand.
func ValidateWildcardDomain ¶
ValidateWildcardDomain rejects a malformed wildcard: a "*" anywhere but the single leading "*." label, or a base domain with no further label for a DNS-01 TXT record to attach under. Non-wildcard domains and syntactically valid wildcards both return nil.
Types ¶
type ACMEEventsApp ¶
type ACMEEventsApp struct{}
ACMEEventsApp subscribes the failure tracker to certificate events at provision time. Caddy starts apps in random order and the stock events app subscribes inside Start, racing the TLS app's async issuance jobs that read its subscriber map.
func (ACMEEventsApp) CaddyModule ¶
func (ACMEEventsApp) CaddyModule() caddy.ModuleInfo
CaddyModule returns the Caddy module information.
func (ACMEEventsApp) Provision ¶
func (ACMEEventsApp) Provision(ctx caddy.Context) error
Provision binds the tracker before any app starts.
func (ACMEEventsApp) Start ¶
func (ACMEEventsApp) Start() error
Start implements caddy.App; there is nothing to run.
type ACMEFailure ¶ added in v0.2.0
ACMEFailure is the last failed certificate attempt for one hostname.
type ACMEFailures ¶ added in v0.2.0
type ACMEFailures struct {
// contains filtered or unexported fields
}
ACMEFailures remembers each hostname's most recent failed obtain or renewal, so the API can show the real CA error instead of "pending". A later success clears the entry.
func DefaultACMEFailures ¶ added in v0.2.0
func DefaultACMEFailures() *ACMEFailures
DefaultACMEFailures is the process-wide tracker fed by Caddy's cert events.
func (*ACMEFailures) ACMEFailing ¶ added in v0.2.0
func (a *ACMEFailures) ACMEFailing(identifier string) (string, bool)
ACMEFailing returns the last error text for identifier; it satisfies alerting.ACMEFailureSource.
func (*ACMEFailures) Clear ¶ added in v0.2.0
func (a *ACMEFailures) Clear(identifier string)
Clear forgets identifier's failure.
func (*ACMEFailures) ClearAll ¶ added in v0.2.0
func (a *ACMEFailures) ClearAll()
ClearAll forgets every recorded failure, for when a proxy takes over TLS and earlier attempts no longer describe what visitors get.
func (*ACMEFailures) Get ¶ added in v0.2.0
func (a *ACMEFailures) Get(identifier string) (ACMEFailure, bool)
Get returns the last failure for identifier.
func (*ACMEFailures) Record ¶ added in v0.2.0
func (a *ACMEFailures) Record(f ACMEFailure)
Record stores a failure for identifier.
type ACMEIssuer ¶
type ACMEIssuer struct {
Module string `json:"module"`
// Email is the ACME account contact address, Caddy's own "email"
// field verbatim. Not technically required by the ACME protocol
// itself, but this codebase's own settings validation
// (internal/api) requires a non-empty, syntactically valid address
// whenever ACME is enabled: an unreachable ACME account is exactly
// the kind of "renewal fails silently" risk this project treats as
// its central one to catch before it bites a real user.
Email string `json:"email,omitempty"`
// CA is Caddy's own "ca" field: the ACME directory endpoint URL.
// Empty leaves Caddy's compiled-in default in place, Let's Encrypt's
// real production directory
// (https://acme-v02.api.letsencrypt.org/directory, per
// caddytls.ACMEIssuer's own doc comment). An operator who wants to
// avoid Let's Encrypt's production rate limits while testing should
// set this explicitly to Let's Encrypt's staging directory
// (https://acme-staging-v02.api.letsencrypt.org/directory); this
// package does not default to staging on its own, since silently
// issuing a staging (browser-untrusted) certificate for what an
// operator believes is a production toggle would be a worse
// surprise than requiring them to opt in explicitly.
CA string `json:"ca,omitempty"`
// Challenges is non-nil for wildcard subjects (NewDNSACMEIssuer sets
// DNS-01 here, RFC 8555 7.1.1 forbids HTTP-01 for a wildcard
// identifier) or when a non-default HTTP-01 port is configured
// (NewACMEIssuer's altHTTPPort parameter).
Challenges *ChallengesConfig `json:"challenges,omitempty"`
}
ACMEIssuer is Caddy's "acme" TLS issuer (tls.issuance.acme): real ACME issuance (Let's Encrypt or any RFC 8555-compatible CA), as opposed to InternalIssuer's fully offline, self-signed certificates. Field names and the "module" value ("acme", resolved within the tls.issuance namespace the same way InternalIssuer's "internal" is, per AutomationPolicy.Issuers' inline_key=module tag) are verified against the vendored source for the pinned caddyserver/caddy/v2 version this module uses (modules/caddytls/acmeissuer.go's ACMEIssuer struct and its CaddyModule ID "tls.issuance.acme"), not guessed: this package intentionally hand-rolls the wire-compatible subset of Caddy's own config types rather than importing them directly (see this file's package doc comment), and ACMEIssuer only needs the three fields an operator-facing settings form actually collects (email, an optional directory URL override, and the module discriminator), not the full surface (challenges config, account key, EAB, and so on) Caddy's own struct exposes for cases this codebase doesn't build a UI for yet.
ADR 005's Verified section is explicit that only the internal issuer path was proven end to end by that spike; this type exists to close that named, expected gap (see internal/api/certificates.go's own doc comment referencing the same open item), not to introduce a new architecture decision.
func NewACMEIssuer ¶
func NewACMEIssuer(email, directoryURL string, altHTTPPort int) ACMEIssuer
NewACMEIssuer returns the JSON shape for Caddy's real ACME issuer module, used as an AutomationPolicy's Issuers entry in place of NewInternalIssuer when an operator has opted into real ACME (internal/store.IngressSettings.ACMEEnabled). directoryURL empty means "use Caddy's own default" (see ACMEIssuer.CA's doc comment); email empty is accepted by this constructor (it does not itself enforce the non-empty rule), since that validation belongs to the caller that owns the operator-facing form (internal/api), not to this low-level config builder. altHTTPPort, if non-zero and not 80, sets Challenges.HTTP.AlternatePort (see HTTPChallengeConfig's doc comment) so a non-wildcard host's HTTP-01 challenge doesn't reach for port 80 on an instance configured to run its ingress on different ports.
func NewDNSACMEIssuer ¶ added in v0.2.0
func NewDNSACMEIssuer(email, directoryURL string, provider DNS01Provider) ACMEIssuer
NewDNSACMEIssuer returns the JSON shape for Caddy's real ACME issuer configured to solve the DNS-01 challenge via provider instead of Caddy's default HTTP-01, for wildcard subjects. email and directoryURL pass straight through to the same fields NewACMEIssuer sets. This package performs no validation on provider's credentials; internal/api owns that.
type AbuseHandler ¶
type AbuseHandler struct {
Handler string `json:"handler"`
App string `json:"app"`
Policy abuse.Policy `json:"policy"`
}
AbuseHandler is the wire shape of the levelrail_abuse handler.
type AbusePolicy ¶
AbusePolicy is one app's ingress abuse defense, applied to each of the app's proxied routes.
type AccessFilter ¶
AccessFilter narrows a ring read.
type AccessLog ¶
type AccessLog struct {
// contains filtered or unexported fields
}
AccessLog keeps the most recent requests per route key in process memory. Memory is bounded by hosts times capacity and the log is lost on restart, which is the point: it answers "what just happened", not history.
func DefaultAccessLog ¶
func DefaultAccessLog() *AccessLog
DefaultAccessLog is the process-wide log the request_stats handler feeds.
func NewAccessLog ¶
NewAccessLog returns a log holding capacity records per key; capacity <= 0 disables recording.
func (*AccessLog) CaptureIdentity ¶
CaptureIdentity turns on filling AccessRecord.Identity for subscribers. It is off by default so no client detail is read unless a sink opted in.
func (*AccessLog) FlushVisitorHistory ¶
FlushVisitorHistory writes the hour accumulators, drops closed hours and prunes old rows.
func (*AccessLog) FlushVisitorHistoryFinal ¶
FlushVisitorHistoryFinal flushes with its own short deadline for the shutdown path.
func (*AccessLog) IdentityWanted ¶
IdentityWanted reports whether callers should fill AccessRecord.Identity.
func (*AccessLog) Query ¶
func (l *AccessLog) Query(keys []string, f AccessFilter) []AccessRecord
Query returns matching records for the given keys, newest first.
func (*AccessLog) Record ¶
func (l *AccessLog) Record(key string, rec AccessRecord)
Record stores one request under key.
func (*AccessLog) RunVisitorHistory ¶
RunVisitorHistory flushes every interval until ctx ends. The caller flushes once more after the server drains so a clean shutdown loses nothing.
func (*AccessLog) SetRetentionSource ¶
SetRetentionSource makes reads ask fn for the window, so a saved setting applies to the next read.
func (*AccessLog) SetVisitorHistory ¶
func (l *AccessLog) SetVisitorHistory(st VisitorHistoryStore, owner func(string) string, retention func() time.Duration, logger *slog.Logger)
SetVisitorHistory turns on hourly rollups persisted to st. owner maps a route key to its app; retention is read at every flush, and 0 keeps nothing.
func (*AccessLog) SetVisitorRotationSource ¶
SetVisitorRotationSource makes the salt period follow a saved setting.
func (*AccessLog) Since ¶
func (l *AccessLog) Since(keys []string, since time.Time) []AccessRecord
Since returns every retained record for the given keys at or after since, oldest first. Unlike Query it is not capped, because a replay needs them all.
func (*AccessLog) Subscribe ¶
func (l *AccessLog) Subscribe(fn func(key string, rec AccessRecord)) (unsubscribe func())
Subscribe registers fn to see every recorded request and returns a func that removes it. fn runs on the request path, so it must never block.
func (*AccessLog) Summarize ¶
func (l *AccessLog) Summarize(keys []string, o VisitorSummaryOptions) VisitorSummary
Summarize aggregates retained records for keys into visitor counts, status classes, top pages and referrers, and a fixed-step series over [Since, Until].
func (*AccessLog) SummarizeWithHistory ¶
func (l *AccessLog) SummarizeWithHistory(ctx context.Context, app string, keys []string, o VisitorSummaryOptions) VisitorSummary
SummarizeWithHistory merges stored rollups with the live ring so long windows survive a restart. A visitor seen in both parts within one salt period can be counted twice; the figures are approximate.
type AccessRecord ¶
type AccessRecord struct {
Time time.Time `json:"time"`
Host string `json:"host"`
Method string `json:"method"`
Path string `json:"path"`
Status int `json:"status"`
Millis float64 `json:"duration_ms"`
BytesIn int64 `json:"bytes_in"`
BytesOut int64 `json:"bytes_out"`
RequestID string `json:"request_id"`
TraceID string `json:"trace_id,omitempty"`
Upstream bool `json:"upstream_error,omitempty"`
Cold bool `json:"cold_start,omitempty"`
Identity *RequestIdentity `json:"-"`
// Client and UserAgent stay in process memory for the abuse defense dry
// run. They are never serialized, so no read API can return them.
Client netip.Addr `json:"-"`
UserAgent string `json:"-"`
// Referrer is the Referer host only, kept in memory for the visitor summary and never serialized.
Referrer string `json:"-"`
}
AccessRecord is one finished edge request. The query string is never captured. Client and UserAgent stay in process memory for the abuse defense replay and are never serialized; Identity reaches export subscribers only.
type ActiveHealthChecks ¶ added in v0.2.0
type ActiveHealthChecks struct {
URI string `json:"uri,omitempty"`
Interval string `json:"interval,omitempty"`
Timeout string `json:"timeout,omitempty"`
Passes int `json:"passes,omitempty"`
Fails int `json:"fails,omitempty"`
ExpectStatus int `json:"expect_status,omitempty"`
}
ActiveHealthChecks is Caddy's health_checks.active block.
type ActiveSplit ¶
type ActiveSplit struct {
TestID string
CookieName string
CookieTTL time.Duration
OverrideParam string
Variants []splittest.Variant
VariantHosts map[string]string
}
ActiveSplit is one running split test as ingress sees it. VariantHosts maps a variant ID to the public host serving it; the production variant has none and is served by the route the split sits in.
type AdminConfig ¶
type AdminConfig struct {
// Listen is the admin API bind address. Empty means Caddy's default,
// localhost:2019.
Listen string `json:"listen,omitempty"`
// Disabled, if true, turns the admin listener off entirely. This
// spike leaves it false (the real default) so the admin endpoint
// actually comes up, since a production driver would want it
// reachable for introspection even though this package itself talks
// to Caddy via Go function calls (caddy.Load), not HTTP requests to
// that listener.
Disabled bool `json:"disabled,omitempty"`
// Config holds admin-level config-management settings, notably
// Persist. See AdminConfigSettings.
Config *AdminConfigSettings `json:"config,omitempty"`
}
AdminConfig configures Caddy's admin API endpoint. Leaving Listen empty (the zero value) means "don't set this field" (omitempty), which lets Caddy apply its own default of localhost:2019 rather than us duplicating that default here.
type AdminConfigSettings ¶
type AdminConfigSettings struct {
// Persist controls whether Caddy keeps its own on-disk copy of
// whatever config was last pushed to it (Caddy's default is true,
// written to a fixed OS-level app-data path independent of the
// storage module configured for certificates, see FileStorage's doc
// comment). This spike found that surprising: it means an embedded
// Caddy instance maintains a second, out-of-band record of its own
// config on disk even when the storage module is pointed elsewhere,
// which is exactly the "second config surface" ADR 005 argues
// against embedding Caddy specifically to avoid. This spike's
// builder pins it to false: the platform's own reconcile loop and
// database are the single source of truth for desired ingress
// state, not a file Caddy manages on its own.
Persist *bool `json:"persist,omitempty"`
}
AdminConfigSettings mirrors Caddy's admin.config JSON field.
type Apps ¶
type Apps struct {
HTTP *HTTPApp `json:"http,omitempty"`
TLS *TLSApp `json:"tls,omitempty"`
PKI *PKIApp `json:"pki,omitempty"`
// Layer4 carries every raw TCP stream (internal/reconcile/ingress's
// StreamRoute), applied in the exact same Config/caddy.Load call as
// HTTP/TLS/PKI above: see layer4.go's own package doc comment for why
// a stream can't be a second, independently-applied Caddy config.
Layer4 *Layer4App `json:"layer4,omitempty"`
// ACMEEvents feeds certificate success and failure events to DefaultACMEFailures.
ACMEEvents *ACMEEventsApp `json:"acme_events,omitempty"`
}
Apps holds the top-level Caddy apps this package configures.
type AuditRecorder ¶ added in v0.2.0
type AuditRecorder interface {
SaveAuditEntry(ctx context.Context, e store.AuditEntry) error
}
AuditRecorder is the narrow surface SQLiteStorage needs to record a certificate issuance or renewal as a system-driven audit_log row. *store.DB satisfies this via the same SaveAuditEntry internal/api's requireAbility already calls for every request-driven row; a background ACME renewal has no request behind it, so this is the one other call site audit_log ever gets written from.
type AuthGate ¶ added in v0.2.0
type AuthGate struct {
URL string
CheckPath string
CopyHeaders []string
Timeout time.Duration
// SetHeaders are set on the subrequest only, overwriting client values.
// Internal use (built-in provider); values may hold Caddy placeholders.
SetHeaders map[string]string
}
AuthGate is one domain's forward-auth check, run before the request reaches the backend.
type AutoHTTPSConfig ¶
type AutoHTTPSConfig struct {
Disabled bool `json:"disable,omitempty"`
DisableRedir bool `json:"disable_redirects,omitempty"`
}
AutoHTTPSConfig mirrors Caddy's automatic_https server field. See the Server.AutomaticHTTPS doc comment for why this spike sets DisableRedir.
type AutoHostOptOutSource ¶
type AutoHostOptOutSource interface {
ListAppAutoHostOptOuts(ctx context.Context) (map[string]bool, error)
}
AutoHostOptOutSource is the optional store surface for per-app opt-outs; *store.DB implements it.
type Automation ¶
type Automation struct {
Policies []AutomationPolicy `json:"policies,omitempty"`
}
Automation holds the ordered list of automation policies. The first policy whose Subjects match a given hostname wins.
type AutomationPolicy ¶
type AutomationPolicy struct {
Subjects []string `json:"subjects,omitempty"`
Issuers []any `json:"issuers,omitempty"`
// OnDemand defers issuance to the first TLS handshake for a subject. Subjects here are exact hosts, which is the allowlist.
OnDemand bool `json:"on_demand,omitempty"`
}
AutomationPolicy binds a set of subjects (hostnames) to the issuer(s) allowed to obtain certificates for them.
type BasicAuthAccount ¶
type BasicAuthAccount struct {
Username string `json:"username"`
Password string `json:"password"`
}
BasicAuthAccount is one entry in HTTPBasicAuthProvider.Accounts. Password is always a bcrypt hash, never the plaintext credential: see NewBasicAuthHandler.
type BasicAuthHandler ¶
type BasicAuthHandler struct {
Handler string `json:"handler"`
Providers BasicAuthProviders `json:"providers"`
}
BasicAuthHandler is Caddy's "authentication" handler (http.handlers.authentication) configured with the http_basic provider (http.authentication.providers.http_basic): per-route HTTP Basic Auth checked against a bcrypt hash. A Route places this ahead of a ReverseProxyHandler in Handle so Caddy's own handler chain short-circuits with 401 before the request ever reaches the backend.
func NewBasicAuthHandler ¶
func NewBasicAuthHandler(username, bcryptHash string) BasicAuthHandler
NewBasicAuthHandler builds Caddy's http_basic authentication handler for a single account. bcryptHash must already be a bcrypt hash (bcrypt.GenerateFromPassword); this function never sees or hashes a plaintext password itself.
type BasicAuthHash ¶
type BasicAuthHash struct {
Algorithm string `json:"algorithm"`
}
BasicAuthHash selects the password hashing algorithm HTTPBasicAuthProvider checks Accounts against. This package only ever sets "bcrypt", the same algorithm internal/api already uses for user login passwords (golang.org/x/crypto/bcrypt).
type BasicAuthProviders ¶
type BasicAuthProviders struct {
HTTPBasic *HTTPBasicAuthProvider `json:"http_basic"`
}
BasicAuthProviders holds the one provider this package configures.
type CA ¶
type CA struct {
// InstallTrust controls whether Caddy attempts to install this CA's
// root certificate into the local OS/browser trust store on first
// use. Caddy's own default is true, which on this spike's dev
// machine meant an unprompted `sudo` invocation that failed
// noisily (no interactive terminal, no `certutil`) but did not
// block certificate issuance. That's tolerable on a developer
// laptop; it is the wrong default for a headless server process,
// which has no desktop trust store to install into and should
// never attempt an interactive sudo prompt at startup. This spike's
// TLS builder pins it to false explicitly rather than relying on
// Caddy's default.
InstallTrust *bool `json:"install_trust,omitempty"`
}
CA configures a single certificate authority managed by the pki app.
type CARetryBackoff ¶
CARetryBackoff supplies the base and cap of the wait before an identical failed CA switch is retried; nil falls back to one minute and one hour.
type CacheBucket ¶ added in v0.2.0
type CacheBucket struct {
Minute int64 `json:"minute"`
Hits uint64 `json:"hits"`
Misses uint64 `json:"misses"`
HitBytes uint64 `json:"hit_bytes"`
MissBytes uint64 `json:"miss_bytes"`
}
CacheBucket is one minute of cache activity for the dashboard sparkline.
type CacheHandler ¶ added in v0.2.0
type CacheHandler struct {
Handler string `json:"handler"`
Key string `json:"key"`
Rules []trafficpolicy.CacheRule `json:"rules"`
MaxObjectBytes int64 `json:"max_object_bytes"`
MaxTTLSeconds int `json:"max_ttl_seconds"`
}
CacheHandler is the wire shape of the levelrail_cache handler.
type CacheReason ¶
type CacheReason struct {
Code string `json:"code"`
Detail string `json:"detail,omitempty"`
Count uint64 `json:"count"`
}
CacheReason is one reason with how often it applied. Detail carries the Vary header name for ReasonVaryHeader.
type CacheStats ¶ added in v0.2.0
type CacheStats struct {
Hits uint64 `json:"hits"`
Misses uint64 `json:"misses"`
Bypasses uint64 `json:"bypasses"`
Stale uint64 `json:"stale"`
// HitBytes were served from the cache; OriginBytes were fetched from the
// app on cacheable paths. HitBytes is the bandwidth the app was spared.
HitBytes uint64 `json:"hit_bytes"`
OriginBytes uint64 `json:"origin_bytes"`
Entries int `json:"entries"`
Bytes int64 `json:"bytes"`
Series []CacheBucket `json:"series"`
// Reasons counts responses and requests left out of the cache, most
// common first.
Reasons []CacheReason `json:"reasons"`
}
CacheStats is one domain's cache activity since the process started.
func MergeCacheStats ¶
func MergeCacheStats(list ...CacheStats) CacheStats
MergeCacheStats sums several domains' stats into one view. Series align by minute because every domain reports the same trailing window.
func (CacheStats) HitRate ¶
func (s CacheStats) HitRate() float64
HitRate is the share of cacheable requests answered from the cache (fresh or stale hits over hits plus misses); bypasses are not counted.
func (CacheStats) Tail ¶
func (s CacheStats) Tail(minutes int) (hits, misses, hitBytes, missBytes uint64)
Tail returns the totals over the last minutes of the series.
type CertKeyPEMPair ¶
type CertKeyPEMPair struct {
CertificatePEM string `json:"certificate"`
KeyPEM string `json:"key"`
Tags []string `json:"tags,omitempty"`
}
CertKeyPEMPair mirrors Caddy's caddytls.CertKeyPEMPair (tls.certificates.load_pem's wire shape, verified against modules/caddytls/pemloader.go in the vendored caddyserver/caddy/v2 source): a certificate and its private key as inline PEM text, no file on disk for either. Loading a certificate this way is also what makes Caddy skip automatic ACME/internal issuance for a matching hostname on its own (caddyhttp.AutoHTTPSConfig.IgnoreLoadedCerts defaults to false): no separate "skip" list is needed in Server.AutomaticHTTPS.
type CertPurgeMarkerStore ¶ added in v0.2.0
type CertPurgeMarkerStore interface {
CertPurgeStore
GetCertStorageValue(ctx context.Context, key string) (*store.CertStorageValue, error)
SaveCertStorageValue(ctx context.Context, key string, value []byte) error
}
CertPurgeMarkerStore adds the marker read and write PurgeCertsFromOtherIssuersOnce needs.
type CertPurgeStore ¶ added in v0.2.0
type CertPurgeStore interface {
ListCertStorageKeys(ctx context.Context, prefix string, recursive bool) ([]string, error)
DeleteCertStorageValue(ctx context.Context, key string) error
}
CertPurgeStore is the storage surface PurgeCertsFromOtherIssuers needs.
type CertStore ¶
type CertStore interface {
SaveCertStorageValue(ctx context.Context, key string, value []byte) error
GetCertStorageValue(ctx context.Context, key string) (*store.CertStorageValue, error)
DeleteCertStorageValue(ctx context.Context, key string) error
ExistsCertStorageValue(ctx context.Context, key string) (bool, error)
ListCertStorageKeys(ctx context.Context, prefix string, recursive bool) ([]string, error)
StatCertStorageValue(ctx context.Context, key string) (*store.CertStorageKeyInfo, error)
AcquireCertStorageLock(ctx context.Context, name string, staleAfter time.Duration) (bool, error)
TouchCertStorageLock(ctx context.Context, name string) error
ReleaseCertStorageLock(ctx context.Context, name string) error
}
CertStore is the narrow surface SQLiteStorage needs from internal/store's SQLite-backed *store.DB, so tests can fake it without a real database. *store.DB satisfies this.
type CertificatesConfig ¶
CertificatesConfig is the wire shape for TLSApp.Certificates: each key names a tls.certificates.* loader module, value is that module's own JSON shape (typed any like Route.Handle, for the same reason: Caddy's module system is inherently polymorphic).
type ChallengesConfig ¶
type ChallengesConfig struct {
DNS *DNSChallengeConfig `json:"dns,omitempty"`
HTTP *HTTPChallengeConfig `json:"http,omitempty"`
}
ChallengesConfig mirrors Caddy's caddytls.ChallengesConfig, scoped to the two fields this package sets: DNS and HTTP.
type ClientIPMatcher ¶
type ClientIPMatcher struct {
Ranges []string `json:"ranges"`
}
ClientIPMatcher mirrors Caddy's http.matchers.client_ip, which honours the server's trusted proxies.
type CloudflareDNSProvider ¶
CloudflareDNSProvider is the wire shape for Caddy's Cloudflare DNS provider module (dns.providers.cloudflare, github.com/caddy-dns/cloudflare wrapping github.com/libdns/cloudflare), used as a DNSChallengeConfig's Provider. APIToken must be a scoped Cloudflare API token (Zone:DNS:Edit), never the global API key.
type Config ¶
type Config struct {
Admin *AdminConfig `json:"admin,omitempty"`
// Storage selects the Caddy storage module certificates, ACME
// account state, and the internal issuer's local CA are persisted
// through. Typed any rather than a single concrete struct for the
// same reason Route.Handle and AutomationPolicy.Issuers already are
// in this file: it is a Caddy module reference, and Caddy's module
// system is inherently polymorphic (any value marshaling to
// {"module": "<id>", ...} is valid here). *FileStorage (the local
// filesystem, via NewFileStorage) and SQLiteStorageRef
// (internal/store's SQLite, via NewSQLiteStorageRef)
// are this package's two concrete options as of this writing; a nil
// Storage leaves Caddy's own OS-specific default in place.
Storage any `json:"storage,omitempty"`
Apps Apps `json:"apps"`
// Logging taps certificate-issuance errors; see newACMELogTap.
Logging *LoggingConfig `json:"logging,omitempty"`
// contains filtered or unexported fields
}
Config is the root of a Caddy JSON config document (https://caddyserver.com/docs/json/), scoped to the subset this spike exercises: the admin endpoint, the http app, and the tls app.
func BuildProxyConfig ¶
func BuildProxyConfig(opts ProxyOptions) (*Config, error)
BuildProxyConfig builds a Config with exactly one server and one route: reverse-proxy every matching request to opts.BackendDial. This is the pure, no-Caddy-required half of the spike, table-tested in config_test.go; driver_test.go covers the half that actually starts Caddy and proxies a real request through it.
func BuildRoutesConfig ¶
func BuildRoutesConfig(opts RoutesOptions) (*Config, error)
BuildRoutesConfig builds a Config with one server carrying one route per entry in opts.Routes (reverse_proxy), opts.StaticRoutes (file_server), opts.MaintenanceRoutes (static_response), and opts.RedirectRoutes (static_response with a Location header), each matched by its own Hosts. All four kinds share the same listener and the same TLS automation policy; nothing about a static, maintenance, or redirect route requires a different Server or a second Caddy config document. All four being empty is valid and produces a Config with a listener but no routes and no TLS app: this is the normal shape for a reconcile pass over zero currently-routable resources (every known service or static site either declares no domains or, for a container service, has no running container yet, and no domain is in maintenance mode or redirected), not an error.
type CorazaWAFHandler ¶ added in v0.2.0
type CorazaWAFHandler struct {
Handler string `json:"handler"`
// Directives is a ModSecurity/Coraza directives block, evaluated
// verbatim. NewCorazaWAFHandler always sets SecRuleEngine plus the
// three @-prefixed OWASP CRS includes LoadOWASPCRS makes resolvable.
Directives string `json:"directives"`
// LoadOWASPCRS makes the coraza-coreruleset ruleset (compiled into
// this binary via the blank import in driver.go) resolvable from
// Directives' @-prefixed Include paths. NewCorazaWAFHandler always
// sets this true; there is no per-domain custom-ruleset authoring in
// this package, only the stock OWASP CRS.
LoadOWASPCRS bool `json:"load_owasp_crs"`
}
CorazaWAFHandler is Caddy's OWASP Coraza WAF handler (http.handlers.waf, github.com/corazawaf/coraza-caddy/v2). Handler is the literal string "waf": the module's own CaddyModule ID, distinct from its Caddyfile directive name "coraza_waf".
func NewCorazaWAFHandler ¶ added in v0.2.0
func NewCorazaWAFHandler(blocking bool) CorazaWAFHandler
NewCorazaWAFHandler builds the OWASP Coraza handler for a domain that opted into the WAF. blocking selects SecRuleEngine On (reject on match) versus DetectionOnly (log on match, never reject); see docs/domains-and-ingress.md for why detection-only is this platform's recommended first-enable default.
type CountedItem ¶
CountedItem is one ranked label.
type DNS01Provider ¶ added in v0.2.0
type DNS01Provider interface {
// contains filtered or unexported methods
}
DNS01Provider is implemented by every ACME DNS-01 challenge provider's wire-shape type (CloudflareDNSProvider, Route53DNSProvider), letting NewDNSACMEIssuer build a wildcard automation policy from whichever provider an operator has configured without a provider-specific issuer constructor per provider. Cloudflare is this interface's first implementation, not a hardcoded special case: adding another provider is exactly what Route53DNSProvider below does.
type DNSChallengeConfig ¶
type DNSChallengeConfig struct {
// Provider is a dns.providers.* module reference, typed any like
// Route.Handle. Its inline_key is "name", not "module" (per
// caddytls.DNSChallengeConfig.ProviderRaw's struct tag).
Provider any `json:"provider"`
}
DNSChallengeConfig mirrors Caddy's caddytls.DNSChallengeConfig, scoped to the one field this package sets: Provider.
type DNSProvider ¶
type DNSProvider struct {
ID string `json:"id"`
Name string `json:"name"`
Suffix string `json:"suffix"`
IPFormat IPFormat `json:"ip_format"`
// IPv4Only providers have no known IPv6 mapping, so an IPv6 public address yields no hostname.
IPv4Only bool `json:"ipv4_only"`
Note string `json:"note"`
}
DNSProvider is one free wildcard-DNS service that maps a hostname with an embedded IP to that IP.
func DNSProviders ¶
func DNSProviders() []DNSProvider
DNSProviders returns the built-in providers in display order.
func LookupDNSProvider ¶
func LookupDNSProvider(id string) (DNSProvider, bool)
LookupDNSProvider finds a built-in provider by id, case-insensitively.
type DomainVisitors ¶
type DomainVisitors struct {
Domain string `json:"domain"`
Requests int `json:"requests"`
Visitors int `json:"visitors"`
}
DomainVisitors is the per-domain split of a summary.
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver drives an in-process Caddy instance through the same code path as its HTTP admin API: caddy.Load calls the identical config-apply logic the admin API's POST /load handler calls, without this package ever shelling out to a `caddy` binary or running one as a subprocess or sibling container. The embedded-ingress design requires this shape specifically: ingress state lives in the control plane's own process, not in something else's.
func New ¶
New builds a Driver. A nil logger falls back to slog.Default(), matching the convention in internal/reconcile.Engine.
func (*Driver) Apply ¶
Apply marshals cfg to Caddy's JSON config format and loads it into the running (package-global) Caddy instance, starting it on first call. Caddy's config model has no notion of "the config for this Driver" versus "the config for some other caller in the process": caddy.Load replaces the entire process-wide config, which is a real constraint Phase 1 needs to design around once ingress config is built incrementally from many apps rather than handed over as one document per call.
func (*Driver) OwnsPort ¶ added in v0.2.0
OwnsPort reports whether the most recently applied config bound an HTTP server to port. Used by GET /api/v1/system/doctor (internal/api) to tell this control plane's own ingress apart from an unrelated process holding the same port.
func (*Driver) RestoreSnapshot ¶
RestoreSnapshot loads the saved config into Caddy so the listeners serve before the first reconcile. A missing, corrupt or stale snapshot is not an error for the caller: it returns false and the normal reconcile path runs.
func (*Driver) SaveSnapshotNow ¶
SaveSnapshotNow writes the currently applied config to the snapshot file.
func (*Driver) SetCARetryBackoff ¶
func (d *Driver) SetCARetryBackoff(f CARetryBackoff)
SetCARetryBackoff makes the backoff a live runtime setting.
func (*Driver) SetSnapshot ¶
func (d *Driver) SetSnapshot(store *SnapshotStore, fingerprint string)
SetSnapshot makes Apply persist every config it loads and enables RestoreSnapshot. fingerprint is InheritedSockets.Fingerprint.
func (*Driver) SnapshotStatus ¶
func (d *Driver) SnapshotStatus() SnapshotStatus
SnapshotStatus reports the snapshot state for doctor.
func (*Driver) Stop ¶
Stop tears down the running Caddy instance, releasing every listener it holds. Safe to call even if Apply was never called.
func (*Driver) VerifySnapshot ¶
func (d *Driver) VerifySnapshot() (SnapshotCheck, error)
VerifySnapshot opens the snapshot the way a restart would, without loading it into Caddy.
type EdgeOverrides ¶
type EdgeOverrides struct {
TrustedProxies string
MaxHeaderBytes *int64
MaxBodyBytes *int64
ReadHeaderTimeoutSecs *int64
IdleTimeoutSecs *int64
}
EdgeOverrides are dashboard-managed edge values. Each applies only when its APP_INGRESS_* variable is unset, so an env value always wins.
type EncodeHandler ¶
type EncodeHandler struct {
Handler string `json:"handler"`
Encodings map[string]struct{} `json:"encodings"`
Prefer []string `json:"prefer"`
MinLength int `json:"minimum_length"`
}
EncodeHandler is Caddy's "encode" handler (http.handlers.encode).
func NewStaticEncodeHandler ¶
func NewStaticEncodeHandler() EncodeHandler
NewStaticEncodeHandler compresses with zstd or gzip. Caddy's default matcher only selects text types, so images, fonts and archives pass through.
type ErrorPage ¶ added in v0.2.0
ErrorPage is one status-code-to-body mapping for a domain (store.DomainErrorPage), threaded through from internal/reconcile/ingress into a route's Handle chain.
type ErrorsConfig ¶ added in v0.2.0
type ErrorsConfig struct {
Routes []Route `json:"routes,omitempty"`
}
ErrorsConfig mirrors Caddy's per-subroute "errors" field (the JSON shape behind the Caddyfile's handle_errors directive): Routes runs after the primary routes return an error, each matched by its own Matcher against {http.error.status_code}.
type ExpressionMatcher ¶ added in v0.2.0
type ExpressionMatcher struct {
Expr string `json:"expr"`
// Name is always emitted: Caddy 2.11's MatchExpression.UnmarshalJSON
// type-asserts it and panics the whole process when it is absent.
Name string `json:"name"`
}
ExpressionMatcher mirrors Caddy's http.matchers.expression module: a CEL expression evaluated with Caddy's placeholders expanded first.
type FallbackResolver ¶
type FallbackResolver struct {
// contains filtered or unexported fields
}
FallbackResolver derives automatic hostnames from the instance default plus per-app overrides.
func LoadFallbackResolver ¶
func LoadFallbackResolver(ctx context.Context, src any, publicHost string) FallbackResolver
LoadFallbackResolver reads the choices from src. A source that does not implement ProviderSource, or a read error, degrades to the built-in default so hostnames never disappear over a settings read.
func NewFallbackResolver ¶
func NewFallbackResolver(publicHost string, def ProviderChoice, overrides map[string]ProviderChoice) FallbackResolver
NewFallbackResolver builds a resolver from explicit choices.
func (FallbackResolver) Choice ¶
func (r FallbackResolver) Choice(app string) (ProviderChoice, bool)
Choice is the effective choice for app and whether it is a per-app override.
func (FallbackResolver) Default ¶
func (r FallbackResolver) Default() ProviderChoice
Default is the instance-wide choice.
func (FallbackResolver) Domain ¶
func (r FallbackResolver) Domain(app string) (string, bool)
Domain is the automatic hostname for app under its effective provider.
func (FallbackResolver) OptedOut ¶
func (r FallbackResolver) OptedOut(app string) bool
OptedOut reports whether app has no automatic hostname by choice.
type FileServerHandler ¶
type FileServerHandler struct {
Handler string `json:"handler"`
// Root is the local filesystem directory to serve, e.g.
// "/var/lib/levelrail-data/static/docs/abc1234". Caddy resolves
// requests underneath it directly; there is no upstream to dial and
// nothing here to route through Docker.
Root string `json:"root,omitempty"`
}
FileServerHandler is Caddy's "file_server" handler (http.handlers.file_server): serves files directly from Root, no backend, no container. This is the whole of what this design means by "static sites get served by the embedded Caddy directly with no container": a route whose Handle is this instead of a ReverseProxyHandler. Registered via the same modules/standard blank import driver.go already pulls in for reverse_proxy and the TLS issuers, no separate module wiring needed.
func NewFileServerHandler ¶
func NewFileServerHandler(root string) FileServerHandler
NewFileServerHandler builds the one handler shape a static route needs: serve files from root for everything matched by the route.
type FileStorage ¶
FileStorage is Caddy's default storage module (caddy.storage.file_system): certificates, ACME account state, and the internal issuer's local CA all live under Root. Caddy's own default, when Storage is left nil, is an OS-specific application-data directory outside the project entirely (e.g. ~/Library/Application Support/Caddy on macOS) which is the right thing for the `caddy` CLI on a developer's own machine, and the wrong thing for an embedded control plane: it scatters state outside the platform's own data directory and, worse, is shared and reused across every unrelated process on the machine that also happens to embed Caddy with default settings.
func NewFileStorage ¶
func NewFileStorage(dir string) *FileStorage
NewFileStorage returns the JSON shape for Caddy's file-system storage module rooted at dir. Module is "file_system", not the fully-qualified "caddy.storage.file_system": the "storage" field resolves modules within the caddy.storage namespace already (see StorageRaw's caddy struct tag in caddy's own Config type), so the namespace prefix here would be doubled up if included, a subtle enough detail to call out explicitly.
type FirewallHandler ¶
type FirewallHandler struct {
Handler string `json:"handler"`
}
FirewallHandler is the wire shape of the levelrail_firewall handler, which sits in front of every host route and runs the instance firewall.
func NewFirewallHandler ¶
func NewFirewallHandler() FirewallHandler
NewFirewallHandler builds the server-wide firewall handler.
type GeoHandler ¶ added in v0.2.0
type GeoHandler struct {
Handler string `json:"handler"`
Rule trafficpolicy.Geo `json:"rule"`
}
GeoHandler is the wire shape of the levelrail_geo handler.
type GeoResolver ¶ added in v0.2.0
type GeoResolver struct {
// contains filtered or unexported fields
}
GeoResolver maps a client address to an ISO country code. Safe for concurrent use; the MMDB reader is read only after Open.
func DefaultGeoResolver ¶ added in v0.2.0
func DefaultGeoResolver() *GeoResolver
DefaultGeoResolver is the process-wide resolver the geo handler uses; Caddy builds handlers from JSON and cannot receive one any other way.
func NewGeoResolver ¶ added in v0.2.0
func NewGeoResolver(getenv func(string) string) *GeoResolver
NewGeoResolver builds a resolver from the environment. source is auto (header when trusted proxies are configured, MMDB when APP_GEOIP_DB loads), header, mmdb or none.
func (*GeoResolver) Country ¶ added in v0.2.0
func (g *GeoResolver) Country(addr netip.Addr, headerValue string, trustedPeer bool) (country, source string)
Country resolves addr. headerValue is the country header's value and trustedPeer whether the connection came from a trusted proxy; the header is ignored otherwise, since any client can send it.
func (*GeoResolver) HeaderName ¶ added in v0.2.0
func (g *GeoResolver) HeaderName() string
HeaderName is the country header honoured from trusted proxies.
func (*GeoResolver) Status ¶ added in v0.2.0
func (g *GeoResolver) Status() GeoStatus
Status reports the active sources.
type GeoStatus ¶ added in v0.2.0
type GeoStatus struct {
Active bool `json:"active"`
Sources []string `json:"sources"`
Header string `json:"header,omitempty"`
HeaderNeeds string `json:"header_needs,omitempty"`
DBPath string `json:"db_path,omitempty"`
DBType string `json:"db_type,omitempty"`
DBBuildEpoch uint `json:"db_build_epoch,omitempty"`
Error string `json:"error,omitempty"`
}
GeoStatus says which country sources are active and why.
type GoogleCloudDNSModule ¶
type GoogleCloudDNSModule struct {
ServiceAccountJSON string `json:"service_account_json,omitempty"`
// contains filtered or unexported fields
}
GoogleCloudDNSModule is the Caddy DNS provider module backed by dnsrecords.ZoneManager, so Cloud DNS needs no third-party libdns module.
func (*GoogleCloudDNSModule) AppendRecords ¶
func (m *GoogleCloudDNSModule) AppendRecords(ctx context.Context, zone string, recs []libdns.Record) ([]libdns.Record, error)
AppendRecords implements libdns.RecordAppender.
func (*GoogleCloudDNSModule) CaddyModule ¶
func (*GoogleCloudDNSModule) CaddyModule() caddy.ModuleInfo
CaddyModule implements caddy.Module.
type GoogleCloudDNSProvider ¶
type GoogleCloudDNSProvider struct {
Name string `json:"name"`
ServiceAccountJSON string `json:"service_account_json"`
}
GoogleCloudDNSProvider is the wire shape for the in-repo Caddy module dns.providers.googleclouddns, used as a DNSChallengeConfig's Provider. ServiceAccountJSON needs the dns.admin or DNS Administrator role on the project.
type HTTPApp ¶
type HTTPApp struct {
Servers map[string]*Server `json:"servers,omitempty"`
// HTTPPort mirrors Caddy's own top-level "http_port" field: the port
// automatic-HTTPS features (an enabled redirect, or an ACME HTTP-01
// challenge with no AlternatePort set) fall back to. 0 (omitempty)
// keeps Caddy's compiled-in default, port 80.
HTTPPort int `json:"http_port,omitempty"`
// GracePeriod mirrors Caddy's grace_period: how long in-flight requests
// get to finish on shutdown or config replacement.
GracePeriod string `json:"grace_period,omitempty"`
}
HTTPApp is Caddy's "http" app: one or more named servers, each with its own listeners and routes.
type HTTPBasicAuthProvider ¶
type HTTPBasicAuthProvider struct {
Accounts []BasicAuthAccount `json:"accounts"`
Hash BasicAuthHash `json:"hash"`
}
HTTPBasicAuthProvider mirrors Caddy's caddyauth.HTTPBasicAuth: a fixed account list checked against Hash.
type HTTPChallengeConfig ¶ added in v0.2.0
type HTTPChallengeConfig struct {
AlternatePort int `json:"alternate_port,omitempty"`
Disabled bool `json:"disabled,omitempty"`
}
HTTPChallengeConfig mirrors Caddy's caddytls.HTTPChallengeConfig, scoped to the one field this package sets: AlternatePort. Without it, certmagic's HTTP-01 solver binds port 80 directly (github.com/caddyserver/certmagic's own default), independent of HTTPApp.HTTPPort, whenever Caddy's http app has no server already listening on port 80 to serve the challenge through (true here: see Server.AutomaticHTTPS's DisableRedir comment, this package never stands up that server). AlternatePort redirects that bind to whatever port this control plane's own ingress is actually configured for.
type HTTPTransport ¶ added in v0.2.0
type HTTPTransport struct {
Protocol string `json:"protocol"`
TLS *UpstreamTLSConfig `json:"tls,omitempty"`
DialTimeout string `json:"dial_timeout,omitempty"`
ResponseHeaderTimeout string `json:"response_header_timeout,omitempty"`
}
HTTPTransport is Caddy's reverse_proxy http transport.
type Hardening ¶ added in v0.2.0
type Hardening struct {
ReadHeaderTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
MaxBodyBytes int64
Protocols []string
TrustedProxies []string
ClientIPHeaders []string
RetryWindow time.Duration
RetryInterval time.Duration
DialTimeout time.Duration
FailDuration time.Duration
RetryAfterSeconds int
// GracePeriod lets in-flight requests finish when the config is replaced or
// the process stops. Zero closes them at once.
GracePeriod time.Duration
// BrandName and DocsURL label the diagnostic page for unmatched hosts and
// backendless apps; both come from the brand layer.
BrandName string
DocsURL string
}
Hardening is the edge policy applied to every proxied route and to the shared HTTP server: slowloris and size limits, protocols, trusted proxies, and the failover behaviour that replaces a raw 502 with a friendly 503.
func DefaultHardening ¶ added in v0.2.0
func DefaultHardening() Hardening
DefaultHardening returns defaults that are safe for general web apps: no whole-request read or write deadline (uploads and SSE must keep working), but bounded headers, idle connections and dial time.
func HardeningFromEnv ¶ added in v0.2.0
HardeningFromEnv reads every APP_INGRESS_* tuning variable over the defaults.
func (Hardening) UnavailableResponse ¶ added in v0.2.0
func (h Hardening) UnavailableResponse(title, message string) StaticResponseHandler
UnavailableResponse is the friendly 503 served when no backend can answer right now: it carries Retry-After, is never cached, and refreshes itself.
func (Hardening) WithCloudflarePreset ¶
WithCloudflarePreset adds Cloudflare's ranges to the trusted proxies and reads the client address from CF-Connecting-IP first. Caddy only honours the header from a trusted peer, so a spoofed one from anywhere else is ignored. Operator-listed proxies and their headers stay in force.
func (Hardening) WithOverrides ¶
WithOverrides layers dashboard values over h for every setting whose env variable is unset.
type HeaderOps ¶ added in v0.2.0
type HeaderOps struct {
Set map[string][]string `json:"set,omitempty"`
Delete []string `json:"delete,omitempty"`
}
HeaderOps is Caddy's headers.HeaderOps.
type HeaderResponse ¶ added in v0.2.0
type HeaderResponse struct {
Set map[string][]string `json:"set,omitempty"`
Deferred bool `json:"deferred,omitempty"`
}
HeaderResponse sets response headers; Deferred applies them after the upstream answered, so they win over the backend's own value.
type HeadersHandler ¶ added in v0.2.0
type HeadersHandler struct {
Handler string `json:"handler"`
Request *HeaderOps `json:"request,omitempty"`
Response *HeaderResponse `json:"response,omitempty"`
}
HeadersHandler mirrors Caddy's headers handler, response side only.
func NewNoIndexHeadersHandler ¶ added in v0.2.0
func NewNoIndexHeadersHandler() HeadersHandler
NewNoIndexHeadersHandler sets X-Robots-Tag on every response.
type HealthChecks ¶ added in v0.2.0
type HealthChecks struct {
Active *ActiveHealthChecks `json:"active,omitempty"`
Passive *PassiveHealthChecks `json:"passive,omitempty"`
}
HealthChecks is Caddy's reverse_proxy health_checks block.
type HoldRoute ¶ added in v0.2.0
type HoldRoute struct {
Hosts []string
}
HoldRoute is a domain whose backend is momentarily absent (mid-deploy or restarting): it answers the friendly 503 and keeps the host in the TLS automation set so the handshake still succeeds.
type HoldTracker ¶ added in v0.2.0
type HoldTracker struct {
// contains filtered or unexported fields
}
HoldTracker remembers when each host last had a live backend, so a host that loses its backend mid-deploy keeps answering a friendly 503 (and keeps its certificate) for a bounded window instead of vanishing from the config. It must outlive per-pass controllers.
func NewHoldTracker ¶ added in v0.2.0
func NewHoldTracker(window time.Duration) *HoldTracker
NewHoldTracker builds a tracker; a window of zero or less disables holding.
type IPFormat ¶
type IPFormat string
IPFormat is how a wildcard-DNS provider expects the address embedded in a hostname label.
type InheritedSockets ¶ added in v0.2.0
InheritedSockets are listener file descriptors handed over by systemd. The kernel keeps accepting into their backlog while the control plane restarts, so a restart delays connections instead of refusing them.
func InheritSockets ¶ added in v0.2.0
InheritSockets reads the sd_listen_fds protocol. It returns nil with no error when nothing was passed. mode is the EnvSocketActivation value: "false" ignores inherited sockets, "true" makes their absence an error.
func (*InheritedSockets) Active ¶ added in v0.2.0
func (s *InheritedSockets) Active() bool
Active reports whether the HTTPS listener was inherited.
func (*InheritedSockets) Fingerprint ¶
func (s *InheritedSockets) Fingerprint() string
Fingerprint identifies the listener setup a config was built for, so a snapshot is never replayed onto different sockets.
type InternalIssuer ¶
type InternalIssuer struct {
Module string `json:"module"`
// Lifetime bounds the certificate it signs, so a fallback certificate
// is replaced (and the public issuer retried) soon after it is issued.
Lifetime string `json:"lifetime,omitempty"`
}
InternalIssuer is Caddy's "internal" TLS issuer (tls.issuance.internal): a locally-generated, self-signed CA, meant for exactly the case this spike is in, no public domain and no inbound port 80/443 reachable from the internet for real ACME. It works fully offline, standing in for real ACME here; verifying this against a real domain is still needed.
func NewInternalIssuer ¶
func NewInternalIssuer() InternalIssuer
NewInternalIssuer returns the JSON shape for Caddy's internal issuer module, used as an AutomationPolicy's Issuers entry.
type LBRoute ¶ added in v0.2.0
type LBRoute struct {
Upstreams []string
// Weights aligns with Upstreams and is only emitted for the weighted policy.
Weights []int
Policy string
// CookieName is the sticky-session cookie for the cookie policy.
CookieName string
Retries int
TryDuration string
TryInterval string
ActiveHealth *ActiveHealthChecks
PassiveHealth *PassiveHealthChecks
StreamCloseDelay string
DialTimeout string
ResponseHeaderTimeout string
UpstreamTLS *UpstreamTLSConfig
RateLimitRPS int
RateLimitBurst int
}
LBRoute is one route's upstream pool and balancing settings. A ProxyRoute with LB set uses these upstreams instead of BackendDial.
func NewLBRoute ¶ added in v0.2.0
func NewLBRoute(cfg loadbalancer.Config, ups []loadbalancer.Upstream, weights []int) *LBRoute
NewLBRoute translates a load balancer config and its discovered upstreams into Caddy route settings. It returns nil when there are no upstreams.
type Layer4App ¶ added in v0.2.0
type Layer4App struct {
Servers map[string]*Layer4Server `json:"servers,omitempty"`
}
Layer4App is Caddy's "layer4" app (github.com/mholt/caddy-l4/layer4), mirrored here the same way HTTPApp/TLSApp hand-roll the wire-compatible subset of Caddy's own config types rather than importing them directly (see config.go's package doc comment).
type Layer4ProxyHandler ¶ added in v0.2.0
type Layer4ProxyHandler struct {
Handler string `json:"handler"`
Upstreams []Layer4UpstreamDial `json:"upstreams,omitempty"`
}
Layer4ProxyHandler is caddy-l4's "proxy" handler (layer4.handlers.proxy, github.com/mholt/caddy-l4/modules/l4proxy): plain TCP passthrough to Upstreams, no TLS termination or re-encryption. Handler is always the literal string "proxy": the module's own CaddyModule ID, mirroring ReverseProxyHandler's identical "fixed discriminator as a plain field" shape.
func NewLayer4ProxyHandler ¶ added in v0.2.0
func NewLayer4ProxyHandler(backendDial string) Layer4ProxyHandler
NewLayer4ProxyHandler builds the one handler shape a stream needs: forward every byte to backendDial, unconditionally.
type Layer4Route ¶ added in v0.2.0
type Layer4Route struct {
Handle []any `json:"handle"`
}
Layer4Route is one layer4 route: Match is always left empty (omitted), which caddy-l4's own MatcherSets.AnyMatch treats as "always match", the same convention Route.Match's absence has in Caddy's HTTP app.
type Layer4Server ¶ added in v0.2.0
type Layer4Server struct {
Listen []string `json:"listen,omitempty"`
Routes []Layer4Route `json:"routes,omitempty"`
}
Layer4Server is one layer4 listener plus the routes it serves. Unlike HTTPApp's Server (one shared listener, many Host-matched routes), one StreamRoute gets its own Layer4Server keyed by its own listen address, because a stream has nothing else to match requests on.
type Layer4UpstreamDial ¶ added in v0.2.0
type Layer4UpstreamDial struct {
Dial []string `json:"dial,omitempty"`
}
Layer4UpstreamDial is one caddy-l4 proxy backend. Dial is a list for caddy-l4's own failover support; this package only ever sets one address, since a stream has exactly one backend (no load balancing, out of scope for this first slice, see RoutesOptions.Streams's own doc comment).
type LoadBalancing ¶ added in v0.2.0
type LoadBalancing struct {
SelectionPolicy *SelectionPolicy `json:"selection_policy,omitempty"`
Retries int `json:"retries,omitempty"`
TryDuration string `json:"try_duration,omitempty"`
TryInterval string `json:"try_interval,omitempty"`
}
LoadBalancing is Caddy's reverse_proxy load_balancing block.
type LogConfig ¶ added in v0.2.0
type LogConfig struct {
Writer map[string]string `json:"writer"`
Encoder map[string]string `json:"encoder"`
Level string `json:"level,omitempty"`
Include []string `json:"include"`
}
LogConfig is one named Caddy log.
type LoggingConfig ¶ added in v0.2.0
LoggingConfig is Caddy's logging section, used only to tap ACME errors.
type MaintenanceGate ¶
type MaintenanceGate struct {
Page MaintenancePage
BypassCIDRs []string
BypassPaths []string
}
MaintenanceGate puts a proxied route behind the maintenance page while letting bypass clients and paths through to the full route.
type MaintenancePage ¶
MaintenancePage is what visitors see while a domain is in maintenance. Empty fields fall back to the defaults.
type MaintenanceRoute ¶
type MaintenanceRoute struct {
// Hosts are the Host header values that get the fixed maintenance
// response. Also contributes to the Subjects list used for TLS
// automation when RoutesOptions.TLS is true, the same as Hosts on
// ProxyRoute/StaticRoute: a domain in maintenance mode still needs
// a valid certificate, an operator flips this on and off far more
// often than they'd want to also manage TLS for.
Hosts []string
Page MaintenancePage
}
MaintenanceRoute is one domain currently in maintenance mode: every request gets NewMaintenanceResponseHandler's fixed response instead of ever reaching a backend, container or otherwise. Unlike ProxyRoute and StaticRoute, a MaintenanceRoute needs no backend address or root directory to have ever existed: "this domain is intentionally unavailable right now" is true independent of whether the service behind it has a running container this pass.
type Matcher ¶
type Matcher struct {
Host []string `json:"host,omitempty"`
Path []string `json:"path,omitempty"`
Expression *ExpressionMatcher `json:"expression,omitempty"`
Not []Matcher `json:"not,omitempty"`
Vars map[string][]string `json:"vars,omitempty"`
Header map[string][]string `json:"header,omitempty"`
ClientIP *ClientIPMatcher `json:"client_ip,omitempty"`
}
Matcher is a single Caddy request matcher set. Host is used by every route this package builds; Expression (a CEL matcher, http.matchers.expression) is used only by error-page routes to match on the {http.error.status_code} placeholder, which no other matcher kind here can express.
type PKIApp ¶
type PKIApp struct {
CertificateAuthorities map[string]*CA `json:"certificate_authorities,omitempty"`
}
PKIApp is Caddy's "pki" app: the certificate authorities available to issuers like InternalIssuer.
type PassiveHealthChecks ¶ added in v0.2.0
type PassiveHealthChecks struct {
FailDuration string `json:"fail_duration,omitempty"`
MaxFails int `json:"max_fails,omitempty"`
UnhealthyStatus []int `json:"unhealthy_status,omitempty"`
}
PassiveHealthChecks is Caddy's health_checks.passive block.
type ProviderChoice ¶
type ProviderChoice struct {
Provider string `json:"provider"`
CustomBase string `json:"custom_base_domain,omitempty"`
}
ProviderChoice selects a provider; CustomBase is used only with ProviderCustom. The zero value is the default provider.
func (ProviderChoice) Label ¶
func (c ProviderChoice) Label() string
Label is the provider domain or custom base, for messages.
func (ProviderChoice) Normalized ¶
func (c ProviderChoice) Normalized() ProviderChoice
Normalized fills in the default provider and lowercases the fields.
func (ProviderChoice) Validate ¶
func (c ProviderChoice) Validate() error
Validate reports whether c names a known provider and, for custom, a valid base domain.
type ProviderSource ¶
type ProviderSource interface {
GetDNSProviderDefault(ctx context.Context) (store.DNSProviderChoice, error)
ListAppDNSProviders(ctx context.Context) (map[string]store.DNSProviderChoice, error)
}
ProviderSource is the store surface the resolver reads; *store.DB implements it.
type ProxyHeaders ¶ added in v0.2.0
type ProxyHeaders struct {
Request *HeaderOps `json:"request,omitempty"`
}
ProxyHeaders is reverse_proxy's headers block.
type ProxyOptions ¶
type ProxyOptions struct {
// ServerName keys the server within apps.http.servers. Arbitrary,
// used only for logging/introspection.
ServerName string
// ListenAddr is a Caddy network address, e.g. ":8080" or
// "127.0.0.1:8443".
ListenAddr string
// BackendDial is the reverse-proxy target, e.g. "127.0.0.1:9090".
BackendDial string
// Hosts, if non-empty, restricts the route to these Host header
// values and is also the Subjects list used for TLS automation when
// TLS is enabled. Empty means "match every request on this
// listener", which is only valid for the plain-HTTP case: Caddy's
// automatic HTTPS needs at least one qualifying hostname to know
// what to issue a certificate for.
Hosts []string
// TLS, if true, adds a tls app automation policy scoping the
// internal issuer to Hosts, and disables the HTTP->HTTPS redirect
// (see Server.AutomaticHTTPS).
TLS bool
// AdminListen overrides Caddy's admin API bind address. Empty keeps
// Caddy's own default (localhost:2019).
AdminListen string
// StorageDir overrides Caddy's storage root (certificates, ACME
// account state, the internal issuer's local CA). Empty keeps
// Caddy's own OS-specific default. See FileStorage's doc comment for
// why Phase 1 should always set this rather than rely on the
// default.
StorageDir string
}
ProxyOptions is the input to BuildProxyConfig: everything needed to stand up one reverse-proxy route, with or without TLS.
type ProxyRewrite ¶ added in v0.2.0
type ProxyRewrite struct {
Method string `json:"method,omitempty"`
URI string `json:"uri,omitempty"`
}
ProxyRewrite is reverse_proxy's rewrite block: it changes only the request sent upstream, never the one later handlers see.
type ProxyRoute ¶
type ProxyRoute struct {
// Hosts are the Host header values routed to BackendDial. Also
// contributes to the Subjects list used for TLS automation when
// RoutesOptions.TLS is true.
Hosts []string
// BackendDial is the reverse-proxy target, e.g. "127.0.0.1:9090".
BackendDial string
// BasicAuth, if non-nil, adds a BasicAuthHandler ahead of the
// reverse-proxy handler so every host in this route requires HTTP
// Basic Auth. Nil (the default) reproduces this package's prior
// behavior exactly: a plain reverse-proxy route with no
// authentication handler.
BasicAuth *BasicAuthAccount
// WAF, if non-nil, adds a WAF and/or rate-limit handler ahead of
// BasicAuth and the reverse-proxy handler. Nil (the default)
// reproduces this package's prior behavior exactly: no such handler
// at all.
WAF *WAFConfig
// AuthGate, if non-nil, sends every request to an auth service first.
AuthGate *AuthGate
// ErrorPages, if non-empty, replaces the response for each listed
// status code with the operator's own HTML, covering both a status
// the backend itself returns (via ReverseProxyHandler.HandleResponse)
// and a real proxy failure Caddy generates on this route's behalf
// (via a wrapping SubrouteHandler.Errors). Empty (the default)
// reproduces this package's prior behavior exactly.
ErrorPages []ErrorPage
// Hidden asks search engines to stay away: X-Robots-Tag on every
// response and an ingress-served /robots.txt that disallows everything.
Hidden bool
// MaxBodyBytes, when positive, replaces the instance request body cap for these hosts.
MaxBodyBytes int64
// LB, if non-nil, replaces BackendDial with a multi-upstream pool.
LB *LBRoute
// Policy, if non-nil, adds the domain's traffic controls (policy_*.go).
Policy *RoutePolicy
// Abuse, if non-nil and active, puts the owning app's abuse defense in
// front of every other control on the route.
Abuse *AbusePolicy
// Upstream, if non-nil, shapes a standalone domain's single backend.
// Ignored when LB is set.
Upstream *UpstreamOptions
// Maintenance, if non-nil, serves the maintenance page to every request
// outside its bypass list, ahead of every other control.
Maintenance *MaintenanceGate
}
ProxyRoute is one reverse-proxy backend routed by hostname within a Server potentially shared with other routes. Unlike ProxyOptions (BuildProxyConfig), where Hosts is optional (an empty Hosts list means "match everything on this listener," valid for a single-backend listener), a Route here always requires host matching: hostname is the only thing that disambiguates two different backends sharing one listener.
type RateLimitHandler ¶ added in v0.2.0
type RateLimitHandler struct {
Handler string `json:"handler"`
RateLimits map[string]RateLimitZone `json:"rate_limits,omitempty"`
}
RateLimitHandler is Caddy's rate_limit handler (http.handlers.rate_limit, github.com/mholt/caddy-ratelimit).
func NewRateLimitHandler ¶ added in v0.2.0
func NewRateLimitHandler(zonePrefix string, rps, burst int) RateLimitHandler
NewRateLimitHandler builds the rate-limit handler for a domain that opted into rate limiting. zonePrefix must be globally unique across every domain's rate_limit handler in the same Caddy config (the underlying module keys its limiter state by zone name across the whole process, not just within one handler instance); callers pass the domain itself, which service_domains already enforces as unique.
Two zones approximate a token-bucket's sustained-rate-plus-burst shape on top of caddy-ratelimit's sliding-window primitive: a 10s window bounds the sustained average at rps, and (only when burst exceeds rps) a 1s window separately allows up to burst requests in any single second. burst at or below rps adds no second zone, since it would never be the tighter constraint.
type RateLimitZone ¶ added in v0.2.0
type RateLimitZone struct {
Key string `json:"key,omitempty"`
MaxEvents int `json:"max_events,omitempty"`
Window string `json:"window,omitempty"`
}
RateLimitZone is one named zone within a RateLimitHandler (github.com/mholt/caddy-ratelimit's own RateLimit struct, mirrored here the same way this package hand-rolls every other Caddy module's wire shape rather than importing it directly). Window is a Caddy duration string (e.g. "1s", "10s"), not a number of nanoseconds: caddy.Duration's UnmarshalJSON accepts either, this package always emits the string form for readability.
type RedirectRoute ¶ added in v0.2.0
type RedirectRoute struct {
// Hosts are the Host header values that get redirected to TargetURL.
// Also contributes to the Subjects list used for TLS automation when
// RoutesOptions.TLS is true, the same as MaintenanceRoute.Hosts: a
// redirected domain still needs a valid certificate to redirect a
// browser that arrives over HTTPS in the first place.
Hosts []string
// TargetURL is the absolute URL every matched request is redirected
// to, e.g. "https://example.com" or "https://newapp.example.com/promo".
// Validated by the caller (internal/api); this package performs no
// validation of its own.
TargetURL string
// StatusCode is the redirect's HTTP status: store.DomainRedirectPermanent
// (301, the default) or store.DomainRedirectTemporary (302).
StatusCode int
}
RedirectRoute is one domain currently configured to redirect to an arbitrary target URL: every request gets NewRedirectResponseHandler's fixed response instead of ever reaching a backend, container or otherwise. Mirrors MaintenanceRoute's shape exactly (no backend address needed to have ever existed) for a different per-domain behavior: "this domain has moved" rather than "this domain is intentionally unavailable right now".
type RedirectsHandler ¶ added in v0.2.0
type RedirectsHandler struct {
Handler string `json:"handler"`
// contains filtered or unexported fields
}
RedirectsHandler is the wire shape of redirectsModule.
func (RedirectsHandler) CaddyModule ¶ added in v0.2.0
func (RedirectsHandler) CaddyModule() caddy.ModuleInfo
type RequestBodyHandler ¶ added in v0.2.0
RequestBodyHandler is Caddy's request_body handler: it caps how much of a request body the proxy will read.
type RequestIdentity ¶
RequestIdentity is client detail handed to traffic export subscribers only, and only while a sink has opted in. The ring never stores it and it is never serialized.
type RequestObservation ¶
type RequestObservation struct {
Method string
Path string
Status int
Duration time.Duration
BytesIn int64
BytesOut int64
UpstreamErr bool
Upstream string
RequestID string
At time.Time
// Referrer is the Referer header's host only; Country an ISO code.
Referrer string
Country string
}
RequestObservation is one finished request as the stats see it. Upstream is the replica host:port; no query string or client address is ever included.
type RequestStats ¶ added in v0.2.0
type RequestStats struct {
// contains filtered or unexported fields
}
RequestStats aggregates request activity per route key in process memory. Memory is bounded by the number of configured hosts: one fixed-size counter set per key, never per path or client.
func DefaultRequestStats ¶ added in v0.2.0
func DefaultRequestStats() *RequestStats
DefaultRequestStats is the process-wide aggregator the request_stats handler feeds; Caddy modules are constructed from JSON and cannot receive a live value any other way.
func NewRequestStats ¶ added in v0.2.0
func NewRequestStats() *RequestStats
NewRequestStats returns an empty aggregator.
func (*RequestStats) DrainByApp ¶ added in v0.2.0
func (s *RequestStats) DrainByApp() map[string]telemetry.RequestWindow
DrainByApp returns and resets activity since the last call, merged per app. Keys with no owner are dropped.
func (*RequestStats) KeysForApp ¶
func (s *RequestStats) KeysForApp(app string) []string
KeysForApp returns the route keys owned by app.
func (*RequestStats) Observe ¶ added in v0.2.0
func (s *RequestStats) Observe(key string, status int, d time.Duration, bytesIn, bytesOut int64, upstreamErr bool)
Observe records one finished request.
func (*RequestStats) ObservePath ¶ added in v0.2.0
func (s *RequestStats) ObservePath(key, path string, status int, d time.Duration, bytesIn, bytesOut int64, upstreamErr bool)
ObservePath records one finished request and, when route counting is on and path is non-empty, its normalized route template. Only the path is read, never the query string, and ids are collapsed before anything is stored.
func (*RequestStats) ObserveRequest ¶
func (s *RequestStats) ObserveRequest(key string, o RequestObservation)
ObserveRequest records o under key: counters, route, replica histogram and, for slow or failed requests, a bounded sample.
func (*RequestStats) ObserveShield ¶
func (s *RequestStats) ObserveShield(key string, reason trafficpolicy.ShieldReason)
ObserveShield counts one request the abuse shield turned away under key.
func (*RequestStats) OwnerOf ¶
func (s *RequestStats) OwnerOf(key string) string
OwnerOf returns the app that owns route key, or "" when none does.
func (*RequestStats) SetHostOwners ¶ added in v0.2.0
func (s *RequestStats) SetHostOwners(owners map[string]string)
SetHostOwners replaces the host-to-app map used to attribute traffic. Keys are configured route hosts, values are app names.
func (*RequestStats) UpstreamTotals ¶
func (s *RequestStats) UpstreamTotals() map[string]telemetry.UpstreamStat
UpstreamTotals returns a copy of the cumulative per-replica counters since process start. Unlike DrainByApp it never resets, so a caller can diff two snapshots to judge one time window (the canary reconciler does).
type RequestStatsHandler ¶ added in v0.2.0
RequestStatsHandler is the wire shape of the request_stats handler (http.handlers.request_stats): it times the rest of the route and records status class, latency bucket and byte counts under Key, the route's first configured host. Query strings and client addresses are never read.
func NewRequestStatsHandler ¶ added in v0.2.0
func NewRequestStatsHandler(key string) RequestStatsHandler
NewRequestStatsHandler builds the handler recording under key.
type ResponseCache ¶ added in v0.2.0
type ResponseCache struct {
// contains filtered or unexported fields
}
ResponseCache is a bounded LRU of whole responses shared by every cache handler in the process. Memory stays under MaxBytes: inserting evicts least recently used entries until the new one fits.
func DefaultResponseCache ¶ added in v0.2.0
func DefaultResponseCache() *ResponseCache
DefaultResponseCache is the process-wide cache the cache handler uses, sized from APP_INGRESS_CACHE_MAX_BYTES.
func NewResponseCache ¶ added in v0.2.0
func NewResponseCache(maxBytes int64) *ResponseCache
NewResponseCache returns an empty cache capped at maxBytes.
func (*ResponseCache) Hosts ¶
func (c *ResponseCache) Hosts() []string
Hosts lists every host that has cache activity or entries.
func (*ResponseCache) MaxBytes ¶ added in v0.2.0
func (c *ResponseCache) MaxBytes() int64
MaxBytes is the global byte cap.
func (*ResponseCache) Purge ¶ added in v0.2.0
func (c *ResponseCache) Purge(host, scope, value string) int
Purge removes host's entries: one URL (path and query, every Vary variant), every path under a prefix, or all of them. Returns the count.
func (*ResponseCache) Stats ¶ added in v0.2.0
func (c *ResponseCache) Stats(host string) CacheStats
Stats returns host's counters and the last 30 minutes of activity, oldest first.
func (*ResponseCache) UsedBytes ¶ added in v0.2.0
func (c *ResponseCache) UsedBytes() int64
UsedBytes is the current total size of every entry.
type ResponseHandler ¶ added in v0.2.0
type ResponseHandler struct {
Match *ResponseMatcher `json:"match,omitempty"`
Routes []Route `json:"routes,omitempty"`
}
ResponseHandler mirrors Caddy's http.ResponseHandler: when Match matches the backend's response, Routes runs instead of passing the original response through.
type ResponseMatcher ¶ added in v0.2.0
type ResponseMatcher struct {
StatusCode []int `json:"status_code,omitempty"`
}
ResponseMatcher mirrors Caddy's reverse_proxy response matcher: a backend response matches when its status code is in StatusCode.
type ReverseProxyHandler ¶
type ReverseProxyHandler struct {
Handler string `json:"handler"`
Upstreams []Upstream `json:"upstreams"`
// HandleResponse mirrors reverse_proxy's own handle_response field:
// routes evaluated against the backend's actual response, ahead of
// it being written to the client. Used by error-page routes to
// replace a backend-returned status like 404 or 500 with a custom
// body while keeping the same status code.
HandleResponse []ResponseHandler `json:"handle_response,omitempty"`
LoadBalancing *LoadBalancing `json:"load_balancing,omitempty"`
HealthChecks *HealthChecks `json:"health_checks,omitempty"`
Transport *HTTPTransport `json:"transport,omitempty"`
StreamCloseDelay string `json:"stream_close_delay,omitempty"`
Headers *ProxyHeaders `json:"headers,omitempty"`
Rewrite *ProxyRewrite `json:"rewrite,omitempty"`
}
ReverseProxyHandler is Caddy's "reverse_proxy" handler (http.handlers.reverse_proxy). Handler is always the literal string "reverse_proxy"; it is a field rather than a constant embedded via MarshalJSON so the struct stays a plain, table-testable value.
func NewLBReverseProxyHandler ¶ added in v0.2.0
func NewLBReverseProxyHandler(lb *LBRoute) ReverseProxyHandler
NewLBReverseProxyHandler renders lb as a reverse_proxy handler with a multi-upstream pool.
func NewReverseProxyHandler ¶
func NewReverseProxyHandler(backendDial string) ReverseProxyHandler
NewReverseProxyHandler builds the one handler shape this spike needs: proxy everything matched by the route to a single backend.
type RewriteHandler ¶ added in v0.2.0
type RewriteHandler struct {
Handler string `json:"handler"`
URI string `json:"uri"`
Method string `json:"method,omitempty"`
}
RewriteHandler is Caddy's "rewrite" handler: it replaces the request URI and, when set, the method. The wake hook is GET-only whatever the client sent.
type Route53DNSProvider ¶ added in v0.2.0
type Route53DNSProvider struct {
Name string `json:"name"`
AccessKeyID string `json:"access_key_id,omitempty"`
SecretAccessKey string `json:"secret_access_key,omitempty"`
Region string `json:"region,omitempty"`
HostedZoneID string `json:"hosted_zone_id,omitempty"`
}
Route53DNSProvider is the wire shape for Caddy's Route53 DNS provider module (dns.providers.route53, github.com/caddy-dns/route53 wrapping github.com/libdns/route53), used as a DNSChallengeConfig's Provider. AccessKeyID and SecretAccessKey must be an IAM credential scoped to route53:ChangeResourceRecordSets/route53:ListResourceRecordSets/ route53:GetChange on the target hosted zone. Region and HostedZoneID are optional: empty lets libdns/route53 resolve them itself (region from the AWS SDK's own default chain, hosted zone by matching the domain against the account's own zones).
type RouteCheck ¶
RouteCheck proves a request reached this ingress: a request carrying Token in RouteCheckHeader gets 204 with Reply, which no app behind it can know.
func NewRouteCheck ¶
func NewRouteCheck() (RouteCheck, error)
NewRouteCheck returns a random token pair, valid for this process.
func (RouteCheck) Valid ¶
func (c RouteCheck) Valid() bool
Valid reports whether both values are well formed and distinct.
type RoutePolicy ¶ added in v0.2.0
type RoutePolicy struct {
Policy trafficpolicy.Policy
// AppDials maps an app name to its reverse proxy dial address.
AppDials map[string]string
// AppWake maps a sleeping app to its wake route, so a forwarder that
// targets it starts it instead of answering "not running".
AppWake map[string]WakeRoute
// Targets maps an external forwarder URL to its checked address. A URL
// missing here (blocked or unresolvable) compiles to a 502 response.
Targets map[string]trafficpolicy.ResolvedTarget
// TLSTerminatedUpstream means a proxy in front terminates TLS.
TLSTerminatedUpstream bool
// TLSReal means visitors see a publicly trusted certificate.
TLSReal bool
// HTTPSPort is the public HTTPS port for force HTTPS redirects.
HTTPSPort int
// GeoActive is false when no country source is configured; geo rules
// are then skipped and reported as disabled.
GeoActive bool
Limits trafficpolicy.Limits
}
RoutePolicy is one domain's traffic controls (internal/trafficpolicy), plus what the reconciler resolved for this pass: upstream dials for app forwarders and checked addresses for external ones.
type RoutesOptions ¶
type RoutesOptions struct {
// ServerName keys the server within apps.http.servers. Arbitrary,
// used only for logging/introspection.
ServerName string
// ListenAddr is a Caddy network address shared by every route, e.g.
// ":443".
ListenAddr string
// HTTPPort is this control plane's own configured HTTP/port-80
// equivalent (see internal/reconcile/ingress.WithHTTPListenAddr).
// Threaded to HTTPApp.HTTPPort and, when ACME is enabled for a
// non-wildcard host, to the ACME issuer's Challenges.HTTP.
// AlternatePort (see NewACMEIssuer), so an instance running its
// ingress on non-default ports never reaches for a literal port 80.
// 0 keeps Caddy's own default (80) in both places.
HTTPPort int
// HTTPRedirect, when true, lets Caddy bind the HTTP port and 308
// redirect every routed host to https. Off keeps the port unbound
// outside ACME challenges (non-root dev runs cannot bind port 80).
HTTPRedirect bool
// FallbackCertLifetime is how long the internal fallback certificate
// served while public ACME issuance fails lives, so the public CA is
// retried on that cadence. Empty keeps Caddy's default (12h).
FallbackCertLifetime string
// LazyCertHosts get their certificate on the first TLS handshake instead of when the config loads.
LazyCertHosts []string
// PlainHTTP serves the same routes over unencrypted HTTP on HTTPPort,
// for a proxy in front that terminates TLS and dials this port.
PlainHTTP bool
// HTTPBindHost is the interface the PlainHTTP listener binds; empty means every interface.
HTTPBindHost string
// Routes is every reverse-proxy backend to route on this listener.
// Empty is valid: it produces a listener with no routes and no TLS
// automation policy, the normal shape for a reconcile pass over zero
// currently-routable services, not an error condition.
Routes []ProxyRoute
// StaticRoutes is every static site to serve directly on this same
// listener, alongside Routes: one shared Caddy server can carry both
// reverse-proxy and file_server routes, disambiguated by Host the
// same way two ProxyRoutes already are. Empty (the default) is valid
// for exactly the same reason an empty Routes is.
StaticRoutes []StaticRoute
// MaintenanceRoutes is every domain currently in maintenance mode,
// sharing this same listener the same way StaticRoutes does. A
// caller must never put the same host in both Routes/StaticRoutes
// and MaintenanceRoutes in one call: BuildRoutesConfig builds
// whatever it's given, it does not itself resolve that conflict
// (the ingress reconciler's job, splitting a service's hosts before
// ever calling this function).
MaintenanceRoutes []MaintenanceRoute
// RedirectRoutes is every domain currently configured to redirect to
// a target URL, sharing this same listener the same way
// MaintenanceRoutes does. A caller must never put the same host in
// both another route kind and RedirectRoutes in one call, the same
// caller-resolves-conflicts contract MaintenanceRoutes documents:
// internal/reconcile/ingress resolves maintenance mode ahead of a
// redirect for any host with both configured (a domain deliberately
// taken down for maintenance stays down, even if it also has a
// stale redirect configured from an earlier migration), so a host
// never reaches this field and MaintenanceRoutes at once.
RedirectRoutes []RedirectRoute
// RouteCheck, when set, answers a verifier's marker request on every
// host, ahead of every other route.
RouteCheck *RouteCheck
// TLS, if true, adds a tls app automation policy scoped to every
// route's Hosts, both Routes and StaticRoutes (skipped if both are
// empty, since automatic HTTPS needs at least one subject to issue a
// certificate for), and disables the HTTP->HTTPS redirect (see
// Server.AutomaticHTTPS).
TLS bool
// ACMEEnabled, if true (and TLS is true and there's at least one
// host), scopes every routed host's single automation policy to a
// real ACME issuer (NewACMEIssuer) instead of Caddy's offline
// internal issuer. This is the operator-facing toggle
// internal/store.IngressSettings.ACMEEnabled drives
// (internal/reconcile/ingress reads that row fresh every reconcile
// pass and threads it straight through). False, the default,
// reproduces this package's original, ADR-005-verified behavior
// exactly: every route gets the internal issuer, byte-identical to
// before this field existed. There is deliberately no per-host mix
// of ACME and internal issuers in this pass: every currently-routed
// host shares one automation policy, either all-internal or
// all-ACME. Real per-domain granularity is genuine, existing Caddy
// capability (AutomationPolicy already supports an ordered list of
// policies, "the first policy whose Subjects match a given hostname
// wins," see that type's own doc comment) but is an explicit,
// deliberately out-of-scope v1 gap, not something this field's
// absence of a per-route override quietly forecloses.
ACMEEnabled bool
// ACMEEmail is the ACME account contact address, passed straight
// through to NewACMEIssuer when ACMEEnabled is true. This package
// performs no validation on it (empty is accepted structurally);
// internal/api's PUT /api/v1/settings/ingress is where "required and
// syntactically valid whenever ACME is enabled" is actually
// enforced, before a value ever reaches this builder.
ACMEEmail string
// ACMEDirectoryURL overrides the ACME CA's directory endpoint,
// passed straight through to NewACMEIssuer when ACMEEnabled is true.
// Empty keeps Caddy's own compiled-in default (Let's Encrypt
// production). See ACMEIssuer.CA's own doc comment for why this
// package never silently substitutes Let's Encrypt's staging
// directory on an operator's behalf.
ACMEDirectoryURL string
// DNSProvider, if non-nil (and ACMEEnabled is true and at least one
// route's host is a wildcard, see IsWildcardDomain), scopes every
// wildcard subject to a separate automation policy using DNS-01 via
// this provider (NewDNSACMEIssuer) instead of the plain ACME policy
// every non-wildcard host still gets: HTTP-01 cannot solve a
// wildcard identifier. Nil reproduces this package's prior behavior
// exactly, wildcard hosts included, byte-identical to before this
// field existed. Exactly one provider is ever active per reconcile
// pass; internal/reconcile/ingress resolves which one (and its
// credentials) from the operator-facing settings and internal/secrets.
DNSProvider DNS01Provider
// DNS01AllHosts makes DNS-01 the challenge for every host, not only
// wildcards, so servers behind CGNAT or a closed port 80 can still
// issue. It needs DNSProvider; without one the build fails closed.
DNS01AllHosts bool
// DNS01Hosts are single hosts that use DNS-01 while every other host
// keeps its own challenge. Ignored when no DNSProvider is set.
DNS01Hosts []string
// AdminListen overrides Caddy's admin API bind address. Empty keeps
// Caddy's own default (localhost:2019).
AdminListen string
// StorageDir overrides Caddy's storage root. Empty keeps Caddy's own
// OS-specific default. See FileStorage's doc comment. Ignored when
// CertStorage is set.
StorageDir string
// TLSCertificates is every domain currently configured with a BYO TLS
// certificate (internal/store.DomainTLSCert), sourced fresh from the
// store and internal/secrets every reconcile pass by the caller (no
// caching here, matching every other RoutesOptions field). Each
// entry's Host must already be present in Routes/StaticRoutes/
// MaintenanceRoutes' combined host list for Caddy to ever select the
// loaded certificate for a real connection; a host with no route at
// all still loads harmlessly, it's just never selected. Empty (the
// default) reproduces this package's prior behavior exactly: every
// host gets Caddy's automatic ACME/internal issuance as before this
// field existed.
TLSCertificates []TLSCertificateOverride
// CertStorage, if non-nil, overrides StorageDir with an arbitrary
// Caddy storage module reference (e.g. NewSQLiteStorageRef()),
// letting the caller point Caddy's certificate/ACME-account storage
// at internal/store's SQLite instead of the local
// filesystem, so certificate storage lives in the database and
// multi-node deployments can share cert state. Takes precedence over
// StorageDir when both are set, rather than being an error, so a
// caller migrating from one to the other doesn't need a separate
// code path just to clear the old field.
CertStorage any
// RequestStats, if true, wraps every proxy and static route in the
// request_stats handler (per-app request rate, errors, latency).
RequestStats bool
// Firewall puts the instance firewall handler in front of every host route.
Firewall bool
// Hardening, if non-nil, applies server limits, failover defaults and the
// friendly 503 page. Nil keeps the legacy bare config.
Hardening *Hardening
// Inherited, when active, replaces the listen addresses with systemd
// passed sockets and serves the HTTP to HTTPS redirect from its own server.
Inherited *InheritedSockets
// HoldRoutes answer 503 for hosts whose backend is momentarily absent.
HoldRoutes []HoldRoute
// WakeRoutes serve a "waking up" page for sleeping apps and ask the
// control plane to start them.
WakeRoutes []WakeRoute
// Streams is every raw TCP port forward to proxy, each on its own
// dedicated listener (apps.layer4.servers), applied in this same
// Config/caddy.Load call: see layer4.go's package doc comment for
// why this can't be a second, independently-applied Caddy config.
// Empty (the default) adds no layer4 app at all, reproducing this
// package's behavior before this field existed exactly.
Streams []StreamRoute
}
RoutesOptions is the input to BuildRoutesConfig: everything needed to stand up one Caddy server carrying many independently host-routed backends on a single shared listener. This is the shape a real ingress controller needs: ADR 005's Verified section found that caddy.Load replaces Caddy's entire process-wide config on every call, so a controller tracking many services builds one complete Config from every currently routable service and applies it whole on every reconcile, rather than incrementally updating a single service's route. BuildProxyConfig, by contrast, only ever builds a single route to a single backend and stays as-is for that narrower spike use case.
type SQLiteStorage ¶
type SQLiteStorage struct {
// contains filtered or unexported fields
}
SQLiteStorage implements certmagic.Storage over internal/store's embedded SQLite, replacing Caddy's default FileStorage module for certificates and ACME account state. This is the concrete shape internal/reconcile/ingress/controller.go's own package doc comment already names as the real requirement: certificate storage lives in the database so multi-node deployments share cert state, and every ingress-driving process pointed at the same database file sees the same certificates and issuance locks through this type, so two of them never independently re-obtain a certificate for a domain the other already has one for.
func NewSQLiteStorage ¶
func NewSQLiteStorage(certStore CertStore, logger *slog.Logger) *SQLiteStorage
NewSQLiteStorage builds a SQLiteStorage backed by certStore. A nil logger falls back to slog.Default(), matching this package's Driver convention (see driver.go's New).
func (*SQLiteStorage) Delete ¶
func (s *SQLiteStorage) Delete(ctx context.Context, key string) error
Delete implements certmagic.Storage.
func (*SQLiteStorage) Exists ¶
func (s *SQLiteStorage) Exists(ctx context.Context, key string) bool
Exists implements certmagic.Storage. The interface has no error return, so a lookup failure is treated as "doesn't exist" rather than panicking, logged so it's never silently swallowed: a false negative here just makes certmagic re-store/re-issue, safe, if wasteful, unlike a false positive, which could skip issuing a certificate that's actually missing.
func (*SQLiteStorage) List ¶
List implements certmagic.Storage. Mirrors certmagic.FileStorage.List: listing a prefix that matches nothing returns fs.ErrNotExist, the same error a filepath.Walk over a missing directory would surface.
func (*SQLiteStorage) Lock ¶
func (s *SQLiteStorage) Lock(ctx context.Context, name string) error
Lock implements certmagic.Locker (embedded in certmagic.Storage). It blocks, polling every lockPollInterval, until it claims name's lock or ctx is cancelled. While held, a background goroutine refreshes the lock row every lockRefreshEvery so a live holder's lock never looks stale to a competitor; a holder that crashes without calling Unlock stops refreshing, and the row is treated as abandoned (and may be taken over) once it is older than lockStaleAfter, mirroring certmagic's own FileStorage doc comment on stale-lock handling.
func (*SQLiteStorage) Store ¶
Store implements certmagic.Storage. A ".crt" key write is the one unambiguous "a certificate was (re)issued" signal regardless of which caller triggered it (Caddy's own ACME timer, or a reconciler nudge), so the audit hook lives here, the single choke point, not in any one caller. existedBefore is checked ahead of the real write so it always reflects state right before this write lands.
func (*SQLiteStorage) Unlock ¶
func (s *SQLiteStorage) Unlock(ctx context.Context, name string) error
Unlock implements certmagic.Locker.
func (*SQLiteStorage) WithAuditRecorder ¶ added in v0.2.0
func (s *SQLiteStorage) WithAuditRecorder(ar AuditRecorder) *SQLiteStorage
WithAuditRecorder sets the audit log destination for certificate issuance/renewal events this storage detects. Returns s so it can be chained onto NewSQLiteStorage's result at the construction site.
type SQLiteStorageRef ¶
type SQLiteStorageRef struct {
Module string `json:"module"`
}
SQLiteStorageRef is the JSON shape assigned to Config.Storage (or RoutesOptions.CertStorage) to select the "sqlite" storage module. See NewSQLiteStorageRef.
func NewSQLiteStorageRef ¶
func NewSQLiteStorageRef() SQLiteStorageRef
NewSQLiteStorageRef returns the JSON shape for the sqlite storage module. Pair with SetActiveCertStorage (directly, or via internal/reconcile/ingress.WithCertStore) so CertMagicStorage has a real backend to resolve to; a Config referencing this without an active backend registered fails to apply with a clear error rather than silently falling back to Caddy's file storage default.
type SelectionPolicy ¶ added in v0.2.0
type SelectionPolicy struct {
Policy string `json:"policy"`
Weights []int `json:"weights,omitempty"`
Name string `json:"name,omitempty"`
MaxAge string `json:"max_age,omitempty"`
Fallback *SelectionPolicy `json:"fallback,omitempty"`
}
SelectionPolicy is Caddy's load_balancing.selection_policy.
type Server ¶
type Server struct {
Listen []string `json:"listen,omitempty"`
Routes []Route `json:"routes,omitempty"`
// AutomaticHTTPS controls Caddy's automatic-HTTPS behavior for this
// server specifically. This spike disables the HTTP->HTTPS redirect
// (DisableRedir) for the TLS server: automatic redirects bind Caddy's
// default HTTP port (80), which needs root and would surprise a local
// spike run under a normal user, so the standalone TLS demo makes
// that trade-off explicit instead of silently trying to grab port 80.
AutomaticHTTPS *AutoHTTPSConfig `json:"automatic_https,omitempty"`
ReadHeaderTimeout string `json:"read_header_timeout,omitempty"`
ReadTimeout string `json:"read_timeout,omitempty"`
WriteTimeout string `json:"write_timeout,omitempty"`
IdleTimeout string `json:"idle_timeout,omitempty"`
MaxHeaderBytes int `json:"max_header_bytes,omitempty"`
Protocols []string `json:"protocols,omitempty"`
TrustedProxies *TrustedProxiesConfig `json:"trusted_proxies,omitempty"`
TrustedProxiesStrict int `json:"trusted_proxies_strict,omitempty"`
ClientIPHeaders []string `json:"client_ip_headers,omitempty"`
Errors *ErrorsConfig `json:"errors,omitempty"`
}
Server is a single HTTP(S) listener plus the routes it serves.
type ShieldHandler ¶
type ShieldHandler struct {
Handler string `json:"handler"`
Key string `json:"key"`
Rule trafficpolicy.Shield `json:"rule"`
}
ShieldHandler is the wire shape of the levelrail_shield handler. Key is the route's first host, the same key request stats use.
type SnapshotCheck ¶
SnapshotCheck is the result of a dry-run snapshot verification.
type SnapshotData ¶
type SnapshotData struct {
SavedAt time.Time `json:"saved_at"`
Fingerprint string `json:"fingerprint"`
Ports []int `json:"ports"`
Config json.RawMessage `json:"config"`
}
SnapshotData is the last applied Caddy config plus what Driver needs to resume.
type SnapshotStatus ¶
type SnapshotStatus struct {
Enabled bool
// SavedAt is the last successful write or the age of the restored file.
SavedAt time.Time
// Restored is true when this process started serving from the snapshot.
Restored bool
// RestoreError is why a present snapshot was not used; empty when it was
// used or there was none.
RestoreError string
// SaveError is the last write failure, cleared by the next success.
SaveError string
}
SnapshotStatus is what the driver knows about its route snapshot.
type SnapshotStore ¶
type SnapshotStore struct {
// contains filtered or unexported fields
}
SnapshotStore keeps the last applied ingress config on disk, sealed with a key derived from the master key (the config carries DNS tokens, private keys and basic auth hashes), so a restarted process can serve before it reconciles.
func NewSnapshotStore ¶
func NewSnapshotStore(path string, key []byte) (*SnapshotStore, error)
NewSnapshotStore seals snapshots at path with a 32 byte key.
func (*SnapshotStore) Load ¶
func (s *SnapshotStore) Load(wantFingerprint string) (SnapshotData, error)
Load reads and opens the snapshot. wantFingerprint must match the one saved.
func (*SnapshotStore) Path ¶
func (s *SnapshotStore) Path() string
Path is the snapshot file location.
func (*SnapshotStore) Save ¶
func (s *SnapshotStore) Save(data SnapshotData) error
Save writes data atomically: a crash mid-write leaves the previous snapshot.
type SplitTestingHandler ¶
type SplitTestingHandler struct {
Handler string `json:"handler"`
}
SplitTestingHandler is the wire shape of the split_testing handler. It carries no settings: the running tests live in DefaultSplits.
func NewSplitTestingHandler ¶
func NewSplitTestingHandler() SplitTestingHandler
NewSplitTestingHandler builds the handler.
type Splits ¶
type Splits struct {
// contains filtered or unexported fields
}
Splits is the process-wide registry of running split tests, keyed by the lowercase host they apply to. Caddy modules are built from JSON and cannot receive a live value, so the handler reads this like DefaultRequestStats.
func DefaultSplits ¶
func DefaultSplits() *Splits
DefaultSplits is the registry the split_testing handler reads.
func NewSplits ¶
func NewSplits() *Splits
NewSplits returns an empty registry with a randomly seeded draw source.
func (*Splits) Drain ¶
Drain returns the requests counted per test and variant since the last call and resets them. A drained count not persisted is lost, which is the accepted cost of keeping the request path free of database writes.
func (*Splits) SeedForTest ¶
SeedForTest replaces the draw source so tests are deterministic.
func (*Splits) Set ¶
func (s *Splits) Set(hosts []string, sp ActiveSplit)
Set activates or updates a split on hosts. Counters survive an update of the same test ID so editing weights does not reset the request counts.
type StaticResponseHandler ¶
type StaticResponseHandler struct {
Handler string `json:"handler"`
// StatusCode is an int, not caddyhttp.WeakString's own
// string-or-number wire shape: Caddy accepts a plain JSON number
// here for a fixed, non-placeholder status code, which is all this
// package ever needs.
StatusCode int `json:"status_code,omitempty"`
Body string `json:"body,omitempty"`
Headers map[string][]string `json:"headers,omitempty"`
}
StaticResponseHandler is Caddy's "static_response" handler (http.handlers.static_response): a fixed response with no upstream, no container dial, and no filesystem read. This is the handler maintenance-mode routes use (routes.go's MaintenanceRoute): every request to a domain in maintenance mode gets this same fixed response instead of ever reaching a ReverseProxyHandler, so it works identically whether or not the service behind it currently has any running container at all.
func NewAssetUnavailableHandler ¶
func NewAssetUnavailableHandler(retryAfter int) StaticResponseHandler
NewAssetUnavailableHandler answers an asset request with an empty, never cached 503 so no client ever stores HTML under an asset URL.
func NewErrorPageResponse ¶ added in v0.2.0
func NewErrorPageResponse(statusCode int, body string) StaticResponseHandler
NewErrorPageResponse builds the fixed response for one custom error page: statusCode preserved verbatim (so a custom 404 page still reads as a 404, not a 200), body replaced with the operator's own HTML.
func NewHiddenRobotsResponse ¶ added in v0.2.0
func NewHiddenRobotsResponse() StaticResponseHandler
NewHiddenRobotsResponse answers /robots.txt for a hidden domain.
func NewMaintenancePageHandler ¶
func NewMaintenancePageHandler(p MaintenancePage) StaticResponseHandler
NewMaintenancePageHandler builds the 503 maintenance response. Operator text is HTML escaped, and Caddy placeholders are escaped so a title can never expand into request or environment values.
func NewMaintenanceResponseHandler ¶
func NewMaintenanceResponseHandler() StaticResponseHandler
NewMaintenanceResponseHandler is the default maintenance page.
func NewRedirectResponseHandler ¶ added in v0.2.0
func NewRedirectResponseHandler(targetURL string, statusCode int) StaticResponseHandler
NewRedirectResponseHandler builds a fixed HTTP redirect: the same StaticResponseHandler shape NewMaintenanceResponseHandler uses, with a Location header and a 3xx status code instead of a fixed body. statusCode is the caller's responsibility to validate (store. DomainRedirectPermanent or store.DomainRedirectTemporary); this function does not itself enforce it is a redirect code.
type StaticRoute ¶
type StaticRoute struct {
// Hosts are the Host header values served from RootDir. Also
// contributes to the Subjects list used for TLS automation when
// RoutesOptions.TLS is true.
Hosts []string
// RootDir is the local filesystem directory to serve.
RootDir string
// Rules, when set, serves the tree through the routing rules compiled
// at deploy time instead of the plain file server.
Rules *staticroutes.Ruleset
}
StaticRoute is one static site routed by hostname within a Server potentially shared with other routes and with ProxyRoutes: a static site and a container service can be routed by the very same Caddy server, on different hostnames, because both ultimately produce a Route in the same Routes slice, just with a different Handle (FileServerHandler instead of ReverseProxyHandler). This is the shape that lets a static site bypass the container reconciler entirely, not a parallel ingress mechanism.
type StaticSiteHandler ¶
type StaticSiteHandler struct {
Handler string `json:"handler"`
// contains filtered or unexported fields
}
StaticSiteHandler is the wire shape of levelrail_static_site (http.handlers.levelrail_static_site): it serves a published tree and applies that deploy's compiled routing rules, so rules change only when the tree does.
func NewStaticSiteHandler ¶
func NewStaticSiteHandler(root string, rules *staticroutes.Ruleset) StaticSiteHandler
NewStaticSiteHandler builds the handler for root and its rules.
func (StaticSiteHandler) CaddyModule ¶
func (StaticSiteHandler) CaddyModule() caddy.ModuleInfo
type StreamRoute ¶ added in v0.2.0
type StreamRoute struct {
// ListenAddr is a Caddy network address, e.g. ":9000". Always a
// single stream's own dedicated port; never shared with HTTP's
// listener or another stream's.
ListenAddr string
// BackendDial is the TCP proxy target, e.g. "127.0.0.1:5432".
BackendDial string
}
StreamRoute is one raw TCP port forward (store.AppStream): every byte received on ListenAddr is proxied unconditionally to BackendDial, no Host-based matching, no TLS termination/re-encryption, no access list. Unlike ProxyRoute, a StreamRoute never shares a listener with anything else: ListenAddr is the operator-chosen host port that disambiguates it, the same role Host plays for a ProxyRoute.
type SubrouteHandler ¶ added in v0.2.0
type SubrouteHandler struct {
Handler string `json:"handler"`
Routes []Route `json:"routes,omitempty"`
Errors *ErrorsConfig `json:"errors,omitempty"`
}
SubrouteHandler is Caddy's "subroute" handler (http.handlers.subroute): runs Routes, then Errors.Routes if Routes returns an error. Error-page routes use this to catch a real proxy failure (e.g. the container is unreachable, which Caddy itself turns into a 502/504) in addition to ReverseProxyHandler.HandleResponse's own coverage of a status the backend actually returned.
type TLSApp ¶
type TLSApp struct {
Automation *Automation `json:"automation,omitempty"`
// Certificates mirrors Caddy's tls.certificates field (a
// caddy.ModuleMap keyed by loader module name within the
// tls.certificates namespace). This package only ever sets the
// "load_pem" key (see CertKeyPEMPair), for BYO TLS certificates.
Certificates CertificatesConfig `json:"certificates,omitempty"`
}
TLSApp is Caddy's "tls" app: certificate automation policy plus any manually loaded certificates.
type TLSCertificateOverride ¶
TLSCertificateOverride is one domain with an operator-supplied certificate and key (internal/store.DomainTLSCert, BYO TLS), used in place of Caddy's automatic ACME/internal issuance for that host.
type TrustedProxiesConfig ¶ added in v0.2.0
TrustedProxiesConfig is Caddy's static trusted_proxies source.
type Upstream ¶
type Upstream struct {
// Dial is a host:port address, e.g. "127.0.0.1:8080". Caddy's own
// docs note this must resolve to exactly one socket, no port ranges.
Dial string `json:"dial"`
}
Upstream is a single reverse-proxy backend target.
type UpstreamOptions ¶ added in v0.2.0
type UpstreamOptions struct {
TLS bool
InsecureSkipVerify bool
ServerName string
// HostHeader is a literal Host value, escaped before use; empty keeps
// the client's Host. HostFromUpstream wins over it.
HostHeader string
HostFromUpstream bool
DisableWebSocket bool
DialTimeout time.Duration
ResponseHeaderTimeout time.Duration
StripPrefix string
// BasePath is prepended to every forwarded path (a URL target's path).
BasePath string
}
UpstreamOptions shape one proxy route's single backend: TLS toward it, the Host it sees, timeouts, websockets and path rewriting.
type UpstreamTLSConfig ¶ added in v0.2.0
type UpstreamTLSConfig struct {
InsecureSkipVerify bool `json:"insecure_skip_verify,omitempty"`
ServerName string `json:"server_name,omitempty"`
}
UpstreamTLSConfig is the transport-level tls block toward upstreams.
type VarsHandler ¶ added in v0.2.0
type VarsHandler struct {
Handler string `json:"handler"`
}
VarsHandler is Caddy's no-op "vars" handler; handle_response skips a response handler whose routes are empty, so the 2xx branch carries one.
type VisitorBucket ¶
type VisitorBucket struct {
Start time.Time `json:"start"`
Requests int `json:"requests"`
Visitors int `json:"visitors"`
}
VisitorBucket is one sparkline step.
type VisitorHistoryStore ¶
type VisitorHistoryStore interface {
UpsertVisitorRollups(ctx context.Context, rows []store.VisitorRollup) error
ListVisitorRollups(ctx context.Context, app, domain string, since, until time.Time) ([]store.VisitorRollup, error)
PruneVisitorRollups(ctx context.Context, cutoff time.Time) (int64, error)
}
VisitorHistoryStore is the persistence the rollups need.
type VisitorSummary ¶
type VisitorSummary struct {
Requests int `json:"requests"`
Visitors int `json:"visitors"`
Status map[string]int `json:"status"`
TopPaths []CountedItem `json:"top_paths"`
TopRefs []CountedItem `json:"top_referrers"`
Series []VisitorBucket `json:"series"`
Domains []DomainVisitors `json:"domains"`
OldestKept time.Time `json:"oldest_record,omitempty"`
SaltPeriod string `json:"salt_period"`
}
VisitorSummary is aggregate only: no address, hash or user agent leaves the process.
type VisitorSummaryOptions ¶
VisitorSummaryOptions selects what Summarize aggregates.
type WAFConfig ¶ added in v0.2.0
type WAFConfig struct {
// Enabled turns on the OWASP Coraza handler (NewCorazaWAFHandler).
Enabled bool
// Blocking selects Coraza's SecRuleEngine mode: true is "On" (CRS
// matches reject the request), false is "DetectionOnly" (CRS runs
// and logs but never rejects). Only meaningful when Enabled is true.
Blocking bool
// RateLimitRPS, if greater than zero, adds a rate-limit handler
// (NewRateLimitHandler) independent of Enabled: an operator can rate
// limit a domain without also enabling the WAF, or vice versa.
RateLimitRPS int
// RateLimitBurst is the short-window (1s) cap; values at or below
// RateLimitRPS are treated as "no extra burst allowance" by
// NewRateLimitHandler.
RateLimitBurst int
}
WAFConfig is one domain's opt-in Web Application Firewall and rate limiting settings (store.DomainWAF), threaded through from internal/reconcile/ingress into a route's Handle chain.
type WakeRoute ¶ added in v0.2.0
type WakeRoute struct {
Hosts []string
// Dial is the control plane's own listener, Token its shared wake secret.
Dial string
Token string
App string
}
WakeRoute is a sleeping app's domains: any request starts the app and gets the control plane's waking response instead of an error.
Source Files
¶
- abuse.go
- access_log.go
- acme_events.go
- acme_fallback.go
- auth_gate.go
- certimport.go
- certpurge.go
- certstorage.go
- certstorage_module.go
- cloudflare_preset.go
- config.go
- diagnostic_page.go
- dns_googlecloud.go
- dns_provider.go
- dns_provider_resolver.go
- driver.go
- driver_snapshot.go
- edge_overrides.go
- fallback_domain.go
- firewall.go
- hardening.go
- hold.go
- inherit.go
- inherit_unix.go
- layer4.go
- loadbalancer.go
- loadbalancer_route.go
- maintenance_page.go
- policy_cache.go
- policy_cache_agg.go
- policy_cache_reasons.go
- policy_cache_store.go
- policy_forward.go
- policy_geo.go
- policy_geo_resolver.go
- policy_headers.go
- policy_redirects.go
- policy_route.go
- publicip.go
- request_audience.go
- request_stats.go
- route_check.go
- routes.go
- served_hosts.go
- shield.go
- snapshot.go
- split_testing.go
- static_site.go
- upstream_options.go
- visitor_history.go
- visitors.go
- wake.go
- wildcard.go