fix: whitelist peer-sync endpoint from session auth + CSRF
/api/cells/peer-sync/permissions is called over the WireGuard tunnel by remote cells — they have no session cookie and cannot produce a CSRF token. The endpoint authenticates via source IP (must be in the remote cell's vpn_subnet) and WireGuard public key instead. Without this, the global enforce_auth hook returns 401 before the route handler runs, so all cross-cell permission pushes fail even when the WG tunnel and iptables rules are correct. Also adds a test verifying the route can be reached without a session. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,9 @@ def enforce_auth():
|
||||
# Always allow non-API paths and auth namespace
|
||||
if not path.startswith('/api/') or path.startswith('/api/auth/'):
|
||||
return None
|
||||
# Cell peer-sync endpoints authenticate via source IP + WG pubkey — not session
|
||||
if path.startswith('/api/cells/peer-sync/'):
|
||||
return None
|
||||
# Only enforce when auth_manager has been properly initialised and seeded.
|
||||
# When the user store is empty (file missing or unreadable — typical in
|
||||
# unit tests and fresh installs), bypass enforcement so pre-auth test
|
||||
@@ -225,6 +228,9 @@ def check_csrf():
|
||||
path = request.path
|
||||
if not path.startswith('/api/') or path.startswith('/api/auth/'):
|
||||
return None
|
||||
# peer-sync uses IP+pubkey auth — no session, no CSRF token possible
|
||||
if path.startswith('/api/cells/peer-sync/'):
|
||||
return None
|
||||
token_session = session.get('csrf_token')
|
||||
if not token_session:
|
||||
# Session predates CSRF tokens (existing login) — issue a token now so
|
||||
|
||||
@@ -205,3 +205,20 @@ def test_anon_can_reach_auth_namespace(anon_client):
|
||||
# 401 is expected here but it must originate from the route, not a redirect/block
|
||||
# on a non-auth path. The response should be JSON, not a redirect (3xx).
|
||||
assert r.status_code not in (301, 302, 403)
|
||||
|
||||
|
||||
def test_anon_can_reach_peer_sync_endpoint(anon_client):
|
||||
"""POST /api/cells/peer-sync/* must not be blocked by session auth.
|
||||
|
||||
The peer-sync endpoint authenticates via source IP + WireGuard pubkey.
|
||||
It must not return 401/403 from the global enforce_auth hook — the route
|
||||
handler itself produces any rejection.
|
||||
"""
|
||||
r = anon_client.post(
|
||||
'/api/cells/peer-sync/permissions',
|
||||
json={},
|
||||
content_type='application/json',
|
||||
)
|
||||
# 400 (bad payload) or 403 (IP/pubkey rejected) are acceptable — 401 from
|
||||
# the global auth hook is NOT acceptable because the route has its own guard.
|
||||
assert r.status_code != 401
|
||||
|
||||
Reference in New Issue
Block a user