c0f3e12483
statfs fsclass helper (network/autofs/stub/unknown, fail-open); probe not_network_fs assertion (stub can never verify — red-proven); deploy-time stub refusal (idle autofs proceeds — red-proven); distinct stub badge, stub wins over unreachable (unreachable line byte-identical); deployed select shows stored HDD_PATH (red-proven vs IsDefault-only). MinAgent unchanged 0.81.0. Gates green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
52 lines
1.9 KiB
Go
52 lines
1.9 KiB
Go
//go:build linux
|
|
|
|
package web
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
|
|
)
|
|
|
|
// platformNetProbeFSClass is the real namespace classifier (statfs f_type) the probe child runs.
|
|
// Unbounded on purpose: the child's whole run is already bounded by the parent's netProbeTimeout.
|
|
func platformNetProbeFSClass(dir string) string {
|
|
return system.ClassifyPathFS(dir)
|
|
}
|
|
|
|
// netProbeTimeout bounds one probe run (LAN write+readback is sub-second; a wedged share must not
|
|
// hold the orchestrator — the NFS soft/retry=0 options error out well inside this).
|
|
const netProbeTimeout = 30 * time.Second
|
|
|
|
// runNetProbe re-execs this binary as `felhom-controller --netprobe <dir>` with uid/gid 1000
|
|
// credentials (supplementary groups CLEARED — the probe must see exactly what a media app sees)
|
|
// and maps the exit code to a verdict. This is the production netProbeFn seam value.
|
|
func runNetProbe(ctx context.Context, dir string) probeOutcome {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return probeOutcome{OK: false, Category: "probe_io", Detail: "probe re-exec: executable path: " + err.Error()}
|
|
}
|
|
pctx, cancel := context.WithTimeout(ctx, netProbeTimeout)
|
|
defer cancel()
|
|
cmd := exec.CommandContext(pctx, exe, "--netprobe", dir)
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Credential: &syscall.Credential{Uid: 1000, Gid: 1000, Groups: []uint32{}},
|
|
}
|
|
out, err := cmd.CombinedOutput()
|
|
output := strings.TrimSpace(string(out))
|
|
if err != nil {
|
|
if ee, ok := err.(*exec.ExitError); ok {
|
|
return netProbeVerdict(ee.ExitCode(), output)
|
|
}
|
|
// Spawn-level failure (EPERM on setuid = capability dropped somewhere — spike Q2 proved the
|
|
// default container HAS the caps, so this is a real config regression worth the raw detail).
|
|
return probeOutcome{OK: false, Category: "probe_io", Detail: "probe spawn failed: " + err.Error() + " | " + output}
|
|
}
|
|
return netProbeVerdict(0, output)
|
|
}
|