Compare commits
1 Commits
a4f8a2687f
...
v1.0.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| 65d5dfdbd6 |
@@ -12,6 +12,11 @@ from gondulf.config import Config
|
|||||||
|
|
||||||
logger = logging.getLogger("gondulf.middleware.https_enforcement")
|
logger = logging.getLogger("gondulf.middleware.https_enforcement")
|
||||||
|
|
||||||
|
# Internal endpoints exempt from HTTPS enforcement
|
||||||
|
# These are called by Docker health checks, load balancers, and monitoring systems
|
||||||
|
# that connect directly to the container without going through the reverse proxy.
|
||||||
|
HTTPS_EXEMPT_PATHS = {"/health", "/metrics"}
|
||||||
|
|
||||||
|
|
||||||
def is_https_request(request: Request) -> bool:
|
def is_https_request(request: Request) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -93,6 +98,12 @@ class HTTPSEnforcementMiddleware(BaseHTTPMiddleware):
|
|||||||
# Continue processing
|
# Continue processing
|
||||||
return await call_next(request)
|
return await call_next(request)
|
||||||
|
|
||||||
|
# Exempt internal endpoints from HTTPS enforcement
|
||||||
|
# These are used by Docker health checks, load balancers, etc.
|
||||||
|
# that connect directly without going through the reverse proxy.
|
||||||
|
if request.url.path in HTTPS_EXEMPT_PATHS:
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
# Production mode: Enforce HTTPS
|
# Production mode: Enforce HTTPS
|
||||||
if not is_https_request(request):
|
if not is_https_request(request):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|||||||
@@ -67,3 +67,66 @@ class TestHTTPSEnforcement:
|
|||||||
response = client.get("/")
|
response = client.get("/")
|
||||||
# TestClient doesn't enforce HTTPS, but middleware should allow it
|
# TestClient doesn't enforce HTTPS, but middleware should allow it
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
def test_health_endpoint_exempt_from_https_in_production(
|
||||||
|
self, client, monkeypatch
|
||||||
|
):
|
||||||
|
"""Test /health endpoint is accessible via HTTP in production mode.
|
||||||
|
|
||||||
|
Docker health checks and load balancers call the health endpoint directly
|
||||||
|
without going through the reverse proxy, so it must work over HTTP.
|
||||||
|
The key assertion is that we don't get a 301 redirect to HTTPS.
|
||||||
|
"""
|
||||||
|
from gondulf.config import Config
|
||||||
|
|
||||||
|
monkeypatch.setattr(Config, "DEBUG", False)
|
||||||
|
monkeypatch.setattr(Config, "TRUST_PROXY", False)
|
||||||
|
|
||||||
|
# HTTP request to /health should NOT redirect to HTTPS
|
||||||
|
response = client.get(
|
||||||
|
"http://localhost:8000/health", follow_redirects=False
|
||||||
|
)
|
||||||
|
# Should NOT be 301 redirect - actual status depends on DB state (200/503)
|
||||||
|
assert response.status_code != 301
|
||||||
|
# Verify it reached the health endpoint (not redirected)
|
||||||
|
assert response.status_code in (200, 503)
|
||||||
|
|
||||||
|
def test_health_endpoint_head_request_in_production(self, client, monkeypatch):
|
||||||
|
"""Test HEAD request to /health is not redirected in production.
|
||||||
|
|
||||||
|
Docker health checks may use HEAD requests. The key is that the
|
||||||
|
middleware doesn't redirect to HTTPS - the actual endpoint behavior
|
||||||
|
(405 Method Not Allowed) is separate from HTTPS enforcement.
|
||||||
|
"""
|
||||||
|
from gondulf.config import Config
|
||||||
|
|
||||||
|
monkeypatch.setattr(Config, "DEBUG", False)
|
||||||
|
monkeypatch.setattr(Config, "TRUST_PROXY", False)
|
||||||
|
|
||||||
|
# HEAD request to /health should NOT redirect to HTTPS
|
||||||
|
response = client.head(
|
||||||
|
"http://localhost:8000/health", follow_redirects=False
|
||||||
|
)
|
||||||
|
# Should NOT be 301 redirect
|
||||||
|
assert response.status_code != 301
|
||||||
|
|
||||||
|
def test_metrics_endpoint_exempt_from_https_in_production(
|
||||||
|
self, client, monkeypatch
|
||||||
|
):
|
||||||
|
"""Test /metrics endpoint is accessible via HTTP in production mode.
|
||||||
|
|
||||||
|
Monitoring systems may call metrics directly without HTTPS.
|
||||||
|
"""
|
||||||
|
from gondulf.config import Config
|
||||||
|
|
||||||
|
monkeypatch.setattr(Config, "DEBUG", False)
|
||||||
|
monkeypatch.setattr(Config, "TRUST_PROXY", False)
|
||||||
|
|
||||||
|
# HTTP request to /metrics should not be redirected
|
||||||
|
# (endpoint may not exist yet, but should not redirect to HTTPS)
|
||||||
|
response = client.get(
|
||||||
|
"http://localhost:8000/metrics", follow_redirects=False
|
||||||
|
)
|
||||||
|
# Should return 404 (not found) not 301 (redirect to HTTPS)
|
||||||
|
assert response.status_code != 301
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user