Enable HTTPS production deployment on Sinbad2 via Apache reverse proxy.

This commit is contained in:
Mireya Cueto Garrido
2026-06-03 10:41:02 +02:00
parent 31be326f2c
commit cccbe15275
22 changed files with 264 additions and 28 deletions
View File
+41
View File
@@ -0,0 +1,41 @@
import os
from functools import lru_cache
def _split_csv(value: str | None) -> list[str]:
if not value:
return []
return [item.strip() for item in value.split(",") if item.strip()]
class Settings:
ENVIRONMENT: str = os.getenv("ENVIRONMENT", "development")
SECURITY_HSTS_SECONDS: int = int(os.getenv("SECURITY_HSTS_SECONDS", "31536000"))
@property
def is_production(self) -> bool:
return self.ENVIRONMENT.strip().lower() == "production"
@property
def cors_allowed_origins(self) -> list[str]:
configured = _split_csv(os.getenv("CORS_ALLOWED_ORIGINS"))
if configured:
return configured
if self.is_production:
frontend = os.getenv("FRONTEND_URL", "").rstrip("/")
return [frontend] if frontend else []
return ["*"]
@property
def trusted_hosts(self) -> list[str]:
configured = _split_csv(os.getenv("TRUSTED_HOSTS"))
if configured:
return configured
if self.is_production:
return ["sinbad2.ujaen.es"]
return ["*"]
@lru_cache
def get_settings() -> Settings:
return Settings()
+29 -8
View File
@@ -1,7 +1,12 @@
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from starlette.middleware.trustedhost import TrustedHostMiddleware
from api.config.settings import get_settings
from api.database.mongodb import db
from api.middleware.security_headers import SecurityHeadersMiddleware
# Routers
from api.routers.test_mongo import router as test_mongo_router
@@ -20,15 +25,31 @@ from api.routers.google_auth import router as google_auth_router
async def lifespan(app: FastAPI):
yield
settings = get_settings()
app = FastAPI(lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(SecurityHeadersMiddleware, settings=settings)
if settings.is_production:
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=settings.trusted_hosts,
)
cors_origins = settings.cors_allowed_origins
cors_kwargs = {
"allow_methods": ["*"],
"allow_headers": ["*"],
}
if cors_origins == ["*"]:
cors_kwargs["allow_origins"] = ["*"]
cors_kwargs["allow_credentials"] = False
else:
cors_kwargs["allow_origins"] = cors_origins
cors_kwargs["allow_credentials"] = True
app.add_middleware(CORSMiddleware, **cors_kwargs)
app.include_router(test_mongo_router, prefix="/api")
app.include_router(value_router, prefix="/api/criteria/doc")
View File
@@ -0,0 +1,24 @@
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from api.config.settings import Settings, get_settings
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
def __init__(self, app, settings: Settings | None = None):
super().__init__(app)
self._settings = settings or get_settings()
async def dispatch(self, request: Request, call_next) -> Response:
response = await call_next(request)
response.headers.setdefault("X-Content-Type-Options", "nosniff")
response.headers.setdefault("X-Frame-Options", "DENY")
response.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin")
if request.url.scheme == "https" or self._settings.is_production:
hsts = f"max-age={self._settings.SECURITY_HSTS_SECONDS}"
response.headers.setdefault("Strict-Transport-Security", hsts)
return response