# Техническое состояние проекта — личный 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 } } 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 процедурах не объединять шаги.*