feat: harden production security boundaries
This commit is contained in:
@@ -14,6 +14,14 @@
|
||||
reverse_proxy web:4321
|
||||
}
|
||||
|
||||
@adminApi path /api/v1/admin/*
|
||||
handle @adminApi {
|
||||
basic_auth {
|
||||
{$ADMIN_BASIC_USER} {$ADMIN_BASIC_PASSWORD_HASH}
|
||||
}
|
||||
reverse_proxy api:8000
|
||||
}
|
||||
|
||||
@api path /api/* /health /ready
|
||||
handle @api {
|
||||
reverse_proxy api:8000
|
||||
@@ -29,6 +37,9 @@
|
||||
X-Content-Type-Options "nosniff"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
Permissions-Policy "camera=(), microphone=(), geolocation=()"
|
||||
X-Frame-Options "DENY"
|
||||
Cross-Origin-Opener-Policy "same-origin"
|
||||
Content-Security-Policy "frame-ancestors 'none'; base-uri 'self'; object-src 'none'"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,5 +51,7 @@
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
X-Content-Type-Options "nosniff"
|
||||
Referrer-Policy "no-referrer"
|
||||
X-Frame-Options "DENY"
|
||||
Content-Security-Policy "default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'; sandbox"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,11 +17,11 @@ Production-контур рассчитан на один Linux-сервер с D
|
||||
```bash
|
||||
cp .env.production.example .env.production
|
||||
chmod 600 .env.production
|
||||
openssl rand -base64 36 # повторить для пароля БД, ADMIN_TOKEN, RATE_LIMIT_SECRET и S3_SECRET_KEY
|
||||
openssl rand -base64 36 # отдельные значения для БД, ADMIN_TOKEN, RATE_LIMIT_SECRET, MinIO root и S3 app
|
||||
docker run --rm caddy:2.10.2-alpine caddy hash-password --plaintext 'ОТДЕЛЬНЫЙ ADMIN-ПАРОЛЬ'
|
||||
```
|
||||
|
||||
Заполните `.env.production`. Если пароль PostgreSQL содержит специальные символы, в `DATABASE_URL` нужна URL-кодированная форма того же пароля. `.env.production` нельзя коммитить или пересылать вместе с логами.
|
||||
Заполните `.env.production`. Хеш Caddy содержит символы `$`, поэтому значение `ADMIN_BASIC_PASSWORD_HASH` в env-файле заключите в одинарные кавычки. `MINIO_ROOT_*` и `S3_*` обязаны быть разными: init-контейнер проверяет это, API получает только application credentials. Если пароль PostgreSQL содержит специальные символы, в `DATABASE_URL` нужна URL-кодированная форма того же пароля. `.env.production` нельзя коммитить или пересылать вместе с логами.
|
||||
|
||||
## 3. Проверка и первый запуск
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ cleanup() {
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
$compose up -d --wait db minio
|
||||
$compose run --rm minio-init
|
||||
$compose exec -T db sh -c 'psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "create table restore_drill (value text primary key); insert into restore_drill values ('"'"'database-ok'"'"');"' >/dev/null
|
||||
$compose run --rm --entrypoint /bin/sh storage-tool -c 'mc alias set local http://minio:9000 "$S3_ACCESS_KEY" "$S3_SECRET_KEY" >/dev/null; mc mb --ignore-existing "local/$S3_BUCKET" >/dev/null; printf object-ok | mc pipe "local/$S3_BUCKET/restore-drill.txt"' >/dev/null
|
||||
|
||||
|
||||
@@ -24,9 +24,12 @@ trap cleanup EXIT INT TERM
|
||||
$compose up --build -d --wait db minio api web
|
||||
curl -fsS "http://127.0.0.1:$BOOTSTRAP_API_PORT/ready" >/dev/null
|
||||
curl -fsS "http://127.0.0.1:$BOOTSTRAP_WEB_PORT/" >/dev/null
|
||||
curl -fsS -D - -o /dev/null "http://127.0.0.1:$BOOTSTRAP_API_PORT/health" | grep -qi '^x-frame-options: DENY'
|
||||
curl -fsS -D - -o /dev/null "http://127.0.0.1:$BOOTSTRAP_API_PORT/health" | grep -qi '^cross-origin-opener-policy: same-origin'
|
||||
test "$($compose exec -T db psql -At -U rf4 -d rf4_spotter -c 'select version_num from alembic_version')" = "0010"
|
||||
test "$($compose exec -T db psql -At -U rf4 -d rf4_spotter -c 'select count(*) from fish')" = "2"
|
||||
test "$($compose exec -T db psql -At -U rf4 -d rf4_spotter -c 'select count(*) from waterbody')" = "2"
|
||||
test "$($compose exec -T db psql -At -U rf4 -d rf4_spotter -c 'select count(*) from catch_report')" = "0"
|
||||
WEB_URL="http://127.0.0.1:$BOOTSTRAP_WEB_PORT" BOOTSTRAP_API_URL="http://127.0.0.1:$BOOTSTRAP_API_PORT" BOOTSTRAP_ADMIN_TOKEN=replace-with-at-least-32-random-characters npm --prefix apps/web run test:bootstrap
|
||||
curl -fsS -D - -o /dev/null -H 'Authorization: Bearer replace-with-at-least-32-random-characters' "http://127.0.0.1:$BOOTSTRAP_API_PORT/api/v1/admin/catch-reports" | grep -qi '^cache-control: no-store'
|
||||
echo "Production bootstrap passed from empty volumes"
|
||||
|
||||
Reference in New Issue
Block a user