Enrol over http only to loopback; serve the API docs to this machine
The enrolment exemption covered any *.local host, which mDNS answers with whoever claims it; it is now localhost, 127.0.0.0/8 and ::1. /docs, /redoc and openapi.json stay closed in production and otherwise answer only a loopback caller unless API_DOCS_ENABLED is set, which the dev compose does, so `fluksio serve --host 0.0.0.0` no longer hands the schema to the network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GAeg4Jo6xjZzjwy9QDWMQ
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""The interactive schema: for this machine, or whoever API_DOCS_ENABLED names."""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from fluksio.core.config import settings
|
||||
from fluksio.main import app
|
||||
|
||||
SCHEMA = f"{settings.API_V1_STR}/openapi.json"
|
||||
|
||||
|
||||
def test_the_schema_is_served_only_on_this_machine_by_default(
|
||||
client: TestClient,
|
||||
) -> None:
|
||||
"""`fluksio serve --host 0.0.0.0` keeps ENVIRONMENT=local, and the network."""
|
||||
for path in (SCHEMA, "/docs", "/redoc"):
|
||||
assert client.get(path).status_code == 404 # "testclient" is not loopback
|
||||
|
||||
here = TestClient(app, client=("127.0.0.1", 50000))
|
||||
assert here.get(SCHEMA).status_code == 200
|
||||
assert here.get("/docs").status_code == 200
|
||||
|
||||
# Through a proxy on this machine, the caller is what counts.
|
||||
forwarded = {"x-forwarded-for": "203.0.113.7"}
|
||||
assert here.get(SCHEMA, headers=forwarded).status_code == 404
|
||||
|
||||
with patch("fluksio.core.config.settings.API_DOCS_ENABLED", True):
|
||||
assert client.get(SCHEMA).status_code == 200
|
||||
@@ -182,6 +182,27 @@ def test_a_claim_the_portal_refuses_keeps_the_connection(
|
||||
assert db.get(User, enrolled.id).portal_sub == "portal-user-1"
|
||||
|
||||
|
||||
def test_a_cleartext_portal_must_be_this_machine() -> None:
|
||||
"""An mDNS name is answered by whoever claims it on the LAN.
|
||||
|
||||
Over http, that is who would receive the claim code and the credential.
|
||||
"""
|
||||
from fluksio.cloud import enroll
|
||||
|
||||
with patch("fluksio.cloud.enroll.httpx.post") as post:
|
||||
for portal in ("http://hub.local", "http://192.168.1.20:8000"):
|
||||
with pytest.raises(enroll.EnrollError) as refused:
|
||||
enroll.redeem_claim(portal, "ABCD-EFGH")
|
||||
assert refused.value.status == 422
|
||||
post.assert_not_called()
|
||||
|
||||
post.return_value = Mock(status_code=404)
|
||||
for portal in ("http://localhost:8000", "http://127.0.0.2", "http://[::1]"):
|
||||
with pytest.raises(enroll.EnrollError) as unknown:
|
||||
enroll.redeem_claim(portal, "ABCD-EFGH")
|
||||
assert unknown.value.status == 400 # past the check, at the portal
|
||||
|
||||
|
||||
def test_enrolling_needs_a_superuser(
|
||||
client: TestClient, normal_user_token_headers: dict[str, str]
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user