feat(ned): CSRF/DNS-rebinding guard on TCP listener; deprecate web tokens #83
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "pr/ned-csrf-host-guard"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes the two holes an ACL-only security model can't cover, and deprecates token auth for the web client.
What and why
Tailnet ACLs + the Unix socket's OS permissions are now the security story. This PR adds the minimal in-app surface that network ACLs structurally can't provide, and removes the token-by-URL path that leaked secrets into history.
1. CSRF / DNS-rebinding guard on the TCP listener (
ned/handler.py,ned/daemon.py)evil.examplerebound to127.0.0.1or the Tailscale IP) get403 Forbidden host header.Originheader, its hostname (and explicit port) must match theHostheader. No third-party website can drive state-changing POSTs (trash,expunge,send,sync, ...) against a tokenless listener. Non-browser clients (desktop,ned-client,ned-mcp, curl) send noOriginand are unaffected.0.0.0.0/::binds made via--allow-insecure; the Origin match still applies there.2. Token deprecation for the web client (#3)
?token=query parameter is removed from_check_auth(it leaked the secret into URLs/browser history; the SSEEventSourcewas the only consumer).Authorization: Bearerstill works for non-browser clients;settings.web_tokenand--tokenare markedDEPRECATED.docs/api.md(transports & auth),README.mdTailscale Serve walkthrough (dropped the token), OpenAPI security scheme note.Testing (per verify skill, all green)
pytest: 515 passed (added 11 request-level guard tests + host parsing/allowlist construction totest_ned_security.py)mypyon changed files: clean (the 4 remainingned/client.pyerrors are pre-existing on main)pyflakes: only the pre-existingsubprocessunused-import warning inned/main.pySuggested review steps
git checkout pr/ned-csrf-host-guardQT_QPA_PLATFORM=offscreen ~/.local/share/pipx/venvs/lazarus-mail/bin/python -m pytest tests/test_ned_security.py tests/test_ned.py -qned --host 127.0.0.1 --port 8080, thencurl -H "Host: evil.example" http://127.0.0.1:8080/api/v1/ping→ 403.