Revert "Allow private-network CORS origins in development"
This reverts commit 1b2e0cb8af.
This commit is contained in:
@@ -36,7 +36,6 @@ PROVIDER_BASE_URL_ALLOWLIST=[]
|
|||||||
PUBLIC_BASE_URL=http://localhost:8000
|
PUBLIC_BASE_URL=http://localhost:8000
|
||||||
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000"]
|
CORS_ORIGINS=["http://localhost:5173","http://localhost:3000"]
|
||||||
CORS_ALLOW_CREDENTIALS=false
|
CORS_ALLOW_CREDENTIALS=false
|
||||||
CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS=true
|
|
||||||
VITE_API_BASE=
|
VITE_API_BASE=
|
||||||
|
|
||||||
# Production baseline overrides (set explicitly for live deployments):
|
# Production baseline overrides (set explicitly for live deployments):
|
||||||
@@ -51,5 +50,4 @@ VITE_API_BASE=
|
|||||||
# PUBLIC_BASE_URL=https://api.example.com
|
# PUBLIC_BASE_URL=https://api.example.com
|
||||||
# CORS_ORIGINS=["https://app.example.com"]
|
# CORS_ORIGINS=["https://app.example.com"]
|
||||||
# CORS_ALLOW_CREDENTIALS=false
|
# CORS_ALLOW_CREDENTIALS=false
|
||||||
# CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS=false
|
|
||||||
# VITE_API_BASE=https://api.example.com/api/v1
|
# VITE_API_BASE=https://api.example.com/api/v1
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ cd frontend && npm run preview
|
|||||||
|
|
||||||
Main runtime variables are defined in `docker-compose.yml`:
|
Main runtime variables are defined in `docker-compose.yml`:
|
||||||
|
|
||||||
- API and worker: `DATABASE_URL`, `REDIS_URL`, `REDIS_SECURITY_MODE`, `REDIS_TLS_MODE`, `STORAGE_ROOT`, `PUBLIC_BASE_URL`, `CORS_ORIGINS`, `CORS_ALLOW_CREDENTIALS`, `CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS`, `AUTH_BOOTSTRAP_*`, `PROCESSING_LOG_STORE_*`, `CONTENT_EXPORT_*`, `TYPESENSE_*`, `APP_SETTINGS_ENCRYPTION_KEY`
|
- API and worker: `DATABASE_URL`, `REDIS_URL`, `REDIS_SECURITY_MODE`, `REDIS_TLS_MODE`, `STORAGE_ROOT`, `PUBLIC_BASE_URL`, `CORS_ORIGINS`, `CORS_ALLOW_CREDENTIALS`, `AUTH_BOOTSTRAP_*`, `PROCESSING_LOG_STORE_*`, `CONTENT_EXPORT_*`, `TYPESENSE_*`, `APP_SETTINGS_ENCRYPTION_KEY`
|
||||||
- Frontend: optional `VITE_API_BASE`
|
- Frontend: optional `VITE_API_BASE`
|
||||||
|
|
||||||
When `VITE_API_BASE` is unset, the frontend uses `http://<current-hostname>:8000/api/v1`.
|
When `VITE_API_BASE` is unset, the frontend uses `http://<current-hostname>:8000/api/v1`.
|
||||||
|
|||||||
@@ -38,4 +38,3 @@ TYPESENSE_API_KEY=replace-with-random-typesense-api-key
|
|||||||
TYPESENSE_COLLECTION_NAME=documents
|
TYPESENSE_COLLECTION_NAME=documents
|
||||||
PUBLIC_BASE_URL=http://localhost:8000
|
PUBLIC_BASE_URL=http://localhost:8000
|
||||||
CORS_ALLOW_CREDENTIALS=false
|
CORS_ALLOW_CREDENTIALS=false
|
||||||
CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS=true
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ class Settings(BaseSettings):
|
|||||||
public_base_url: str = "http://localhost:8000"
|
public_base_url: str = "http://localhost:8000"
|
||||||
cors_origins: list[str] = Field(default_factory=lambda: ["http://localhost:5173", "http://localhost:3000"])
|
cors_origins: list[str] = Field(default_factory=lambda: ["http://localhost:5173", "http://localhost:3000"])
|
||||||
cors_allow_credentials: bool = False
|
cors_allow_credentials: bool = False
|
||||||
cors_allow_development_private_network_origins: bool = True
|
|
||||||
|
|
||||||
|
|
||||||
LOCAL_HOSTNAME_SUFFIXES = (".local", ".internal", ".home.arpa")
|
LOCAL_HOSTNAME_SUFFIXES = (".local", ".internal", ".home.arpa")
|
||||||
|
|||||||
@@ -19,15 +19,6 @@ from app.services.typesense_index import ensure_typesense_collection
|
|||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
UPLOAD_ENDPOINT_PATH = "/api/v1/documents/upload"
|
UPLOAD_ENDPOINT_PATH = "/api/v1/documents/upload"
|
||||||
UPLOAD_ENDPOINT_METHOD = "POST"
|
UPLOAD_ENDPOINT_METHOD = "POST"
|
||||||
CORS_DEVELOPMENT_PRIVATE_ORIGIN_REGEX = (
|
|
||||||
r"^https?://("
|
|
||||||
r"localhost"
|
|
||||||
r"|127\.0\.0\.1"
|
|
||||||
r"|10\.\d{1,3}\.\d{1,3}\.\d{1,3}"
|
|
||||||
r"|192\.168\.\d{1,3}\.\d{1,3}"
|
|
||||||
r"|172\.(1[6-9]|2\d|3[0-1])\.\d{1,3}\.\d{1,3}"
|
|
||||||
r")(?::\d{1,5})?$"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_upload_size_guard_target(request: Request) -> bool:
|
def _is_upload_size_guard_target(request: Request) -> bool:
|
||||||
@@ -40,28 +31,14 @@ def _is_upload_size_guard_target(request: Request) -> bool:
|
|||||||
return request.method.upper() == UPLOAD_ENDPOINT_METHOD and request.url.path == UPLOAD_ENDPOINT_PATH
|
return request.method.upper() == UPLOAD_ENDPOINT_METHOD and request.url.path == UPLOAD_ENDPOINT_PATH
|
||||||
|
|
||||||
|
|
||||||
def _resolve_cors_origin_regex() -> str | None:
|
|
||||||
"""Returns development-only private-network origin regex when explicitly enabled."""
|
|
||||||
|
|
||||||
app_env = settings.app_env.strip().lower()
|
|
||||||
if app_env not in {"development", "dev"}:
|
|
||||||
return None
|
|
||||||
allow_private_dev_origins = bool(getattr(settings, "cors_allow_development_private_network_origins", False))
|
|
||||||
if not allow_private_dev_origins:
|
|
||||||
return None
|
|
||||||
return CORS_DEVELOPMENT_PRIVATE_ORIGIN_REGEX
|
|
||||||
|
|
||||||
|
|
||||||
def create_app() -> FastAPI:
|
def create_app() -> FastAPI:
|
||||||
"""Builds and configures the FastAPI application instance."""
|
"""Builds and configures the FastAPI application instance."""
|
||||||
|
|
||||||
app = FastAPI(title="DCM DMS API", version="0.1.0")
|
app = FastAPI(title="DCM DMS API", version="0.1.0")
|
||||||
allowed_origins = [origin.strip() for origin in settings.cors_origins if isinstance(origin, str) and origin.strip()]
|
allowed_origins = [origin.strip() for origin in settings.cors_origins if isinstance(origin, str) and origin.strip()]
|
||||||
allowed_origin_regex = _resolve_cors_origin_regex()
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=allowed_origins,
|
allow_origins=allowed_origins,
|
||||||
allow_origin_regex=allowed_origin_regex,
|
|
||||||
allow_credentials=bool(getattr(settings, "cors_allow_credentials", False)),
|
allow_credentials=bool(getattr(settings, "cors_allow_credentials", False)),
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ Use `.env.example` as baseline. The table below documents user-managed settings
|
|||||||
| `VITE_API_BASE` | empty for host-derived `http://<frontend-host>:8000/api/v1`, or explicit local URL | `https://api.example.com/api/v1` |
|
| `VITE_API_BASE` | empty for host-derived `http://<frontend-host>:8000/api/v1`, or explicit local URL | `https://api.example.com/api/v1` |
|
||||||
| `CORS_ORIGINS` | `["http://localhost:5173","http://localhost:3000"]` | exact frontend origins only, for example `["https://app.example.com"]` |
|
| `CORS_ORIGINS` | `["http://localhost:5173","http://localhost:3000"]` | exact frontend origins only, for example `["https://app.example.com"]` |
|
||||||
| `CORS_ALLOW_CREDENTIALS` | `false` | `false` (Authorization header flow does not need credentialed CORS) |
|
| `CORS_ALLOW_CREDENTIALS` | `false` | `false` (Authorization header flow does not need credentialed CORS) |
|
||||||
| `CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS` | `true` to allow LAN origins such as `http://192.168.x.x:5173` during dev | `false` |
|
|
||||||
| `REDIS_URL` | `redis://:<password>@redis:6379/0` in isolated local network | `rediss://:<password>@redis.internal:6379/0` |
|
| `REDIS_URL` | `redis://:<password>@redis:6379/0` in isolated local network | `rediss://:<password>@redis.internal:6379/0` |
|
||||||
| `REDIS_SECURITY_MODE` | `compat` or `auto` | `strict` |
|
| `REDIS_SECURITY_MODE` | `compat` or `auto` | `strict` |
|
||||||
| `REDIS_TLS_MODE` | `allow_insecure` or `auto` | `required` |
|
| `REDIS_TLS_MODE` | `allow_insecure` or `auto` | `required` |
|
||||||
@@ -94,7 +93,6 @@ Recommended LIVE pattern:
|
|||||||
## Security Controls
|
## Security Controls
|
||||||
|
|
||||||
- CORS uses explicit origin allowlist only; broad origin regex matching is removed.
|
- CORS uses explicit origin allowlist only; broad origin regex matching is removed.
|
||||||
- Development mode can additionally allow private-network HTTP(S) origins when `CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS=true`.
|
|
||||||
- Worker Redis startup validates URL auth and TLS policy before consuming jobs.
|
- Worker Redis startup validates URL auth and TLS policy before consuming jobs.
|
||||||
- Provider API keys are encrypted at rest with standard AEAD (`cryptography` Fernet).
|
- Provider API keys are encrypted at rest with standard AEAD (`cryptography` Fernet).
|
||||||
- legacy `enc-v1` payloads are read for backward compatibility
|
- legacy `enc-v1` payloads are read for backward compatibility
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ services:
|
|||||||
PROVIDER_BASE_URL_ALLOW_HTTP: ${PROVIDER_BASE_URL_ALLOW_HTTP:-true}
|
PROVIDER_BASE_URL_ALLOW_HTTP: ${PROVIDER_BASE_URL_ALLOW_HTTP:-true}
|
||||||
PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK: ${PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK:-true}
|
PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK: ${PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK:-true}
|
||||||
CORS_ALLOW_CREDENTIALS: ${CORS_ALLOW_CREDENTIALS:-false}
|
CORS_ALLOW_CREDENTIALS: ${CORS_ALLOW_CREDENTIALS:-false}
|
||||||
CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS: ${CORS_ALLOW_DEVELOPMENT_PRIVATE_NETWORK_ORIGINS:-true}
|
|
||||||
PROCESSING_LOG_STORE_MODEL_IO_TEXT: ${PROCESSING_LOG_STORE_MODEL_IO_TEXT:-false}
|
PROCESSING_LOG_STORE_MODEL_IO_TEXT: ${PROCESSING_LOG_STORE_MODEL_IO_TEXT:-false}
|
||||||
PROCESSING_LOG_STORE_PAYLOAD_TEXT: ${PROCESSING_LOG_STORE_PAYLOAD_TEXT:-false}
|
PROCESSING_LOG_STORE_PAYLOAD_TEXT: ${PROCESSING_LOG_STORE_PAYLOAD_TEXT:-false}
|
||||||
CONTENT_EXPORT_MAX_DOCUMENTS: ${CONTENT_EXPORT_MAX_DOCUMENTS:-250}
|
CONTENT_EXPORT_MAX_DOCUMENTS: ${CONTENT_EXPORT_MAX_DOCUMENTS:-250}
|
||||||
|
|||||||
Reference in New Issue
Block a user