- 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
130 lines
7.9 KiB
Markdown
130 lines
7.9 KiB
Markdown
# 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
|
||
```bash
|
||
ssh root@186.246.51.17 # пароль введёшь сам
|
||
```
|
||
|
||
## 2. Обновление + файрвол
|
||
```bash
|
||
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 (официальный репозиторий)
|
||
```bash
|
||
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. Пользователь под туннели
|
||
```bash
|
||
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 — залей свои файлы и склей:
|
||
```bash
|
||
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):
|
||
```bash
|
||
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](../backup/README.md#шаг-3-vps-пользователь-только-для-sftp).
|
||
|
||
Проверка приёма после первого бэкапа: `ls -la /srv/fs-backups/restic` (там `config`, `data`,
|
||
`index`, `keys`, `snapshots`). Старые снимки удаляет сам контейнер с Pi по политике хранения.
|