feat(felhomsshd): dedicated OOB sshd instance + port-adaptive belt (H1 Parts 2-4 agent)
internal/felhomsshd: agent-managed felhom-sshd (claim port [8822,2222,8022,62222] loud-fail-on-exhaustion; render config→sshd -t→reload never-restart-on-change [SF-2]; operator authorized_keys from the hub block outside ~/.ssh [SF-3]); the static-table nft belt mutating ONLY @operator_ips + @ssh_port [trap 4]; health/heal (reset-failed-then-restart with 10min cooldown, NEVER restart onto an invalid config) + the oob heartbeat stanza. configs/felhom-sshd.service (SAFE, no RuntimeDirectory [SF-1]). FELHOM_SSHD + FELHOM_OOB sudoers (set-elements only). oob.enabled config DEFAULT FALSE. Wired into main like wgtunnel. Non-hollow tests: claim clean/contention/idempotent/exhaustion; config safe+byte-stable+refuses-:22; belt mutate-then-idempotent + never-touches-rules; heal no-restart-on-invalid-config + cooldown; status reflects block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -31,6 +31,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/desired"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/dr"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/escrow"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/felhomsshd"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/guesthook"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/lanresolver"
|
||||
@@ -706,6 +707,28 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
}
|
||||
}
|
||||
|
||||
// OOB operator access (H1): the dedicated felhom-sshd instance + port-adaptive belt + health.
|
||||
// DEFAULT OFF (oob.enabled) until the operator endpoint + static belt table exist. Consumes the
|
||||
// SAME wireguard desired-state block (oob_peer_ip → belt, oob_operator_ssh_key → authorized_keys).
|
||||
oobServers := 0
|
||||
var oobLoop *felhomsshd.Loop
|
||||
{
|
||||
oc := cfg.OOB.WithDefaults()
|
||||
if oc.Enabled {
|
||||
oobMode := proxmox.RunnerMode(cfg.Privileged.Mode)
|
||||
if oobMode == "" {
|
||||
oobMode = proxmox.RunnerSudo
|
||||
}
|
||||
oobRunner := &proxmox.ExecRunner{Mode: oobMode, SudoPath: cfg.Privileged.SudoPath}
|
||||
oobMgr := felhomsshd.NewManager(oobRunner, oc.StateDir, logger)
|
||||
oobBelt := felhomsshd.NewBelt(oobRunner, logger)
|
||||
oobLoop = felhomsshd.NewLoop(oobMgr, oobBelt, time.Duration(oc.IntervalSeconds)*time.Second, logger)
|
||||
desiredSyncer.AddConsumer(oobLoop) // raw desired-state → oob_peer_ip + operator key
|
||||
collector.SetOOBReporter(oobLoop) // heartbeat oob health stanza
|
||||
logger.Info("felhomsshd (OOB): enabled", "interval_s", oc.IntervalSeconds, "state_dir", oc.StateDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Run reconcile, the hub loop, the storage watchdog, the restore-test scheduler, the PBS
|
||||
// verify loop, (optionally) the local-API server, and (optionally) the LAN resolver loop
|
||||
// concurrently; any one returning ends the daemon (ctx cancel tears down the rest).
|
||||
@@ -768,6 +791,10 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
wgServers = 1
|
||||
go func() { errc <- wgLoop.Run(ctx) }()
|
||||
}
|
||||
if oobLoop != nil {
|
||||
oobServers = 1
|
||||
go func() { errc <- oobLoop.Run(ctx) }()
|
||||
}
|
||||
|
||||
// TASK D1 Scenario D: if this process is a JUST-FLIPPED self-update (a pending marker names THIS
|
||||
// version), commit it after a clean dwell — but only now that core init is done (config parsed,
|
||||
@@ -777,8 +804,8 @@ func runDaemon(cfg config.Config, logger *slog.Logger) int {
|
||||
go selfUpdateMgr.MaybeCommit(ctx)
|
||||
|
||||
err = <-errc
|
||||
stop() // tear down the siblings on the first exit
|
||||
for i := 0; i < 4+localServers+lanServers+wgServers; i++ { // wait for the other goroutines
|
||||
stop() // tear down the siblings on the first exit
|
||||
for i := 0; i < 4+localServers+lanServers+wgServers+oobServers; i++ { // wait for the other goroutines
|
||||
<-errc
|
||||
}
|
||||
if err != nil && err != context.Canceled {
|
||||
|
||||
Reference in New Issue
Block a user