Go 1.25 + capsh + Coverage: Warum plötzlich "go" im PATH fehlt

Nach dem Update auf Go 1.25 scheitert `go test -cover` unter capsh, weil der PATH beschnitten ist und Re-exec mehr PATH-Suche triggert. Fix: PATH setzen

15.08.2025 · Notizen aus der Testhölle · Tools: Go 1.25.0, Linuxbrew, capsh, CAP_NET_RAW, pro-bing v0.7.0

TL;DR

Nach dem Update auf Go 1.25.0 schlugen meine Tests mit -cover in Paketen ohne eigene Tests fehl, wenn ich sie wegen ICMP mit capsh und CAP_NET_RAW starte. Ursache: sudo/capsh liefern einen beschnittenen PATH, während go test bei Coverage intern erneut Tools/go über den PATH aufruft. Fix: PATH beim Aufruf setzen.


Setup

Ich teste bei mein Online-Status-Test mit ICMP-Ping über:

  • Modul: github.com/prometheus-community/pro-bing v0.7.0
  • Das braucht Root oder CAP_NET_RAW für „echten“ (icmp/privileged) Ping.
  • Deshalb rufe ich go test via capsh auf:
sudo capsh --caps="cap_net_raw+ep" -- -c "/home/linuxbrew/.linuxbrew/bin/go test -cover ./..."

Das lief bis Go 1.24 problemlos.


Symptom nach Update auf Go 1.25.0

Mit -cover knallt es in Paketen ohne Tests:

github.com/AleksCee/dsl_test_go: exec: "go": executable file not found in $PATH
github.com/AleksCee/dsl_test_go/cmd: exec: "go": executable file not found in $PATH
github.com/AleksCee/dsl_test_go/database: exec: "go": executable file not found in $PATH
ok   github.com/AleksCee/dsl_test_go/hosttest (cached) coverage: 50.9% of statements
ok   github.com/AleksCee/dsl_test_go/mail     (cached) coverage: 62.5% of statements
ok   github.com/AleksCee/dsl_test_go/properties (cached) coverage: 93.8% of statements
ok   github.com/AleksCee/dsl_test_go/version     (cached) coverage: 100.0% of statements

Setze ich den PATH explizit, läuft es:

sudo capsh --caps="cap_net_raw+ep" -- -c 'PATH=/home/linuxbrew/.linuxbrew/bin:$PATH /home/linuxbrew/.linuxbrew/bin/go test -cover ./...'

Erwartbares Ergebnis (0.0 % für Pakete ohne Tests, Rest ok):

github.com/AleksCee/dsl_test_go              coverage: 0.0% of statements
github.com/AleksCee/dsl_test_go/cmd          coverage: 0.0% of statements
github.com/AleksCee/dsl_test_go/database     coverage: 0.0% of statements
ok   github.com/AleksCee/dsl_test_go/hosttest (cached) coverage: 50.9% of statements
ok   github.com/AleksCee/dsl_test_go/mail     (cached) coverage: 62.5% of statements
ok   github.com/AleksCee/dsl_test_go/properties (cached) coverage: 93.8% of statements
ok   github.com/AleksCee/dsl_test_go/version     (cached) coverage: 100.0% of statements

Ohne Coverage ist sowieso alles gut:

sudo capsh --caps="cap_net_raw+ep" -- -c "/home/linuxbrew/.linuxbrew/bin/go test ./..."

Ausgabe:

?    github.com/AleksCee/dsl_test_go            [no test files]
?    github.com/AleksCee/dsl_test_go/cmd        [no test files]
?    github.com/AleksCee/dsl_test_go/database   [no test files]
ok   github.com/AleksCee/dsl_test_go/hosttest   (cached)
ok   github.com/AleksCee/dsl_test_go/mail       (cached)
ok   github.com/AleksCee/dsl_test_go/properties (cached)
ok   github.com/AleksCee/dsl_test_go/version    (cached)

Was da wirklich passiert (Arbeitsthese)

  • sudo & secure_path: Unter sudo/capsh ist häufig ein restriktiver PATH aktiv. /home/linuxbrew/.linuxbrew/bin fehlt dann.
  • Re-exec bei Coverage: Seit den neueren Toolchain-Mechaniken ruft go test intern zusätzliche Tools (oder sogar go selbst) über den Namen auf. Das triggert PATH-Suche.
  • Spezialeffekt bei -cover ohne Tests: Auch wenn ein Paket keine Tests hat, verursacht -cover zusätzliche interne Schritte. Ohne passenden PATH findet der Subprozess kein goexec: "go": not found.

Kurz: Der erste go-Start klappt dank absolutem Pfad, Folgeprozesse stolpern über den beschnittenen PATH.


Lösungswege

1) PATH beim Aufruf setzen (sofort, pragmatisch)

sudo capsh --caps="cap_net_raw+ep" -- -c 'PATH=/home/linuxbrew/.linuxbrew/bin:$PATH /home/linuxbrew/.linuxbrew/bin/go test -cover ./...'

Im Makefile:

GOBINPATH=$(shell which go)
CAPSH_PATH=$(shell dirname $(shell which go)):$(PATH)

...

test:
  @sudo capsh --caps="cap_net_raw+ep" -- -c 'PATH=$(CAPSH_PATH) $(GOBINPATH) test -v -cover ./...'

2) Ping ohne Root? Ja, aber…

pro-bing kann unprivilegiert im UDP-Modus, liefert bei mir aber ungenauere Ergebnisse. Für „echtes“ ICMP bleibe ich bei capsh + CAP_NET_RAW.


Fazit

Kein Bug in pro-bing, kein reines capsh-Thema, sondern ein Timing zwischen Coverage-Re-exec und beschnittenem PATH unter sudo.
Mit einem gesetzten PATH ist die Welt wieder gerade. Ping bleibt privilegiert, Tests bleiben grün, und go bleibt im PATH, wo es hingehört.