checker-tls/checker/types.go

144 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package checker implements a TLS checker for happyDomain. See README for
// the payload shape and consumer contract.
package checker
import "time"
// ObservationKeyTLSProbes is the observation key this checker writes.
const ObservationKeyTLSProbes = "tls_probes"
// Option ids on CheckerOptions.
const (
OptionEndpoints = "endpoints"
OptionProbeTimeoutMs = "probeTimeoutMs"
)
// Defaults shared between the definition's Default field and the runtime
// fallback when probeTimeoutMs is unset or invalid.
const (
DefaultProbeTimeoutMs = 10000
// MaxConcurrentProbes caps parallel probes per collect run to avoid
// exhausting file descriptors on domains with many endpoints.
MaxConcurrentProbes = 32
)
// TLSData is the full collected payload written under ObservationKeyTLSProbes.
type TLSData struct {
Probes map[string]TLSProbe `json:"probes"`
CollectedAt time.Time `json:"collected_at"`
}
// TLSProbe captures the outcome of probing a single endpoint.
//
// Only raw observation fields live here. Judgement (severity, pass/fail,
// human-facing messages) is derived from these fields by CheckRules.
type TLSProbe struct {
Host string `json:"host"`
Port uint16 `json:"port"`
Endpoint string `json:"endpoint"`
Type string `json:"type"`
SNI string `json:"sni,omitempty"`
// RequireSTARTTLS is copied from the discovery entry so rules can tell
// whether a missing STARTTLS advertisement is a hard or soft failure.
RequireSTARTTLS bool `json:"require_starttls,omitempty"`
// STARTTLSDialect mirrors contract.TLSEndpoint.STARTTLS verbatim. An
// empty value means direct TLS.
STARTTLSDialect string `json:"starttls_dialect,omitempty"`
// Raw error strings. Exactly one of TCPError or HandshakeError is set
// when the probe failed before gathering handshake data.
TCPError string `json:"tcp_error,omitempty"`
HandshakeError string `json:"handshake_error,omitempty"`
// STARTTLSNotOffered is true when HandshakeError was produced because
// the server did not advertise STARTTLS (errStartTLSNotOffered).
STARTTLSNotOffered bool `json:"starttls_not_offered,omitempty"`
// STARTTLSUnsupportedProto is true when the STARTTLS dialect is not
// implemented by this checker.
STARTTLSUnsupportedProto bool `json:"starttls_unsupported_proto,omitempty"`
// TLSHandshakeOK is true when a TLS handshake completed. It is
// independent from chain validity.
TLSHandshakeOK bool `json:"tls_handshake_ok,omitempty"`
// TLSVersionNum is the numeric TLS version negotiated (uint16 from
// crypto/tls). Zero means no handshake occurred. Kept as an unsigned
// integer so rules can compare against tls.VersionTLS12 without
// re-parsing a string.
TLSVersionNum uint16 `json:"tls_version_num,omitempty"`
TLSVersion string `json:"tls_version,omitempty"`
CipherSuite string `json:"cipher_suite,omitempty"`
CipherSuiteID uint16 `json:"cipher_suite_id,omitempty"`
// NoPeerCert is true when the handshake succeeded but the server sent
// no certificate.
NoPeerCert bool `json:"no_peer_cert,omitempty"`
HostnameMatch *bool `json:"hostname_match,omitempty"`
ChainValid *bool `json:"chain_valid,omitempty"`
ChainVerifyErr string `json:"chain_verify_err,omitempty"`
NotAfter time.Time `json:"not_after,omitempty"`
Issuer string `json:"issuer,omitempty"`
// IssuerDN is the leaf's issuer as an RFC 2253 DN string, suitable for
// matching the CCADB CAA Identifiers CSV "Subject" column when the AKI
// lookup misses.
IssuerDN string `json:"issuer_dn,omitempty"`
// IssuerAKI is the uppercase hex of the leaf's Authority Key Identifier
// extension (i.e. the issuer cert's SKI). This is the primary lookup key
// into the CCADB CAA Identifiers CSV ("Subject Key Identifier (Hex)").
IssuerAKI string `json:"issuer_aki,omitempty"`
Subject string `json:"subject,omitempty"`
DNSNames []string `json:"dns_names,omitempty"`
// Chain carries one entry per certificate presented by the server
// (leaf first, then intermediates in order). Each entry precomputes
// the four TLSA selector×matching_type hashes plus the raw DER so
// DANE consumers can match without re-handshaking or re-parsing.
Chain []CertInfo `json:"chain,omitempty"`
ElapsedMS int64 `json:"elapsed_ms,omitempty"`
// Error is a compatibility summary of whichever raw error applies.
// Left for any external consumer still inspecting it; rules should
// look at TCPError / HandshakeError instead.
Error string `json:"error,omitempty"`
}
// CertInfo describes one certificate in the presented chain together with
// pre-hashed forms suitable for DANE/TLSA matching (RFC 6698 §2.1).
//
// Hex fields are lowercase, matching the representation emitted by
// miekg/dns for TLSA RR Certificate fields.
type CertInfo struct {
// DERBase64 is the standard base64 encoding of the certificate's DER
// form. Carried so consumers can do matching-type 0 (Full) without
// requiring a precomputed "full" hash and for fallback inspection.
DERBase64 string `json:"der_base64,omitempty"`
// Subject / Issuer are short human-readable DNs for the HTML report.
Subject string `json:"subject,omitempty"`
Issuer string `json:"issuer,omitempty"`
// NotAfter is the certificate's expiry. Carried so editors can show
// "expires on …" without re-parsing the DER.
NotAfter time.Time `json:"not_after,omitempty"`
// Selector 0 = full certificate.
CertSHA256 string `json:"cert_sha256,omitempty"`
CertSHA512 string `json:"cert_sha512,omitempty"`
// Selector 1 = SubjectPublicKeyInfo.
SPKISHA256 string `json:"spki_sha256,omitempty"`
SPKISHA512 string `json:"spki_sha512,omitempty"`
// SPKIDERBase64 lets consumers handle (selector=1, matching=0) without
// re-parsing the certificate.
SPKIDERBase64 string `json:"spki_der_base64,omitempty"`
}
// Expiry thresholds shared by rules.
const (
ExpiringSoonThreshold = 14 * 24 * time.Hour
)