fix: separate admin page and API authentication
This commit is contained in:
@@ -18,7 +18,7 @@ Web Docker-образ устанавливает зависимости чере
|
||||
|
||||
Идёт исправление аудита: актуальные изменения и ограничения перечислены в [AUDIT_FIXES.md](docs/AUDIT_FIXES.md). Production Compose включает community scheduler; страницы rules/privacy реализованы. Для запуска остаются сервер, DNS/TLS, секреты, внешний backup и контакты. Шкала 72 часов использует полную выборку по времени поступления; одинаковые поля разных источников больше не считаются доказательством одного события. Фоновая публикация обновляет кэш API в пределах TTL, не мгновенно.
|
||||
|
||||
Полный аудит 8 сентября: [отчёт](docs/PROJECT_AUDIT_2026-09-08.md), [приоритетный план](docs/ROADMAP.md#ближайший-рабочий-пакет). T01 выполнен: Caddy-маршруты формы исправлены и проверены через временный proxy командой `sh deploy/test-proxy-routing.sh` на запущенном локальном Compose. Перед production остаются конфликт Basic/Bearer admin-аутентификации и исходящая сеть community scheduler. Затем — клиентский rate limit, надёжность парсеров, согласованность фильтров/оценок и HTTP/SEO-контракт ошибок.
|
||||
Полный аудит 8 сентября: [отчёт](docs/PROJECT_AUDIT_2026-09-08.md), [приоритетный план](docs/ROADMAP.md#ближайший-рабочий-пакет). T01/T02 выполнены: исправлены маршруты формы и конфликт admin-аутентификации. Проверка `sh deploy/test-proxy-routing.sh` на запущенном локальном Compose проверяет Astro redirects, API, Basic-доступ к страницам и Bearer-доступ к API, включая отказы. Следующий блокер — исходящая сеть community scheduler (T03). Затем — клиентский rate limit, надёжность парсеров, согласованность фильтров/оценок и HTTP/SEO-контракт ошибок.
|
||||
|
||||
На ширинах 320, 390, 768 и 1280 px ранее проверено отсутствие горизонтального переполнения основных страниц. Это не полная визуальная приёмка: аудит обнаружил неверную desktop-компоновку фильтров; наполненные карточки, длинные названия, клавиатура и zoom остаются отдельной задачей.
|
||||
|
||||
@@ -59,7 +59,7 @@ FastAPI ─ PostgreSQL 17
|
||||
└ MinIO/S3
|
||||
```
|
||||
|
||||
Наружу production-профиль публикует только Caddy. PostgreSQL, API, Astro и MinIO находятся во внутренних Docker-сетях. Caddy завершает TLS и защищает административные маршруты Basic Auth; административный API дополнительно требует bearer token.
|
||||
Наружу production-профиль публикует только Caddy. PostgreSQL, API, Astro и MinIO находятся в Docker-сетях. Caddy завершает TLS и защищает административные страницы Basic Auth; административный API отдельно проверяет Bearer-токен в FastAPI. Basic не накладывается на API-запросы.
|
||||
|
||||
Gitea Actions workflow `.gitea/workflows/ci.yml` на каждый push и pull request проверяет Python, миграции на чистой PostgreSQL, Astro build и полный Compose/Playwright-сценарий. При падении E2E сохраняются логи контейнеров и Playwright-артефакты.
|
||||
|
||||
|
||||
+2
-3
@@ -25,9 +25,8 @@
|
||||
|
||||
@adminApi path /api/v1/admin/*
|
||||
handle @adminApi {
|
||||
basic_auth {
|
||||
{$ADMIN_BASIC_USER} {$ADMIN_BASIC_PASSWORD_HASH}
|
||||
}
|
||||
# FastAPI verifies the Bearer token. Basic Auth here would reject the
|
||||
# browser's Bearer Authorization header before it reached the API.
|
||||
reverse_proxy api:8000
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Развёртывание открытой альфы rf4spotter.ru
|
||||
|
||||
Production-контур рассчитан на один Linux-сервер с Docker Compose. Наружу публикуются только Caddy `80/443`; PostgreSQL, FastAPI и MinIO не имеют host-портов. Административные страницы защищены одновременно Caddy Basic Auth и API bearer token.
|
||||
Production-контур рассчитан на один Linux-сервер с Docker Compose. Наружу публикуются только Caddy `80/443`; PostgreSQL, FastAPI и MinIO не имеют host-портов. Административные страницы защищены Caddy Basic Auth, административный API — Bearer-токеном FastAPI. API-запросы не требуют Basic: обе схемы используют заголовок Authorization и не могут накладываться на один запрос.
|
||||
|
||||
Текущее состояние публичных DNS/TLS и незакрытые инфраструктурные действия ведутся в [`docs/deployment-status.md`](../docs/deployment-status.md).
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""Read-only checks through a temporary proxy, run inside the local API container."""
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
|
||||
base = os.environ["RF4_PROXY_TEST_URL"]
|
||||
token = os.environ["ADMIN_TOKEN"]
|
||||
basic = "Basic " + base64.b64encode(b"route-test:hiccup").decode()
|
||||
|
||||
|
||||
def probe(path, expected, authorization=None, method="GET", data=None):
|
||||
headers = {"Host": "localhost:8080"}
|
||||
if authorization:
|
||||
headers["Authorization"] = authorization
|
||||
if data is not None:
|
||||
headers["Content-Type"] = "application/json"
|
||||
request = Request(base + path, headers=headers, method=method, data=data)
|
||||
try:
|
||||
response = urlopen(request, timeout=15)
|
||||
except HTTPError as error:
|
||||
response = error
|
||||
with response:
|
||||
assert response.status == expected, f"{method} {path}: expected {expected}, got {response.status}"
|
||||
if path.startswith("/api/v1/admin/") and expected == 200:
|
||||
assert "no-store" in response.headers.get("Cache-Control", "")
|
||||
assert isinstance(json.load(response), dict)
|
||||
|
||||
|
||||
probe("/admin/moderation", 401)
|
||||
probe("/admin/moderation", 200, basic)
|
||||
probe("/admin/moderation", 401, "Bearer " + token)
|
||||
for path in ("/api/v1/admin/diagnostics", "/api/v1/admin/catch-reports"):
|
||||
probe(path, 401)
|
||||
probe(path, 401, basic)
|
||||
probe(path, 401, "Bearer invalid-routing-test-token")
|
||||
probe("/api/v1/admin/diagnostics", 200, "Bearer " + token)
|
||||
# Invalid UUID prevents writes while checking the moderation request path.
|
||||
probe("/api/v1/admin/catch-reports/not-a-uuid", 401, method="PATCH", data=b'{"status":"approved"}')
|
||||
probe("/api/v1/admin/catch-reports/not-a-uuid", 422, "Bearer " + token,
|
||||
method="PATCH", data=b'{"status":"approved"}')
|
||||
print("Admin auth passed: Basic pages, Bearer API, rejected missing/wrong credentials")
|
||||
@@ -44,4 +44,6 @@ check_redirect /api/report '/report?state=create_error'
|
||||
check_redirect /api/report-screenshot '/report?state=screenshot_error&report_id='
|
||||
curl -fsS --max-time 15 -H 'Host: localhost:8080' "$base/api/v1/fishes?limit=1" >/dev/null
|
||||
curl -fsS --max-time 15 -H 'Host: localhost:8080' "$base/ready" >/dev/null
|
||||
docker compose exec -T -e "RF4_PROXY_TEST_URL=http://$container:8080" api \
|
||||
python - < "$repo/deploy/probe-admin-auth.py"
|
||||
echo "Proxy routing passed: Astro form redirects, FastAPI catalog and health/readiness"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
## Исправлено в рабочей версии
|
||||
|
||||
- [x] T02: разделены Basic для административных страниц и Bearer для admin API. 9 сентября проверены положительные/отрицательные сценарии через Caddy; API сохраняет обязательную проверку токена и no-store.
|
||||
|
||||
- [x] T01 полного аудита: маршруты `/api/report` и `/api/report-screenshot` достигают Astro через Caddy; FastAPI получает `/api/v1/*`, health/readiness. 9 сентября: `sh deploy/test-proxy-routing.sh` проходит на локальном стенде с корректным Origin; создание реальной заявки через production proxy остаётся частью T06.
|
||||
|
||||
- [x] Очередь внешних наблюдений фильтруется в SQL до limit/offset; UI показывает по 50 записей с навигацией, опубликованные записи не вытесняют ожидающие проверки.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Аудит RF4 Spotter — 8 сентября 2026
|
||||
|
||||
Обновление 9 сентября: T01 исправлен (`bb61d21`) и проверен скриптом `deploy/test-proxy-routing.sh`. Ниже сохранены исходные находки аудита; актуальные отметки выполнения — в ROADMAP. T02/T03 остаются блокерами production.
|
||||
Обновление 9 сентября: T01 исправлен (`bb61d21`), T02 закрыт разделением Basic для страниц и Bearer для API; оба проверены скриптом `deploy/test-proxy-routing.sh`. Ниже сохранены исходные находки аудита; актуальные отметки выполнения — в ROADMAP. T03 остаётся блокером production.
|
||||
|
||||
База проверки: commit `486b4e9`. Аудит охватывает production-конфигурацию, API, импорт и публикацию данных, Astro, пользовательские сценарии, визуальную идентичность и SEO. Это отчёт о текущем состоянии, а не подтверждение готовности к запуску. Исправления приложения в этот пакет не входят.
|
||||
|
||||
|
||||
+2
-2
@@ -180,12 +180,12 @@
|
||||
|
||||
## Ближайший рабочий пакет
|
||||
|
||||
Production-контур требует исправлений до запуска. Доказательства и критерии приёмки каждого ID находятся в [полном аудите](PROJECT_AUDIT_2026-09-08.md). Следующая задача — **T02**, затем T03. Инфраструктурные изменения можно объединить в один проверочный Compose-прогон.
|
||||
Production-контур требует исправлений до запуска. Доказательства и критерии приёмки каждого ID находятся в [полном аудите](PROJECT_AUDIT_2026-09-08.md). Следующая задача — **T03**. Инфраструктурные изменения можно объединить в один проверочный Compose-прогон.
|
||||
|
||||
### P0 — блокеры production
|
||||
|
||||
- [x] T01: Caddy направляет в FastAPI только `/api/v1/*`, `/health`, `/ready`, обработчики формы остаются в Astro (правка `bb61d21`). Проверено 9 сентября: `sh deploy/test-proxy-routing.sh`, оба POST дают ожидаемый 303, каталог и readiness доступны. Тест использует временный proxy и не создаёт уловов; успешная отправка с сохранением входит в T06.
|
||||
- [ ] T02: согласовать Basic/Bearer admin-аутентификацию через production proxy.
|
||||
- [x] T02: Basic применяется к admin-страницам, Bearer — к admin API, конфликт Authorization устранён. 9 сентября: `sh deploy/test-proxy-routing.sh` проверяет доступ к странице/диагностике, 401 без правильных credentials и авторизованный PATCH с невалидным UUID (422, без записи). Полный цикл модерации — T06.
|
||||
- [ ] T03: обеспечить исходящую сеть community scheduler при изоляции БД/MinIO.
|
||||
|
||||
### P1 — до открытой альфы
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| Область | Реализация |
|
||||
|---|---|
|
||||
| Внешняя поверхность | наружу публикуются только Caddy `80/443`; PostgreSQL, API, web и MinIO находятся во внутренних сетях |
|
||||
| Admin web/API | `/admin/*` и `/api/v1/admin/*` требуют Caddy Basic Auth; API дополнительно требует длинный bearer token |
|
||||
| Admin web/API | `/admin/*` требует Caddy Basic Auth; `/api/v1/admin/*` требует длинный Bearer-токен в FastAPI. Схемы разделены по маршрутам, поскольку используют один заголовок Authorization (T02, 9 сентября) |
|
||||
| Сравнение токена | constant-time `hmac.compare_digest` |
|
||||
| CORS | production принимает только явно заданные HTTPS origins, wildcard запрещён конфигурацией |
|
||||
| Чувствительные ответы | административные ответы и создание заявки получают `Cache-Control: no-store` |
|
||||
@@ -31,8 +31,10 @@
|
||||
|
||||
1. Убедиться, что `ss -lntup` не показывает наружу порты `4321`, `8000`, `9000`, `9001`, `5432`.
|
||||
2. Проверить `stat -c '%a %U:%G' .env.production`: ожидается `600 rf4spotter:rf4spotter`.
|
||||
3. Без Basic Auth запрос к `/admin/moderation` и `/api/v1/admin/catch-reports` должен вернуть `401` от Caddy.
|
||||
4. С Basic Auth, но без bearer token административный API должен вернуть `401` от FastAPI.
|
||||
3. Без Basic Auth запрос к `/admin/moderation` должен вернуть `401` от Caddy; с правильным Basic — `200`. Bearer не открывает административную страницу.
|
||||
4. Admin API без Bearer, с неверным Bearer или только с Basic должен вернуть `401` от FastAPI; правильный Bearer даёт доступ и `Cache-Control: no-store`. Владение API-токеном достаточно для API-доступа; дополнительная Basic-проверка к этим запросам не применяется.
|
||||
5. Проверить security headers через `curl -I https://rf4spotter.ru/`.
|
||||
6. Убедиться, что `https://files.rf4spotter.ru/` не открывает Console, list buckets или анонимную запись.
|
||||
7. После проверки удалить команды с секретами из shell history либо вводить их через защищённый password manager.
|
||||
|
||||
Локальная проверка: `sh deploy/test-proxy-routing.sh` запускает временный Caddy с production-маршрутами перед работающим Compose. `probe-admin-auth.py` читает токен из окружения API-контейнера и не печатает его. Проверяются успешное чтение, отказы и PATCH с невалидным UUID без изменения данных; полный цикл реальной модерации через proxy относится к T06.
|
||||
|
||||
Reference in New Issue
Block a user