Skip to content

feat(serve): require an auth token by default (secure on shared login nodes)#97

Merged
RealZST merged 1 commit into
mainfrom
feat/serve-secure-default-token
Jun 30, 2026
Merged

feat(serve): require an auth token by default (secure on shared login nodes)#97
RealZST merged 1 commit into
mainfrom
feat/serve-secure-default-token

Conversation

@RealZST

@RealZST RealZST commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Problem

hk serve defaulted to --host 127.0.0.1 with no token — a token was auto-generated only for non-localhost binds. On a shared host (e.g. an HPC login node) the loopback interface is not isolated per user, so binding 127.0.0.1 does not keep other local users out. Any user on the same node could ss -ltn | grep 7070, then curl 127.0.0.1:7070 and reach the HarnessKit UI — including install / clone / toggle actions — with no auth.

Change

Secure-by-default token, enforced even for 127.0.0.1:

  • Auto-generate + persist the token at ~/.harnesskit/web-token (mode 0600), reused across restarts. A file with looser permissions is treated as compromised and regenerated.
  • Auto-generated (not passed via --token) so it never leaks through ps//proc/<pid>/cmdline to other users on the host.
  • --no-token opt-out for a trusted single-user machine.
  • Embed the token in the 127.0.0.1 startup URL and store it in localStorage, so one ?token= visit logs in and survives new tabs/reloads (was sessionStorage).
  • README: setup steps point at the ?token= URL and note it's saved for next time.

The static frontend shell is still served unauthenticated (no secrets in it); all /api/ routes are token-gated, so other users get an empty, non-functional shell at most. Hiding the shell/port entirely (query-param auth on all routes, or a Unix-socket mode) is left as a follow-up.

Testing

  • cargo test --workspace + npm test green. Added 3 unit tests for the token-file logic (round-trip + 0600, reject group/world-readable, re-harden on overwrite).
  • Cross-compiled x86_64-unknown-linux-musl and tested on a real shared HPC login node: from a second session on the same node, unauthenticated /api/* returns 401; ~/.harnesskit/web-token is -rw-------. Another user sees only the empty static shell.

🤖 Generated with Claude Code

…skit/web-token

Auto-generate and enforce a web access token even for 127.0.0.1 binds. On a
shared host (e.g. an HPC login node) loopback is not isolated per user, so
binding 127.0.0.1 alone does not keep other local users out — only the token
does. Previously a token was generated only for non-localhost binds, leaving
the common default wide open on shared login nodes.

- Persist the token at ~/.harnesskit/web-token (mode 0600) and reuse it across
  restarts; a file with looser permissions is treated as compromised and
  regenerated.
- Add --no-token to opt out on a trusted single-user machine.
- Embed the token in the 127.0.0.1 startup URL, and store it in localStorage
  so one ?token= visit logs in and survives new tabs/reloads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@RealZST RealZST merged commit a120a3f into main Jun 30, 2026
3 checks passed
@RealZST RealZST deleted the feat/serve-secure-default-token branch June 30, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant