139 lines
5.8 KiB
Go
139 lines
5.8 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// Rule returns a single aggregate rule covering the whole XMPP posture.
|
|
// Kept for backwards compatibility with callers that expect exactly one
|
|
// CheckRule; prefer Rules() which splits concerns into individual rules.
|
|
func Rule() sdk.CheckRule {
|
|
return &xmppRule{}
|
|
}
|
|
|
|
// Rules returns the full list of CheckRules exposed by the XMPP checker,
|
|
// one per concern so callers can see at a glance which checks passed and
|
|
// which did not, instead of looking up Code on a single monolithic rule.
|
|
func Rules() []sdk.CheckRule {
|
|
return []sdk.CheckRule{
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.srv_c2s",
|
|
description: "Verifies that client-to-server SRV records (_xmpp-client / _xmpps-client / _jabber) are published and resolvable.",
|
|
codes: []string{CodeNoSRV, CodeSRVServfail, CodeLegacyJabber},
|
|
passCode: "xmpp.srv_c2s.ok",
|
|
passMessage: "Client-to-server SRV records are published and resolve cleanly.",
|
|
modeFilter: modeFilterC2S,
|
|
},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.srv_s2s",
|
|
description: "Verifies that server-to-server SRV records (_xmpp-server / _xmpps-server) are published and resolvable.",
|
|
codes: []string{CodeNoSRV, CodeSRVServfail},
|
|
passCode: "xmpp.srv_s2s.ok",
|
|
passMessage: "Server-to-server SRV records are published and resolve cleanly.",
|
|
modeFilter: modeFilterS2S,
|
|
},
|
|
&c2sReachableRule{},
|
|
&s2sReachableRule{},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.starttls_required",
|
|
description: "Verifies that STARTTLS is advertised and required on every reachable c2s/s2s endpoint.",
|
|
codes: []string{CodeStartTLSMissing, CodeStartTLSNotRequired, CodeStartTLSFailed},
|
|
passCode: "xmpp.starttls_required.ok",
|
|
passMessage: "STARTTLS is offered and required on every reachable endpoint.",
|
|
},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.sasl_mechanisms",
|
|
description: "Reviews the c2s SASL mechanisms offer (presence of SCRAM, absence of password-equivalent PLAIN-only).",
|
|
codes: []string{CodeSASLPlainOnly, CodeSASLNoSCRAM, CodeSASLNoSCRAMPlus},
|
|
passCode: "xmpp.sasl_mechanisms.ok",
|
|
passMessage: "c2s advertises a strong SASL mechanism (SCRAM family).",
|
|
modeFilter: modeFilterC2S,
|
|
},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.s2s_dialback",
|
|
description: "Verifies that s2s endpoints advertise dialback or SASL EXTERNAL after TLS (federation auth).",
|
|
codes: []string{CodeS2SNoAuth, CodeS2SProbeIncomplete},
|
|
passCode: "xmpp.s2s_dialback.ok",
|
|
passMessage: "Every reachable s2s endpoint advertises dialback or SASL EXTERNAL.",
|
|
modeFilter: modeFilterS2S,
|
|
},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.ipv6_reachable",
|
|
description: "Flags deployments that are only reachable over IPv4.",
|
|
codes: []string{CodeNoIPv6},
|
|
passCode: "xmpp.ipv6_reachable.ok",
|
|
passMessage: "At least one endpoint is reachable over IPv6.",
|
|
},
|
|
&simpleXMPPConcernRule{
|
|
name: "xmpp.direct_tls",
|
|
description: "Flags c2s deployments that do not publish XEP-0368 direct-TLS SRV records.",
|
|
codes: []string{CodeNoDirectTLS},
|
|
passCode: "xmpp.direct_tls.ok",
|
|
passMessage: "XEP-0368 direct-TLS SRV records are published for c2s.",
|
|
modeFilter: modeFilterC2S,
|
|
},
|
|
&tlsQualityRule{},
|
|
}
|
|
}
|
|
|
|
// modeFilter lets a rule short-circuit to "skipped" when the selected mode
|
|
// excludes the concern (e.g. c2s-specific rule running in mode=s2s).
|
|
type modeFilter func(wantC2S, wantS2S bool) bool
|
|
|
|
func modeFilterC2S(wantC2S, _ bool) bool { return wantC2S }
|
|
func modeFilterS2S(_, wantS2S bool) bool { return wantS2S }
|
|
|
|
// simpleXMPPConcernRule covers the common shape: "derive the issue list,
|
|
// keep the ones matching these codes, emit them as states or a single pass
|
|
// state when none match".
|
|
type simpleXMPPConcernRule struct {
|
|
name string
|
|
description string
|
|
codes []string
|
|
passCode string
|
|
passMessage string
|
|
modeFilter modeFilter // optional
|
|
}
|
|
|
|
func (r *simpleXMPPConcernRule) Name() string { return r.name }
|
|
func (r *simpleXMPPConcernRule) Description() string { return r.description }
|
|
|
|
func (r *simpleXMPPConcernRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, opts sdk.CheckerOptions) []sdk.CheckState {
|
|
data, errSt := loadXMPPData(ctx, obs)
|
|
if errSt != nil {
|
|
return []sdk.CheckState{*errSt}
|
|
}
|
|
wantC2S, wantS2S := wantsFromOpts(opts)
|
|
if r.modeFilter != nil && !r.modeFilter(wantC2S, wantS2S) {
|
|
return []sdk.CheckState{notTestedState(r.name+".skipped", "Not applicable to the selected mode.")}
|
|
}
|
|
issues := filterIssuesByCodes(deriveIssues(data, wantC2S, wantS2S), r.codes...)
|
|
if len(issues) == 0 {
|
|
return []sdk.CheckState{passState(r.passCode, r.passMessage)}
|
|
}
|
|
return statesFromIssues(issues)
|
|
}
|
|
|
|
// tlsQualityRule folds findings from a downstream TLS checker into XMPP
|
|
// output, so cert chain / hostname / expiry problems show up on the XMPP
|
|
// service page without needing a separate glance at the TLS report.
|
|
type tlsQualityRule struct{}
|
|
|
|
func (r *tlsQualityRule) Name() string { return "xmpp.tls_quality" }
|
|
func (r *tlsQualityRule) Description() string {
|
|
return "Folds the downstream TLS checker findings (certificate chain, hostname match, expiry) onto the XMPP service."
|
|
}
|
|
|
|
func (r *tlsQualityRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
|
related, _ := obs.GetRelated(ctx, TLSRelatedKey)
|
|
if len(related) == 0 {
|
|
return []sdk.CheckState{notTestedState("xmpp.tls_quality.skipped", "No related TLS observation available (no TLS checker downstream, or no probe yet).")}
|
|
}
|
|
issues := tlsIssuesFromRelated(related)
|
|
if len(issues) == 0 {
|
|
return []sdk.CheckState{passState("xmpp.tls_quality.ok", "Downstream TLS checker reports no issues on the XMPP endpoints.")}
|
|
}
|
|
return statesFromIssues(issues)
|
|
}
|