checker-dane/checker/types.go

106 lines
3.8 KiB
Go

// Package checker implements the DANE/TLSA checker for happyDomain.
//
// This checker is bound to the svcs.TLSAs service. Collect takes the TLSA
// records the user published (or plans to publish) for the service, derives
// one TLS endpoint per distinct (port, proto, base name), and declares those
// endpoints as tls.endpoint.v1 discovery entries. checker-tls then probes
// them; on the next evaluation, this checker reads the related TLS probes
// via obs.GetRelated and verifies each TLSA record matches the certificate
// chain the probe observed.
//
// The user-visible contract matches what DANE deployers expect:
//
// - Usage 0 (PKIX-TA) / 1 (PKIX-EE): also require the PKIX chain to be
// publicly trusted.
// - Usage 2 (DANE-TA) / 3 (DANE-EE): trust the TLSA as the anchor; PKIX
// validity is informational.
// - Selector 0 (Cert) / 1 (SPKI) and matching types 0/1/2 (Full/SHA-256/
// SHA-512) are matched against the chain slot implied by the usage.
package checker
import "time"
// ObservationKeyDANE is the observation key this checker writes.
const ObservationKeyDANE = "dane_checks"
// Option ids on CheckerOptions.
const (
// OptionService is auto-filled by the happyDomain host with the
// svcs.TLSAs service payload this checker is bound to.
OptionService = "service"
// OptionDomain is auto-filled with the domain apex. TLSA owner names
// in the service are relative to this apex.
OptionDomain = "domain_name"
// OptionSubdomain is the optional sub-zone under which the TLSAs
// service lives (matches the svcs.TLSAs analyzer's subdomain bucket).
OptionSubdomain = "subdomain"
// OptionProbeTimeoutMs is how long each underlying TLS probe is allowed.
// Passed through to checker-tls verbatim via the discovery entry options.
OptionProbeTimeoutMs = "probeTimeoutMs"
// OptionSTARTTLS is an optional per-endpoint STARTTLS hint keyed by
// "<port>/<proto>" → RFC 6335 service name (e.g. "25/tcp" → "smtp",
// "587/tcp" → "submission"). Common ports auto-map via a built-in table.
OptionSTARTTLS = "starttls"
)
// Severity constants mirror checker-tls.
const (
SeverityCrit = "crit"
SeverityWarn = "warn"
SeverityInfo = "info"
)
// TLSA field enum constants (RFC 6698 §2.1).
const (
UsagePKIXTA uint8 = 0
UsagePKIXEE uint8 = 1
UsageDANETA uint8 = 2
UsageDANEEE uint8 = 3
SelectorCert uint8 = 0
SelectorSPKI uint8 = 1
MatchingFull uint8 = 0
MatchingSHA256 uint8 = 1
MatchingSHA512 uint8 = 2
)
// DANEData is the full payload the checker writes under ObservationKeyDANE.
type DANEData struct {
// Targets is one entry per (port, proto, basename) triplet extracted
// from the TLSAs service.
Targets []TargetResult `json:"targets"`
CollectedAt time.Time `json:"collected_at"`
}
// TargetResult groups all TLSA records declared on a single endpoint and
// carries enough context to render an actionable HTML row per endpoint.
type TargetResult struct {
// Owner is the fully qualified DANE owner name (_<port>._<proto>.<host>).
Owner string `json:"owner"`
// Host is the connection target (typically the base name the TLSA
// records live under, or its MX/SRV target when relevant).
Host string `json:"host"`
Port uint16 `json:"port"`
Proto string `json:"proto"`
STARTTLS string `json:"starttls,omitempty"`
// Ref ties this target to the tls.endpoint.v1 discovery entry the
// checker emitted, so the rule can pick the matching RelatedObservation.
Ref string `json:"ref"`
// Records are the TLSA records declared for this endpoint.
Records []TLSARecord `json:"records"`
}
// TLSARecord is a user-facing view of a single dns.TLSA record.
type TLSARecord struct {
Usage uint8 `json:"usage"`
Selector uint8 `json:"selector"`
MatchingType uint8 `json:"matching_type"`
Certificate string `json:"certificate"` // lowercase hex
}