Split monolithic rule into per-test rules, collect gathers facts only
This commit is contained in:
parent
5b71e85f49
commit
4177fcdc7b
14 changed files with 758 additions and 259 deletions
|
|
@ -22,33 +22,67 @@ const (
|
|||
MaxConcurrentProbes = 32
|
||||
)
|
||||
|
||||
// Severity values used in Issue.Severity (lowercase, ascii).
|
||||
const (
|
||||
SeverityCrit = "crit"
|
||||
SeverityWarn = "warn"
|
||||
SeverityInfo = "info"
|
||||
)
|
||||
|
||||
// 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. Field names
|
||||
// mirror what consumers already parse (checker-xmpp's tlsProbeView).
|
||||
// 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"`
|
||||
TLSVersion string `json:"tls_version,omitempty"`
|
||||
CipherSuite string `json:"cipher_suite,omitempty"`
|
||||
HostnameMatch *bool `json:"hostname_match,omitempty"`
|
||||
ChainValid *bool `json:"chain_valid,omitempty"`
|
||||
NotAfter time.Time `json:"not_after,omitempty"`
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
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.
|
||||
|
|
@ -65,8 +99,11 @@ type TLSProbe struct {
|
|||
// DANE consumers can match without re-handshaking or re-parsing.
|
||||
Chain []CertInfo `json:"chain,omitempty"`
|
||||
ElapsedMS int64 `json:"elapsed_ms,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Issues []Issue `json:"issues,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
|
||||
|
|
@ -101,10 +138,7 @@ type CertInfo struct {
|
|||
SPKIDERBase64 string `json:"spki_der_base64,omitempty"`
|
||||
}
|
||||
|
||||
// Issue is a single TLS finding surfaced to the consumer.
|
||||
type Issue struct {
|
||||
Code string `json:"code"`
|
||||
Severity string `json:"severity"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Fix string `json:"fix,omitempty"`
|
||||
}
|
||||
// Expiry thresholds shared by rules.
|
||||
const (
|
||||
ExpiringSoonThreshold = 14 * 24 * time.Hour
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue