fix(nat): scrub dashboard-issued credentials from all accepted token channels

The initial selective strip covered only the Authorization header, but the
dashboard also accepts its JWT via the ?token= query parameter and the
nz-jwt cookie (jwt.go TokenLookup channels). A dashboard credential arriving
through those channels would still reach a NAT backend.

Apply the same classify-then-strip rule to all three channels at NAT
ingress: values verified as dashboard-issued are removed, foreign values
are preserved byte-for-byte. Adds a ServeNAT flow test exercising the live
classifier and a wiring test pinning the gate injection in main().
This commit is contained in:
xvkong233 authored and naiba committed 2026-10-01 11:17:45 +00:00
1 parent 40240ed02b
commit 3ad985f36e
12 files changed
+806 -82

No files matched your search

+5 -4
View File
@@ -84,10 +84,11 @@ func ServeNAT(w http.ResponseWriter, r *http.Request, natConfig *model.NAT) {
// dashboard — the panel-signed JWT (verified by signature only, expiry
// deliberately ignored) and the panel API token — must never become origin
// credentials for the configured NAT backend. prepareNATCapability has
// already stripped every Authorization value IsDashboardCredential
// classified as dashboard-issued; whatever remains is either absent or a
// foreign credential, which is forwarded untouched because the NAT backend
// may authenticate with credentials of its own.
// already stripped them from every channel the panel's own TokenLookup
// accepts (Authorization header, ?token= query parameter, nz-jwt cookie);
// whatever remains on those channels is either absent or a foreign
// credential, which is forwarded untouched because the NAT backend may
// authenticate with credentials of its own.
wWrapped, err := utils.NewRequestWrapper(r, w)
if err != nil {
return