feat(agent-auth): session tokens replace per-request scrypt (item 25)

Panel: after one scrypt-verified key auth, issue a random 24h
session token; store only its SHA-256 hash + expiry on the agent
record. authenticateAgent tries the token path first (constant-time
hash compare, active + unexpired), then the original fingerprint /
legacy VerifySecret path, and hands fresh tokens out via the
X-Agent-Session-Token response headers. Revoke() now clears token
material at all three revocation sites.

Agent: persist the token in the config dir (0600, temp+rename like
keys.json), present it as bearer, and on any 401 clear it and retry
once with the key (which re-issues). Old panels never send the
headers, so key-only operation is unchanged; old agents ignore the
headers. Version stays 0.6.8.

Tests: panel — issuance, hash-only storage, scrypt-free token path
(sabotaged KeyHash still authenticates), expiry fallback + rotation,
hash-mismatch fallback, revocation, per-record binding, key-only
rotation. Agent — capture/persist/reuse against a fake panel, 401
fallback and re-issue, old-panel key-only, client-side expiry,
double rejection surfacing without retry loops.
This commit is contained in:
aaaamoooo
2026-10-21 13:12:19 +00:00
parent 18074daf50
commit 408b03ee68
6 changed files with 796 additions and 100 deletions
+8
View File
@@ -132,6 +132,14 @@ using sing-box's own protobuf stubs. The Go agent keeps that exact design:
## Known divergences (all behavior-preserving)
0. **Panel session tokens (round-4 item 25, protocol superset)**: against a
panel that issues them, the Go agent persists the short-lived session
token (`session-token.json` in the config dir, 0600) and presents it as
the bearer instead of its key, falling back to the key on any 401. The
Node agent has no such concept; wire format (a Bearer value) and all
endpoints are unchanged, and panels without token support are
unaffected. Divergent by design — it exists to take scrypt off the
panel's per-request path.
1. **JSON key order inside resource files / combined config**: Go reserializes
nested objects with alphabetically sorted keys; JS preserves insertion
order. Semantically identical for sing-box and for the panel. Side effect: