Files
ForbiddenStarsApp/deploy/vps/README.md
T
NotBigGhostandClaude Opus 5 63fd90e017 Документация: сверка README и deploy/*.md с кодом
- README: test без порта на хосте (только через домен, Secure-cookie), прод из реестра
  (build-push + docker compose up -d), лимиты перебора и регистраций, dev_admin.py в списке
  dev-кода, структура репозитория, раздел о бэкапах, отличия test от prod; отмечены
  известные проблемы (#69, #71, #72, #73).
- deploy/README.md: три сервиса (app + tunnel + backup), источники ключа туннеля для Pi,
  test и dev-туннеля, слот 9000 у временного прода.
- deploy/pi/README.md: контейнер backup, fail-fast по секретам, ADMIN_PASSWORD только при
  первом создании админа (#73), порядок обновления.
- deploy/vps/README.md: туннель-контейнер вместо autossh, сниппет (edge), единые имена
  файлов в примере сборки сертификатов, дописывать authorized_keys через >>.
- deploy/backup/README.md: первый бэкап на новом Pi, выбор снимка с данными при
  восстановлении (#74), метка keep, --no-pre-restore, служебные команды, причины unhealthy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-14 20:06:54 +03:00

7.9 KiB
Raw Blame History

VPS (186.246.51.17) — реверс-прокси Caddy + точка входа SSH-туннелей

Единственная публичная точка. На VPS: Caddy терминирует HTTPS твоими сертификатами для двух доменов и проксирует трафик в SSH reverse-туннели от Pi (прод) и ПК (dev/test).

forbiddenstars.ru   → 127.0.0.1:9000  ← Pi  (туннель-контейнер, постоянно)      PROD
forbidden-stars.ru  → 127.0.0.1:9001  ← ПК  (контейнер test или ssh dev, по требованию)  DEV/TEST

Туннель ssh -R по умолчанию слушает на loopback VPS (127.0.0.1) — ровно туда смотрит Caddy. GatewayPorts включать НЕ нужно. Снаружи порты 9000/9001 недоступны.

0. DNS

A-записи обоих доменов → 186.246.51.17:

forbiddenstars.ru    A   186.246.51.17
forbidden-stars.ru   A   186.246.51.17

Проверка: dig +short forbiddenstars.ru (и второго) должны вернуть этот IP.

1. Зайти на VPS

ssh root@186.246.51.17     # пароль введёшь сам

2. Обновление + файрвол

apt update && apt -y upgrade
apt -y install ufw curl gnupg
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

3. Установить Caddy (официальный репозиторий)

apt -y install debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | tee /etc/apt/sources.list.d/caddy-stable.list
apt update && apt -y install caddy

4. Пользователь под туннели

useradd -m -s /bin/bash tunnel
install -d -m 700 -o tunnel -g tunnel /home/tunnel/.ssh
install -m 600 -o tunnel -g tunnel /dev/null /home/tunnel/.ssh/authorized_keys

Публичные ключи Pi и ПК добавишь в /home/tunnel/.ssh/authorized_keys на шагах настройки Pi (deploy/pi/README.md) и ПК (deploy/tunnel/id_tunnel.pub и/или ключ по умолчанию из ~/.ssh для dev-туннеля). Ключи дописывай (>>), а не перезаписывай файл (>): иначе туннель другого хоста перестанет пускать.

5. Сертификаты

Caddy читает PEM (текст с -----BEGIN CERTIFICATE-----). Расширение неважно — .crt от GlobalSign обычно УЖЕ PEM. Проверь первой строкой: head -1 файл.crt.

  • если -----BEGIN CERTIFICATE----- → это PEM, конвертировать НЕ нужно;
  • если бинарь/абракадабра → это DER, сконвертируй: openssl x509 -inform DER -in файл.crt -out файл.pem.

На каждый домен нужны два файла:

  • fullchain.pem = твой сертификат (leaf) + промежуточный(е) GlobalSign, в одном файле, именно в таком порядке (сначала leaf, потом промежуточный).
  • privkey.pem = приватный ключ (-----BEGIN PRIVATE KEY-----, без пароля; обычно это .key).

Удобно собрать прямо на VPS — залей свои файлы и склей:

mkdir -p /etc/caddy/certs/forbidden-stars.ru /etc/caddy/certs/forbiddenstars.ru /root/certs-tmp
# с локальной машины (пример для домена forbidden-stars.ru; имена файлов — свои):
scp forbidden-stars.crt intermediate.crt forbidden-stars.key root@186.246.51.17:/root/certs-tmp/
# на VPS — собрать fullchain (leaf + промежуточный) и положить ключ:
cat /root/certs-tmp/forbidden-stars.crt /root/certs-tmp/intermediate.crt \
  > /etc/caddy/certs/forbidden-stars.ru/fullchain.pem
cp /root/certs-tmp/forbidden-stars.key /etc/caddy/certs/forbidden-stars.ru/privkey.pem
# то же для forbiddenstars.ru (свои crt/intermediate/key), затем права
# (папкам нужен x-бит → 750, файлам 640; иначе пользователь caddy не прочитает):
chown -R caddy:caddy /etc/caddy/certs
find /etc/caddy/certs -type d -exec chmod 750 {} \;
find /etc/caddy/certs -type f -exec chmod 640 {} \;
rm -rf /root/certs-tmp

Если GlobalSign дал не отдельный промежуточный, а готовый bundle/chain — используй его как «промежуточную» часть. Если дал один файл, где leaf и chain уже вместе, — это и есть fullchain.pem (склейка не нужна).

6. Caddyfile + страница-заглушка

Скопируй deploy/vps/Caddyfile из репозитория в /etc/caddy/Caddyfile и положи страницу «сайт недоступен» (показывается, когда туннель к хосту лежит — вместо зависшей «Загрузки» SPA):

mkdir -p /etc/caddy/maintenance
# с локальной машины:
scp deploy/vps/maintenance.html root@186.246.51.17:/etc/caddy/maintenance/maintenance.html
# на VPS:
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy

Заглушка живёт в сниппете (edge) Caddyfile (там же security-заголовки и no-store для HTML): при ответе апстрима 502/503/504 Caddy отдаёт /etc/caddy/maintenance/maintenance.html со статусом 503 и Cache-Control: no-store. Для /api/events (SSE) у каждого домена отдельный handle без encode и с flush_interval -1. Правки текста/вида — в deploy/vps/maintenance.html, затем повтори scp (reload Caddy не нужен, файл читается на каждый запрос).

7. Проверка

  1. Подними туннель прода на Pi (см. deploy/pi/README.md) и/или dev/test на ПК (лаунчер run.ps1: dev — при LOCAL_PUBLIC=vps, test — при APP_ENV=test).
  2. Открой https://forbiddenstars.ru и https://forbidden-stars.ru.
  3. Пока соответствующий туннель не поднят — Caddy отдаёт страницу-заглушку «Технические шоколадки» (HTTP 503), это ожидаемо.

Логи Caddy: journalctl -u caddy -f.

8. Приём бэкапов с Pi (оффсайт-копии)

Контейнер backup на Pi каждую ночь отправляет зашифрованный снимок (restic) на VPS по SFTP. Для этого на VPS нужен отдельный пользователь fsbackup только для SFTP: без shell, без туннелей, без пароля. Репозиторий лежит в /srv/fs-backups/restic, вне веб-корней Caddy, так что бэкапы не публичны.

Имя не backup: в Debian/Ubuntu системный пользователь backup уже существует.

Пошаговая настройка: создание пользователя, ключ, sshd_config.d/60-fs-backup.conf, проверки — deploy/backup/README.md, шаг 3.

Проверка приёма после первого бэкапа: ls -la /srv/fs-backups/restic (там config, data, index, keys, snapshots). Старые снимки удаляет сам контейнер с Pi по политике хранения.