fix(security): harden Redis auth, restrict /metrics, document Gitea port fix
Some checks failed
CI / ruff (push) Successful in 10s
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / pytest (push) Has been cancelled

- Add Redis password via REDIS_PASSWORD env var (--requirepass flag)
- Update all REDIS_URL and REDIS_ADDR references to include password
- Restrict /metrics endpoint to localhost and Docker internal networks (403 for external requests)
- Document Gitea port 3000 localhost binding fix (must be applied manually on server)
- Add REDIS_PASSWORD to .env.example

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 23:15:15 +01:00
parent a7392de9f6
commit b68d542258
4 changed files with 60 additions and 8 deletions

View File

@@ -34,7 +34,8 @@ from datetime import UTC, datetime
from enum import Enum
from typing import Any
from fastapi import APIRouter, Response
from fastapi import APIRouter, Request, Response
from fastapi.responses import JSONResponse
logger = logging.getLogger(__name__)
@@ -538,12 +539,20 @@ async def readiness_check() -> dict[str, Any]:
@health_router.get("/metrics")
async def metrics_endpoint() -> Response:
async def metrics_endpoint(request: Request) -> Response:
"""
Prometheus metrics endpoint.
Returns metrics in Prometheus text format for scraping.
Restricted to localhost and Docker internal networks only.
"""
client_ip = request.client.host if request.client else None
allowed_prefixes = ("127.", "10.", "172.16.", "172.17.", "172.18.", "172.19.",
"172.20.", "172.21.", "172.22.", "172.23.", "172.24.",
"172.25.", "172.26.", "172.27.", "172.28.", "172.29.",
"172.30.", "172.31.", "192.168.", "::1")
if not client_ip or not client_ip.startswith(allowed_prefixes):
return JSONResponse(status_code=403, content={"detail": "Forbidden"})
content = metrics_registry.generate_latest()
return Response(
content=content,