server: self-test — sysctl audit + egress-MTU self-proof ("server proven good")
A measurement server must prove its own host isn't distorting results:
- sysctl audit (/proc/sys): flags accept_ra on a static host, ICMP
redirects, ICMP rate-limiting of the server's own errors, and disabled
TCP options — each a measurement-fidelity hazard, with the "why".
- egress-MTU self-proof: DF PMTUD probe (IP_MTU_DISCOVER + getsockopt
IP_MTU, no root — Linux-only, stub elsewhere) to external anchors. If the
server's own uplink is below 1500, client MTU tests measure THIS server,
so we say so.
Exposed at GET /admin/selftest (full report) and as server_selftest
{mtu_ok, sysctl_ok} in the profile so clients can trust or skip MTU tests.
Recommended deploy/99-echolot-sysctl.conf + README section.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c9e0d06ea2
commit
4ae744aae5
@@ -18,6 +18,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -37,6 +39,7 @@ import (
|
||||
"echo-lot.app/server/internal/config"
|
||||
"echo-lot.app/server/internal/control"
|
||||
"echo-lot.app/server/internal/dataplane"
|
||||
"echo-lot.app/server/internal/selftest"
|
||||
"echo-lot.app/server/internal/selfupdate"
|
||||
"echo-lot.app/server/internal/session"
|
||||
"echo-lot.app/server/internal/store"
|
||||
@@ -138,11 +141,42 @@ func serve(cfg *config.Config) error {
|
||||
}(addr, ln)
|
||||
}
|
||||
|
||||
// Self-test: prove the host is a clean measurement target. Sysctl audit is
|
||||
// instant; the egress-MTU proof does network round trips, so publish the
|
||||
// sysctl-only report immediately and swap in the full one when it lands.
|
||||
var selftestPtr atomic.Pointer[selftest.Report]
|
||||
initial := selftest.Report{Sysctls: selftest.Sysctls()}
|
||||
selftestPtr.Store(&initial)
|
||||
for _, c := range initial.Sysctls {
|
||||
if c.Severity == selftest.Warn {
|
||||
slog.Warn("sysctl not measurement-clean", "sysctl", c.Name, "got", c.Got, "want", c.Want, "why", c.Why)
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
r := selftest.Run(config.Addrs(cfg.MTUProbeTargets))
|
||||
selftestPtr.Store(&r)
|
||||
for _, m := range r.EgressMTU {
|
||||
if !m.FullMTU {
|
||||
slog.Warn("egress MTU below 1500 — client MTU results measure THIS server, not the client",
|
||||
"target", m.Target, "discovered_mtu", m.DiscoveredMTU, "err", m.Err)
|
||||
}
|
||||
}
|
||||
slog.Info("self-test complete", "sysctl_ok", r.SysctlOK, "mtu_ok", r.MTUOK)
|
||||
}()
|
||||
ctl.ProvenGood = func() (mtuOK, sysctlOK bool) {
|
||||
r := selftestPtr.Load()
|
||||
return r.MTUOK, r.SysctlOK
|
||||
}
|
||||
|
||||
// Admin/health (plain HTTP, localhost by default; spec §7)
|
||||
admin := http.NewServeMux()
|
||||
admin.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
fmt.Fprintf(w, `{"ok":true,"version":%q}`, Version)
|
||||
})
|
||||
admin.HandleFunc("GET /admin/selftest", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(selftestPtr.Load())
|
||||
})
|
||||
// TODO(spec §7): enrollment token management + device list. Until the
|
||||
// admin UI exists, mint tokens with: echolot-admin (or curl on this
|
||||
// listener once the endpoint lands).
|
||||
|
||||
Reference in New Issue
Block a user