Validate the federation tester URI placeholder, escape the domain, set a client timeout, cap the response body, and ship CA certificates in the scratch image so HTTPS calls succeed. Sort hosts, connection reports, and errors when rendering so output is deterministic, and deduplicate TLS problems. Drop the deprecated aggregate Rule() and add tests for collection and rules.
90 lines
2.4 KiB
Go
90 lines
2.4 KiB
Go
package checker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
sdk "git.happydns.org/checker-sdk-go/checker"
|
|
)
|
|
|
|
// tlsChecksRule reviews the TLS-level findings the federation tester
|
|
// reports for every endpoint it managed to reach: certificate validity,
|
|
// matching server name, future expiry, presence of an Ed25519 key, and so
|
|
// on. One CheckState is emitted per reachable endpoint so the UI can pin
|
|
// the outcome on the exact address.
|
|
type tlsChecksRule struct{}
|
|
|
|
func (r *tlsChecksRule) Name() string { return "matrix.tls_checks" }
|
|
func (r *tlsChecksRule) Description() string {
|
|
return "Reviews the TLS posture on every reachable federation endpoint (certificate chain, hostname match, Ed25519 key, …)."
|
|
}
|
|
|
|
func (r *tlsChecksRule) Evaluate(ctx context.Context, obs sdk.ObservationGetter, _ sdk.CheckerOptions) []sdk.CheckState {
|
|
data, errSt := loadMatrixData(ctx, obs)
|
|
if errSt != nil {
|
|
return []sdk.CheckState{*errSt}
|
|
}
|
|
|
|
if len(data.ConnectionReports) == 0 {
|
|
return []sdk.CheckState{unknownState("matrix.tls_checks.skipped", "No endpoint reached: TLS posture could not be assessed.")}
|
|
}
|
|
|
|
addrs := make([]string, 0, len(data.ConnectionReports))
|
|
for addr := range data.ConnectionReports {
|
|
addrs = append(addrs, addr)
|
|
}
|
|
sort.Strings(addrs)
|
|
|
|
out := make([]sdk.CheckState, 0, len(addrs))
|
|
for _, addr := range addrs {
|
|
cr := data.ConnectionReports[addr]
|
|
var problems []string
|
|
seen := make(map[string]struct{})
|
|
add := func(p string) {
|
|
if p == "" {
|
|
return
|
|
}
|
|
if _, ok := seen[p]; ok {
|
|
return
|
|
}
|
|
seen[p] = struct{}{}
|
|
problems = append(problems, p)
|
|
}
|
|
if !cr.Checks.MatchingServerName {
|
|
add("server name does not match certificate")
|
|
}
|
|
if !cr.Checks.FutureValidUntilTS {
|
|
add("certificate expired or near expiry")
|
|
}
|
|
if !cr.Checks.ValidCertificates {
|
|
add("certificate chain is invalid")
|
|
}
|
|
if !cr.Checks.HasEd25519Key {
|
|
add("no Ed25519 signing key advertised")
|
|
}
|
|
if !cr.Checks.AllEd25519ChecksOK {
|
|
add("Ed25519 key verification failed")
|
|
}
|
|
for _, e := range cr.Errors {
|
|
add(e)
|
|
}
|
|
|
|
if len(problems) == 0 && cr.Checks.AllChecksOK {
|
|
st := passState("matrix.tls_checks.ok", "All TLS checks passed.")
|
|
st.Subject = addr
|
|
out = append(out, st)
|
|
continue
|
|
}
|
|
|
|
msg := "TLS checks failed."
|
|
if len(problems) > 0 {
|
|
msg = fmt.Sprintf("TLS checks failed: %s.", strings.Join(problems, "; "))
|
|
}
|
|
st := critState("matrix.tls_checks.fail", msg)
|
|
st.Subject = addr
|
|
out = append(out, st)
|
|
}
|
|
return out
|
|
}
|