HTTPS через Caddy с автоматическими сертификатами
Токен и расшифровки разговоров ходили по сети открытым текстом. Теперь в пакете лежит Caddy: он держит сертификаты Let's Encrypt и продлевает их сам, а сервис уходит на localhost. Отдельно решён вопрос подмены адреса: за прокси все запросы приходят с localhost, и список разрешённых адресов пускал бы кого угодно. Заголовку с настоящим адресом сервис верит только от доверенного прокси - на это восемь тестов. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
64ce562bb1
commit
c992b7907d
+11
-4
@@ -19,7 +19,7 @@ from app.config import Settings, load_settings
|
||||
from app.logbuffer import get as log_buffer
|
||||
from app.logbuffer import install as install_log_buffer
|
||||
from app.pipeline import ModelsMissing, Pipeline, to_wav16k
|
||||
from app.security import check_token, ip_allowed, parse_allowlist
|
||||
from app.security import check_token, client_address, ip_allowed, parse_allowlist
|
||||
from app.store import JobStatus, JobStore
|
||||
from app.webhook import deliver_async
|
||||
from app.version import __version__
|
||||
@@ -28,6 +28,7 @@ log = logging.getLogger("talkscore-asr")
|
||||
|
||||
settings: Settings = load_settings()
|
||||
allowlist = parse_allowlist(settings.allow_ips)
|
||||
trusted_proxies = parse_allowlist(settings.trust_proxy)
|
||||
|
||||
# Хранилище создаётся не здесь, а при старте сервиса. На Windows процессы
|
||||
# поднимаются через spawn и заново импортируют этот модуль: если бы очередь
|
||||
@@ -190,16 +191,22 @@ app = FastAPI(
|
||||
bearer_scheme = HTTPBearer(description="Токен из config.toml, раздел [security]")
|
||||
|
||||
|
||||
def peer_address(request: Request) -> str | None:
|
||||
"""Адрес клиента с поправкой на обратный прокси."""
|
||||
peer = request.client.host if request.client else None
|
||||
return client_address(peer, request.headers.get("x-forwarded-for"), trusted_proxies)
|
||||
|
||||
|
||||
def ip_guard(request: Request) -> None:
|
||||
"""Только проверка адреса: нужна для страницы с описанием методов."""
|
||||
client_ip = request.client.host if request.client else None
|
||||
client_ip = peer_address(request)
|
||||
if not ip_allowed(client_ip, allowlist):
|
||||
raise HTTPException(status_code=403, detail="адрес не в списке разрешённых")
|
||||
|
||||
|
||||
def guard(request: Request) -> None:
|
||||
"""Проверяет адрес и токен. Порядок важен: сначала сеть, потом секрет."""
|
||||
client_ip = request.client.host if request.client else None
|
||||
client_ip = peer_address(request)
|
||||
if not ip_allowed(client_ip, allowlist):
|
||||
log.warning("отказано по адресу: %s", client_ip)
|
||||
raise HTTPException(status_code=403, detail="адрес не в списке разрешённых")
|
||||
@@ -253,7 +260,7 @@ def health(request: Request) -> JSONResponse:
|
||||
отладка отказов превращается в гадание, а сам адрес клиенту и так известен.
|
||||
Список разрешённых адресов при этом не раскрывается.
|
||||
"""
|
||||
client_ip = request.client.host if request.client else None
|
||||
client_ip = peer_address(request)
|
||||
return JSONResponse({
|
||||
"status": "ok" if _state["ready"] else "no_models",
|
||||
"version": __version__,
|
||||
|
||||
Reference in New Issue
Block a user