Files
vps-server-01/vps-nao-kvn-state.md

230 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Техническое состояние проекта — личный VPS nao-kvn.ru
> Снимок финального актуального состояния для продолжения работы в новом чате.
> Только текущее состояние, без истории обсуждений. Секреты (приватные ключи, пароли, токены) в документ намеренно не вынесены — они лежат в указанных файлах на VPS.
## Быстрый доступ (для продолжения работы)
- **Вход на VPS:** `ssh root@45.91.8.169` — только по ed25519-ключу (ключ на рабочей машине Manjaro, `~/.ssh/id_ed25519`, с passphrase). Пароль отключён.
- **Стек:** `/opt/docker/core/``docker-compose.yml` + `caddy/Caddyfile`.
- **URL сервисов:** `https://vault.nao-kvn.ru`, `https://portainer.nao-kvn.ru`, `https://status.nao-kvn.ru`.
- **Часовой пояс VPS:** MSK.
---
## 1. Цель проекта
Личный VPS «для себя», на котором развёрнуты:
- управление контейнерами (Portainer);
- мониторинг доступности сервисов по трём площадкам с белыми IP — основная работа (ДК «Арктика»), вторая работа (Архангельскстат, Нарьян-Мар) и дом (Uptime Kuma);
- менеджер паролей (Vaultwarden).
Дополнительно: вся инфраструктура должна быть защищена (HTTPS, файрвол, ключи) и иметь проверенную многокопийную резервную копию.
## 2. Архитектура решения
- Один Docker Compose-стек в `/opt/docker/core`.
- **Caddy — единственная публичная точка входа** (порты 80/443), автоматический Let's Encrypt.
- Остальные сервисы (Portainer, Uptime Kuma, Vaultwarden) **портов на хост не публикуют** — доступны только во внутренней docker-сети `core_proxy`, Caddy ходит к ним по имени контейнера. Это устраняет проблему обхода UFW докером и не светит сервисы в интернет.
- Portainer и Uptime Kuma закрыты дополнительным слоем Caddy basic-auth; Vaultwarden публичен намеренно (защищён собственной авторизацией + мастер-паролем).
- Для офсайт-бэкапа на домашний SMB поднят WireGuard-туннель VPS → домашний роутер Keenetic (SMB наружу не выставляется).
## 3. Серверы и их роли
**VPS (основной):**
- IP `45.91.8.169`, хост `box-938039`.
- Ubuntu 24.04.4 LTS, виртуализация KVM.
- 1 vCPU (Intel Xeon Skylake @ 2.0 GHz), 961 MiB RAM, диск 20 ГБ (`/dev/vda1` ~19 ГБ).
- Swap 2 ГБ (`/swapfile`, в fstab), `vm.swappiness=10`. Локаль `C.UTF-8`. ТЗ MSK.
- Тариф 400₽/мес. RAM в простое со стеком ~0.5 ГБ из 0.96 ГБ — запас небольшой.
- Роль: Docker-стек сервисов, WireGuard-клиент к дому, узел резервного копирования.
**Домашний роутер Keenetic Hopper KN-3811:**
- Белый IP `91.122.209.x` (последний октет в документе скрыт; реальный endpoint — в `/etc/wireguard/wg0.conf` на VPS).
- WireGuard-**сервер**; VPS подключён к нему как пир.
- SMB-сервер: USB-диск, шара **`TOSHIBA EXT`**, выделенный пользователь `only_vps`.
- Домашняя LAN `172.16.0.0/24` (адрес роутера/SMB-хоста `172.16.0.1`).
## 4. Используемые сервисы (контейнеры)
Все в стеке `/opt/docker/core`, сеть `core_proxy`, политика `restart: unless-stopped`.
| Сервис | Образ | Внутр. порт | Доступ |
|---|---|---|---|
| caddy | `caddy:2` | 80/443 (на хост) | reverse-proxy + TLS |
| portainer | `portainer/portainer-ce:lts` | 9000 | `portainer.nao-kvn.ru`, за basic-auth |
| uptime-kuma | `louislam/uptime-kuma:1` | 3001 | `status.nao-kvn.ru`, за basic-auth |
| vaultwarden | `vaultwarden/server:latest` | 80 | `vault.nao-kvn.ru`, публичный |
Vaultwarden env: `DOMAIN=https://vault.nao-kvn.ru`, `SIGNUPS_ALLOWED=false`, `ADMIN_TOKEN` задан (значение в `docker-compose.yml`).
Portainer: админ-логин `administrator` (пароль задан вручную).
Volumes: `core_caddy_data`, `core_caddy_config`, `core_portainer_data`, `core_uptime_data`, `core_vaultwarden_data`.
## 5. Выполненные настройки безопасности
- HTTPS на всех сервисах (Caddy + Let's Encrypt).
- Наружу опубликованы только 80/443 (Caddy); сервисные порты на хост не пробрасываются.
- Caddy basic-auth (логин `admin`, bcrypt-хеш) перед Portainer и Uptime Kuma — через сниппет `(protected)`.
- Регистрация в Vaultwarden закрыта (`SIGNUPS_ALLOWED=false`).
- UFW: входящие закрыты, разрешён только SSH.
- fail2ban на SSH (бан через UFW).
- SSH только по ключу, парольный вход отключён, root — `prohibit-password`.
- Лимиты docker-логов (10 МБ × 3) против переполнения диска.
- SMB доступен только через приватный WireGuard-туннель; порт 445 в интернет не выставлен.
## 6. Настройки файрвола (UFW)
- Статус: active. Default: `deny (incoming)`, `allow (outgoing)`, `deny (routed)`.
- Разрешено: `22/tcp (OpenSSH)` — IPv4 и IPv6. Больше ничего во `ALLOW IN`.
- Порты 80/443 управляются Docker через собственные правила iptables (в обход UFW — это ожидаемо и нужно для Caddy).
- Баны fail2ban оформляются как `deny`-правила UFW (`banaction = ufw`), видны в `ufw status`.
## 7. Настройки SSH
Файл `/etc/ssh/sshd_config.d/00-hardening.conf` (префикс `00-` выбран намеренно — sshd берёт первое значение, так наши параметры перебивают cloud-init):
```
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
```
- Порт 22. Вход: root по ed25519-ключу.
- Приватный ключ — на машине Manjaro (`~/.ssh/id_ed25519`, защищён passphrase). Публичный — в `/root/.ssh/authorized_keys` на VPS.
## 8. Настройки резервного копирования
**Объект:** данные Vaultwarden (`core_vaultwarden_data``/var/lib/docker/volumes/core_vaultwarden_data/_data`).
**Метод:** консистентный снимок БД через `sqlite3 .backup` (учитывает WAL) + `rsa_key*`/`config.json`/`attachments`/`sends``tar.gz`.
**Три копии:**
1. Локально: `/opt/backups/vaultwarden/`, ретенция — последние 14 архивов.
2. Google Drive (обязательная): rclone-remote `gdrive``gdrive:vaultwarden-backups/`, ретенция 90 дней.
3. Домашний SMB (best-effort, не валит бэкап при недоступности дома): rclone-remote `homesmb``homesmb:TOSHIBA EXT/vaultwarden-backups/`, ретенция 90 дней.
**Восстановление проверено фактически:** `PRAGMA integrity_check = ok`, аккаунт и записи присутствуют, `rsa_key.pem` на месте (проверка через временный контейнер и прямой инспекцией БД).
**Расписание:** systemd-таймер `vw-backup.timer`, ежедневно 03:30 MSK, `Persistent=true` (догоняет пропущенный запуск). Сервис `vw-backup.service` (oneshot, `Environment=HOME=/root`).
**Скрипт:** `/usr/local/bin/vw-backup.sh` (см. раздел 11).
rclone-конфиг: `/root/.config/rclone/rclone.conf`
- `gdrive` — type `drive`, scope `drive.file` (rclone видит только свои файлы).
- `homesmb` — type `smb`, host `172.16.0.1`, user `only_vps`, пароль зашифрован (`--obscure`).
## 9. Настройки мониторинга
- Uptime Kuma развёрнут: `https://status.nao-kvn.ru` (basic-auth → собственный логин), админ-аккаунт создан.
- **Мониторы и уведомления ещё НЕ настроены** — это незавершённая часть (см. разделы 15 и 17).
- Канал уведомлений: Telegram **отвергнут** (массово блокируется/тротлится в РФ с весны 2026). Планируется ntfy (self-hosted) или email.
## 10. Установленные пакеты и компоненты
- Docker CE `29.6.0`, `containerd.io 2.2.5`, плагин `docker compose`.
- `sqlite3` — для консистентного снимка БД Vaultwarden.
- `rclone` (с поддержкой SMB-бэкенда) — выгрузка в Google Drive и SMB.
- `wireguard-tools` — туннель к дому.
- `smbclient` — диагностика SMB.
- `fail2ban`, `ufw`.
- daemon.json: `{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"}}`.
## 11. Созданные скрипты
`/usr/local/bin/vw-backup.sh` (права 700):
```bash
#!/usr/bin/env bash
set -euo pipefail
VOL="/var/lib/docker/volumes/core_vaultwarden_data/_data"
DEST="/opt/backups/vaultwarden"
KEEP=14
TS="$(date +%Y%m%d-%H%M%S)"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$DEST"
sqlite3 "$VOL/db.sqlite3" ".timeout 10000" ".backup '$WORK/db.sqlite3'"
cp -a "$VOL"/rsa_key* "$WORK"/ 2>/dev/null || true
cp -a "$VOL"/config.json "$WORK"/ 2>/dev/null || true
cp -a "$VOL"/attachments "$WORK"/ 2>/dev/null || true
cp -a "$VOL"/sends "$WORK"/ 2>/dev/null || true
ARCHIVE="$DEST/vaultwarden-$TS.tar.gz"
tar -czf "$ARCHIVE" -C "$WORK" .
ls -1t "$DEST"/vaultwarden-*.tar.gz | tail -n +$((KEEP+1)) | xargs -r rm -f
# офсайт 1: Google Drive (обязательный)
rclone copy "$ARCHIVE" gdrive:vaultwarden-backups/
rclone delete --min-age 90d gdrive:vaultwarden-backups/ 2>/dev/null || true
# офсайт 2: домашний SMB через WG (best-effort)
if rclone copy "$ARCHIVE" "homesmb:TOSHIBA EXT/vaultwarden-backups/" 2>/dev/null; then
rclone delete --min-age 90d "homesmb:TOSHIBA EXT/vaultwarden-backups/" 2>/dev/null || true
SMB="ok"
else
SMB="недоступен"
fi
echo "OK: $ARCHIVE ($(du -h "$ARCHIVE" | cut -f1)) -> локально + gdrive + smb:$SMB"
```
## 12. Cron-задачи и автоматизация
- **Cron не используется.**
- systemd-таймер `vw-backup.timer` — ежедневно 03:30 MSK (`Persistent=true`).
- `wg-quick@wg0`**enabled**, WireGuard-туннель поднимается при загрузке.
- Все контейнеры — `restart: unless-stopped` (поднимаются после ребута сами).
- Логи бэкапа: `journalctl -u vw-backup.service`.
## 13. Используемые домены и DNS
- Домен: **nao-kvn.ru**.
- A-записи → `45.91.8.169`: `portainer`, `status`, `vault`.
- Дом: белый IP `91.122.209.x` на роутере Keenetic (endpoint WG — в конфиге на VPS).
## 14. SSL/TLS конфигурация
- Caddy автоматически выпускает и продлевает сертификаты Let's Encrypt для `portainer.nao-kvn.ru`, `status.nao-kvn.ru`, `vault.nao-kvn.ru`.
- Валидация — `TLS-ALPN-01`. Сертификаты в volume `core_caddy_data`. Автопродление включено.
- `Caddyfile` (хеш basic-auth скрыт — реальный в файле на VPS):
```
(protected) {
basic_auth {
admin <bcrypt-хеш в /opt/docker/core/caddy/Caddyfile>
}
}
portainer.nao-kvn.ru {
import protected
reverse_proxy portainer:9000
}
status.nao-kvn.ru {
import protected
reverse_proxy uptime-kuma:3001
}
vault.nao-kvn.ru {
reverse_proxy vaultwarden:80
}
```
## WireGuard (конфиг для справки)
`/etc/wireguard/wg0.conf` (приватный ключ и endpoint скрыты — в файле на VPS):
```
[Interface]
PrivateKey = <в файле на VPS>
Address = 10.0.0.5/32
[Peer]
PublicKey = <публичный ключ WG-сервера Keenetic>
Endpoint = 91.122.209.x:<порт>
AllowedIPs = 10.0.0.0/24, 172.16.0.0/24
PersistentKeepalive = 25
```
- Туннельный IP VPS: `10.0.0.5`; Keenetic в туннеле: `10.0.0.1`.
- Через туннель маршрутизируется домашняя LAN `172.16.0.0/24` (где живёт SMB).
## 15. Незавершённые задачи
- **Мониторинг не введён в строй:** в Uptime Kuma не созданы мониторы по трём площадкам и не настроен канал уведомлений. Это исходная цель проекта, пока не достигнута.
- **Канал уведомлений не выбран/не поднят:** Telegram непригоден (блокировки в РФ); нужно ntfy (self-hosted) или email.
- **Перенос паролей** из бумажного блокнота в Vaultwarden ещё не сделан (бэкап проверен, делать безопасно).
## 16. Известные риски
- **RAM в обрез:** ~0.5 ГБ занято из 0.96 ГБ. Любой тяжёлый сервис потребует апгрейда тарифа (до 2 ГБ).
- **Нет рабочего мониторинга** → если что-то на площадках упадёт, оповещения не придёт (главная функциональная дыра на данный момент).
- **`ADMIN_TOKEN` Vaultwarden — открытым текстом** в `docker-compose.yml`. Приемлемо для машины с единственным root, но это компромисс.
- **SMB-копия зависит от доступности дома** (by design best-effort): если дом офлайн, третьей копии за эту ночь не будет (локальная + Google Drive остаются).
- **Один VPS — единая точка отказа** по доступности (бэкапы спасают данные, но не аптайм).
## 17. Следующие шаги
1. Поднять канал уведомлений: **ntfy** self-hosted на этом же VPS (`ntfy.nao-kvn.ru`, через Caddy, лёгкий) — приоритетный вариант под РФ; либо email (SMTP).
2. В Uptime Kuma создать мониторы по трём площадкам: ping шлюзов (отличать «упала площадка» от «упал сервис») + TCP/HTTP-проверки ключевых сервисов; сгруппировать «Арктика / Архангельскстат / Дом».
3. Перенести пароли из блокнота в Vaultwarden, после чего блокнот можно уничтожить.
4. Опционально: `live-restore` для Docker (чтобы обновления движка не роняли контейнеры), периодический `docker system prune`, апгрейд до 2 ГБ при расширении.
---
*Принципы работы для продолжения: продакшен-аккуратность — для рискованных изменений сначала проверка, затем применение, с путём отката; по одному шагу за раз с ожиданием подтверждения; в multi-step процедурах не объединять шаги.*