fix(security): require token for Tailscale exposure, harden auth - #105
Merged
Conversation
Close a HIGH-severity auth bypass from an external security review: with no PI_WEB_TOKEN, startup published the loopback server via Tailscale Serve and allowlisted the tailnet hostname for tokenless access, giving any tailnet peer unauthenticated access to POST /api/chat (agent RCE). Only configure Serve and allowlist its host when a token is set; otherwise stay loopback-only and say so. Also address two hardening notes from the same review: - Set the pi_token cookie Secure over HTTPS (detected via X-Forwarded-Proto, since Tailscale terminates TLS and the server itself listens on loopback). - Reject Web Push subscribe endpoints that are not absolute https:// URLs, preventing the push sender from being pointed at http/loopback/other schemes. Docs (backend.md, en/install.md) updated to the token-conditional behavior. Translated install.md locales still need a regen pass via build_userdocs.py.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes findings from an external security review of pi-web.
HIGH — tokenless agent access over Tailscale. With no
PI_WEB_TOKEN, startup rantailscale serve(which proxies the tailnet to the loopback server) and then allowlisted the tailnet hostname for tokenless access. Any tailnet peer could reachPOST /api/chatwith no credential and drive the agent to run arbitrary shell — RCE. The existing "refuse non-loopback bind without a token" guard missed it because the Go process genuinely stays on127.0.0.1; the Serve proxy is what reaches the tailnet. Fix: only configure Serve and allowlist its host when a token is set — otherwise stay loopback-only and print why.Also addresses two lower-priority hardening notes from the same review:
Secure: thepi_tokencookie is nowSecureon HTTPS, detected viaX-Forwarded-Proto(Tailscale terminates TLS; the server itself listens on loopback HTTP, sor.TLSis always nil)./api/push/subscribenow rejects endpoints that aren't absolutehttps://URLs, so the push sender can't be pointed athttp:///loopback/other schemes.Changes
internal/app/app.go— gate Tailscale Serve + allowlist onPI_WEB_TOKENinternal/auth/auth.go—Securecookie viaisTLSRequest(+ tests)internal/server/push.go—validPushEndpointhttps check (+ tests)docs/architecture/backend.md,user-docs/en/install.md— document token-conditional behaviorTesting
make checkgreen (lint, format, tests, build, vet, install test).Follow-up
Translated
user-docs/<lang>/install.mdneed a regen pass viapython3 scripts/build_userdocs.py(kept out to keep this diff scoped).