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

16 KiB
Raw Blame History

Техническое состояние проекта — личный 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/sendstar.gz. Три копии:

  1. Локально: /opt/backups/vaultwarden/, ретенция — последние 14 архивов.
  2. Google Drive (обязательная): rclone-remote gdrivegdrive:vaultwarden-backups/, ретенция 90 дней.
  3. Домашний SMB (best-effort, не валит бэкап при недоступности дома): rclone-remote homesmbhomesmb: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):

#!/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@wg0enabled, 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 процедурах не объединять шаги.