Vaultwarden: self-hosted password manager¶
Личный менеджер паролей на VPS vm-mini, Bitwarden-совместимый.
Дата снимка: 05.08.2026 Хост: VPS vm-mini (Hostkey, 46.17.99.183) Домен: https://vault.alatyr-service.ru Fallback по IP: https://46.17.99.183:8443/ (когда DNS не работает или SNI блокируется)
Быстрая справка¶
| Параметр | Значение |
|---|---|
| Развёртывание | Docker Compose, restart: unless-stopped |
| Image | vaultwarden/server:latest |
| Container name | vaultwarden |
| Внутренний порт | 80 (в контейнере) |
| Публикуется на | 127.0.0.1:8222 (только localhost, наружу через nginx) |
| Данные | /opt/vaultwarden/data/ (bind mount → /data) |
| БД | SQLite (db.sqlite3) |
| Регистрация | SIGNUPS_ALLOWED=false (только по invite) |
| Приглашения | INVITATIONS_ALLOWED=true |
| Push (mobile) | PUSH_ENABLED=false |
| SMTP | Не настроен ⚠ |
| Log | /opt/vaultwarden/data/vaultwarden.log |
| fail2ban jails | vaultwarden, vaultwarden-admin (active) |
| Бэкапы | Ежедневно 03:15, GPG-зашифрованные, 30-дневный ретеншн |
Расположение файлов на VPS¶
/opt/vaultwarden/
├── .env # секреты и конфиг (chmod 600)
├── .env.backup.1784919342 # старый бэкап .env (перед изменениями)
├── docker-compose.yml # определение сервиса
├── data/ # bind-mounted в контейнер как /data
│ ├── db.sqlite3 # основная БД (заблокирована пока Vaultwarden работает)
│ ├── db.sqlite3-shm # SQLite shared memory
│ ├── db.sqlite3-wal # SQLite WAL
│ ├── attachments/ # прикреплённые файлы
│ ├── sends/ # Bitwarden Send (эфемерные ссылки)
│ ├── config.json # runtime-конфиг (менеджмент через /admin)
│ ├── rsa_key.pem # приватный ключ для шифрования (КРИТИЧНО!)
│ ├── rsa_key.pub.pem # публичный ключ
│ └── vaultwarden.log # лог warn+ (для fail2ban)
└── backups/ # локальные бэкапы (chmod 700)
├── backup.sh # скрипт бэкапа (chmod 700)
├── backup.log # лог выполнения
└── vaultwarden-backup-YYYY-MM-DD_HHMMSS.tar.gz.gpg # шифрованные архивы
docker-compose.yml¶
services:
vaultwarden:
image: vaultwarden/server:latest
container_name: vaultwarden
restart: unless-stopped
env_file: .env
volumes:
- ./data:/data
ports:
# Слушаем ТОЛЬКО на 127.0.0.1 — снаружи Vaultwarden недоступен,
# весь трафик пойдёт через nginx с TLS
- "127.0.0.1:8222:80"
healthcheck:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:80/alive || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 30s
Ключевые решения:
- ✅
127.0.0.1:8222:80— порт слушает только на loopback, снаружи Vaultwarden не виден - ✅ Весь трафик снаружи идёт через nginx:
vault.alatyr-service.ru:443 → 127.0.0.1:8222 - ✅
restart: unless-stopped— автозапуск после reboot - ⚠
image: vaultwarden/server:latest— фиксация версии в TODO
TODO:
- [ ] Зафиксировать точную версию image (vaultwarden/server:1.30.5 вместо :latest) — чтобы breaking-change не сломал в момент обновления
.env (только структура, без секретов)¶
Файл: /opt/vaultwarden/.env (29 строк, chmod 600).
Активные переменные:
| Переменная | Значение | Комментарий |
|---|---|---|
DOMAIN |
https://vault.alatyr-service.ru |
Основной URL, обязательно с https:// |
ADMIN_TOKEN |
$argon2id$v=19$m=65540,t=3,p=<токен> |
Argon2-хэш для /admin панели, оригинал offline |
SIGNUPS_ALLOWED |
false |
Регистрация закрыта |
SIGNUPS_VERIFY |
false |
Верификация email не требуется |
INVITATIONS_ALLOWED |
true |
Приглашения разрешены |
LOG_LEVEL |
warn |
Логировать warn и выше |
EXTENDED_LOGGING |
true |
Расширенные логи с IP |
LOG_FILE |
/data/vaultwarden.log |
Файл, который читает fail2ban |
PUSH_ENABLED |
false |
Push отключён (нет mobile) |
TZ |
Europe/Moscow |
Таймзона |
IP_HEADER |
X-Real-IP |
Реальный IP от nginx |
Не настроено (TODO):
- ❌ SMTP — нет
SMTP_HOST,SMTP_FROM,SMTP_USERNAME,SMTP_PASSWORD→ нельзя отправлять email (invites, password reset, 2FA email)
Рекомендация: подключить SMTP через Yandex 360 (уже настроен для alatyr-service.ru):
SMTP_HOST=smtp.yandex.ru
SMTP_FROM=vault@alatyr-service.ru
SMTP_FROM_NAME=Alatyr Vault
SMTP_SECURITY=starttls
SMTP_PORT=587
SMTP_USERNAME=vault@alatyr-service.ru
SMTP_PASSWORD=<application password из Yandex>
SMTP_AUTH_MECHANISM=Plain
TODO:
- [ ] Создать почтовый ящик vault@alatyr-service.ru в Yandex 360
- [ ] Сгенерировать application password
- [ ] Добавить SMTP-блок в .env, docker compose down && up -d
- [ ] Проверить отправку через /admin → Send test email
nginx перед Vaultwarden¶
Файл: /etc/nginx/sites-enabled/vault.alatyr-service.ru
Server blocks:
- HTTP → HTTPS (
listen 80): 301 →https://vault.alatyr-service.ru - HTTPS основной (
listen 443 ssl+listen 8443 ssl): proxy_pass http://127.0.0.1:8222- WebSocket ready (
proxy_http_version 1.1,Upgradeheaders) X-Real-IP,X-Forwarded-For,X-Forwarded-Proto→ Vaultwarden видит реальный IP
Особенности:
listen 8443 ssl— резервный доступ по IPhttps://46.17.99.183:8443/в обход DNS/SNI (подробнее см. nginx-vps-configs.md)- Отдельного
access_logнет — всё летит в общий/var/log/nginx/access.log(TODO)
Сертификат:
- CN:
vault.alatyr-service.ru - Issuer: Let's Encrypt
- Расположение:
/etc/letsencrypt/live/vault.alatyr-service.ru/{fullchain,privkey}.pem - Автообновление через certbot systemd timer
🔴 Проблема: DPI провайдера Inetcom блокирует vault.alatyr-service.ru¶
✅ 06.08.2026 — блокировка временно снята¶
Повторные тесты с домашнего Wi-Fi Инетком (без VPN):
PS C:\Users\user> curl.exe -sI --max-time 10 https://vault.alatyr-service.ru
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Thu, 06 Aug 2026 09:53:14 GMT
...
С рабочего ПК через Инетком без AmneziaWG — теперь отвечает нормально. Сайт открывается, TLSv1.3 handshake проходит.
Гипотезы: - Провайдер обновил правила DPI — снял ключевое слово "vault" или конкретный домена - 05.08 было временное срабатывание DPI (какая-то A/B-кампания?) - Изменился DPI-вендор у провайдера
Статус: наблюдаем. AmneziaWG остаётся на случай возврата. Фул-диагностика (задача 4 в TODO) — пропускаем до возврата проблемы.
✅ 07.08.2026 — Healthcheck исправлен (wget → curl)¶
При фиксации версии выяснилось что healthcheck давно был unhealthy — в образах vaultwarden/server нет wget, есть только curl. Ошибка маскировалась на :latest просто тем что никто не смотрел docker ps внимательно.
Исправлено в /opt/vaultwarden/docker-compose.yml:
# было:
test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:80/alive || exit 1"]
# стало:
test: ["CMD-SHELL", "curl -fsS http://localhost:80/alive || exit 1"]
Проверка:
sudo docker ps --filter name=vaultwarden --format 'table {{.Names}}\t{{.Status}}'
# vaultwarden Up About a minute (healthy)
✅ 07.08.2026 — Зафиксирована версия образа¶
Задача 23 закрыта. В /opt/vaultwarden/docker-compose.yml (VPS 46.17.99.183):
Резервная копия сохранена в docker-compose.yml.bak. Перекат без downtime — sha256 тот же, что был у :latest.
Как обновлять в будущем:
1. Проверить changelog: https://github.com/dani-garcia/vaultwarden/releases
2. Отредактировать image: в docker-compose.yml на новый тег
3. Обязательно: бэкап ./data перед обновлением
4. docker compose pull && docker compose up -d
5. Проверить docker ps и curl -sI https://vault.alatyr-service.ru
✅ 07.08.2026 — HSTS добавлен в nginx¶
Задача 21a закрыта. В /etc/nginx/sites-available/vault.alatyr-service.ru (на VPS 46.17.99.183) в HTTPS server-блок добавлена строка:
Расположение: сразу после ssl_dhparam в первом блоке (443/8443).
Проверка:
curl -sI https://vault.alatyr-service.ru | grep -i strict-transport
# Strict-Transport-Security: max-age=31536000; includeSubDomains
Полный набор security headers на vault.alatyr-service.ru: | Заголовок | Источник | |---|---| | Strict-Transport-Security | nginx (07.08.2026) | | X-Frame-Options: SAMEORIGIN | Vaultwarden | | Content-Security-Policy (полный) | Vaultwarden | | Permissions-Policy (27 фичей) | Vaultwarden | | X-Content-Type-Options: nosniff | Vaultwarden | | Referrer-Policy: same-origin | Vaultwarden | | X-Robots-Tag: noindex, nofollow | Vaultwarden | | Cross-Origin-Resource-Policy: same-origin | Vaultwarden |
Симптомы (05.08.2026)¶
- С домашней сети (провайдер Inetcom / Инетком, Железнодорожный) при попытке открыть
https://vault.alatyr-service.ru—ERR_CONNECTION_RESET - С мобильного интернета (МТС/Билайн/Мегафон) и в VPN (AmneziaWG) — работает нормально
curl -v https://vault.alatyr-service.ruс домашнего роутера обрывается на TLS ClientHello с SNI =vault.alatyr-service.ru- Тот же IP (
176.99.153.164) по другому домену (rag.alatyr-service.ru) — открывается
Диагноз¶
DPI провайдера Inetcom блокирует TLS-соединения по SNI с частью "vault" в имени. Вероятно:
- Срабатывает keyword-блок на слова
vault,wallet,cryptoв SNI - Или IP
176.99.153.164в blacklist'е (но тогда быrag.тоже не работал) — маловероятно - Или конкретно домен
vault.alatyr-service.ruпопал в какой-то список (RKN / чёрный список Inetcom)
Обходы (по приоритету)¶
1. AmneziaWG VPN на домашнем клиенте — рабочее решение:
Клиент Amnezia (Windows/Android) → 8443/udp (обфускация) → VPS 46.17.99.183 → nginx :443 → Vaultwarden
DPI видит только UDP-мусор на порт 8443 (обфускация Amnezia), SNI в TLS ему не виден.
2. Fallback по IP без SNI — аварийный вариант:
- URL: https://46.17.99.183:8443/
- Браузер скажет "не доверяю сертификату" (CN не совпадает с IP) → продолжить вручную
- Bitwarden CLI тоже может работать через
bw config server https://46.17.99.183:8443 --disable-cert-check - Работает потому что SNI в TLS ClientHello будет в IP-виде (или отсутствовать), DPI не найдёт
vaultв имени
3. Bitwarden mobile app через мобильную сеть — не через Wi-Fi дома:
- В настройках Bitwarden: Self-hosted →
https://vault.alatyr-service.ru - На Wi-Fi дома — не работает, на мобильной (МТС и т.д.) — работает
Что не стоит делать¶
- ❌ Менять домен на
vw.alatyr-service.ruилиpw.alatyr-service.ru— может временно помочь, но Inetcom вероятно подтянет правило; кроме того сломает все закладки в клиентах Bitwarden - ❌ ECH (Encrypted Client Hello) — не все nginx билды поддерживают, требует координации с CDN
- ❌ Жалоба провайдеру — Inetcom скорее всего выполняет требования РКН/центрального DPI, снять не сможет
Повторная диагностика когда проблема вернётся¶
С машины в домашней сети (без VPN):
# 1. DNS вообще работает?
dig vault.alatyr-service.ru +short
# Ожидаем: 176.99.153.164
# 2. TCP доходит?
curl -v --connect-to vault.alatyr-service.ru:443:176.99.153.164:443 https://vault.alatyr-service.ru 2>&1 | head -30
# Если "Connection reset by peer" на TLS handshake → DPI режет SNI
# 3. Тот же IP без SNI?
curl -vk https://176.99.153.164:8443/alive
# Если 200 OK → DPI точно по SNI блокирует домен
# 4. Другие поддомены alatyr-service.ru тоже блокируются?
curl -sI https://rag.alatyr-service.ru | head -3
curl -sI https://alatyr-service.ru | head -3
TODO:
- [ ] Проверить какой именно паттерн DPI ловит (curl с разными SNI: test-vault., vlt., v4ult. — что пройдёт?)
- [ ] Задокументировать в amneziawg-tunnels.md что VPN теперь обязательный для домашнего доступа к Vaultwarden
Бэкапы¶
Расписание¶
Cron root:
Ежедневно в 03:15 локального времени сервера (Europe/Moscow).
Что бэкапится¶
Скрипт /opt/vaultwarden/backups/backup.sh:
VACUUM INTO— консистентный дамп SQLite (не битый при живой БД)- Копирует в staging-каталог (
/tmp/vw_staging_<date>): db.sqlite3(свежий дамп)attachments/(все прикреплённые файлы)sends/(Bitwarden Send)config.jsonrsa_key.pem,rsa_key.pub.pem← критично, без них БД нельзя расшифровать.env(в файлenv.backup) ← включая ADMIN_TOKENdocker-compose.ymltar -czf— сжимает в архив- GPG AES-256 symmetric encryption с паролем из
/root/.vw_backup_pass - Удаляет незашифрованный архив и staging
- Ретеншн 30 дней — старые
.gpgудаляются автоматом - Всё логируется в
backup.log
Текущее состояние (05.08.2026)¶
- ✅ 13 бэкапов подряд (25.07 — 05.08), ни одного пропуска
- ✅ Размер стабилен ~23 KB (база растёт линейно)
- ✅ Все зашифрованы GPG AES-256
- ✅ Retention 30 дней работает
🔴 Критичные пробелы disaster recovery¶
| Пункт | Состояние | Риск |
|---|---|---|
| GPG-пароль offline (не только на VPS) | ✅ Записан на бумаге / в сейфе | Защищено |
| Копия бэкапов вне VPS | ❌ Только локально на VPS | Пропадёт VPS → пропадут бэкапы |
| Тест восстановления | ❌ Не тестировалось | Скрипт может работать, но restore может не сработать |
Правило: непроверенный бэкап = отсутствующий бэкап.
План восстановления (drill в Block 4)¶
Шаг 1 — тест на локальной машине или в отдельной директории на VPS:
# Расшифровать
gpg --decrypt --output vaultwarden-restore.tar.gz \
/opt/vaultwarden/backups/vaultwarden-backup-2026-08-04_031501.tar.gz.gpg
# (пароль ввести вручную из бумажной копии)
# Распаковать
mkdir /tmp/vw-restore
tar -xzf vaultwarden-restore.tar.gz -C /tmp/vw-restore
# Проверить что БД корректная
sqlite3 /tmp/vw-restore/db.sqlite3 "SELECT COUNT(*) FROM users;"
sqlite3 /tmp/vw-restore/db.sqlite3 "SELECT COUNT(*) FROM ciphers;"
Шаг 2 — если БД корректная, восстановление в новую директорию:
docker compose -f test-compose.yml down
cp -r /tmp/vw-restore/* /opt/vw-test/data/
docker compose -f test-compose.yml up -d
# зайти на test.vault.alatyr-service.ru, проверить логин + записи
TODO:
- [ ] Настроить off-site бэкап (варианты):
- rsync/rclone на ragserver (/opt/knowledge/vault-backups/)
- S3-совместимое хранилище (Yandex Object Storage)
- Резервный VPS
- [ ] Раз в квартал делать test restore — задокументировать в KB (или cron с автоматической проверкой чек-суммы)
- [ ] Установить pass_check.sh — скрипт, который раз в неделю пытается расшифровать последний бэкап и валидирует БД
fail2ban¶
Активные jails (см. также nginx-vps-configs.md § Состояние безопасности):
| Jail | Filter | Что ловит |
|---|---|---|
vaultwarden |
Username or password is incorrect. Try again. IP: <ADDR>. |
Failed login на веб-vault |
vaultwarden-admin |
(тот же лог, специальный regex) | Failed попытки на /admin |
Оба на 05.08.2026 = 0 events — никто не пытается ломать (см. также подробный разбор в nginx-vps-configs.md).
Логи Vaultwarden при этом пишутся — грепом видим только WARN про dotfile-сканеров (BadStart('.')), которые Vaultwarden сам блочит через PathBuf guard. Реальных попыток брутфорса пароля не было.
Ежедневная работа¶
Проверить статус¶
sudo docker compose -f /opt/vaultwarden/docker-compose.yml ps
sudo docker logs vaultwarden --tail 20
Хелсчек¶
Логи¶
Ручной бэкап¶
Перезапуск¶
Обновление образа (осторожно с :latest)¶
Доступ к /admin панели¶
- URL: https://vault.alatyr-service.ru/admin
- Токен: оригинал ADMIN_TOKEN хранится offline (в
.envтолько Argon2-хэш) - Что можно: инвайты, blacklist, тестовое письмо, посмотреть/удалить пользователей, изменить runtime-конфиг
TODO (сводка)¶
🔴 Критично (Block 4 hardening)¶
- Off-site бэкап (rsync на ragserver / S3 Yandex / резервный VPS)
- Test restore — хотя бы один раз выполнить полное восстановление в отдельной директории
- Скрипт weekly-check: раз в неделю расшифровка последнего бэкапа +
sqlite3 pragma integrity_check - Проверить паттерн DPI Inetcom — точно по SNI
vaultили есть другие триггеры?
🟡 Улучшения¶
- Настроить SMTP через Yandex 360 для invitations, password reset, 2FA email
- Зафиксировать точную версию image вместо
:latest - Добавить
access_logдля vault-сайта в nginx (сейчас смешано с общим логом) - Пересмотреть
LOG_LEVEL=warn→ возможноinfoдля лучшей аудитности
🟢 Далёкий horizon¶
- Мигрировать с SQLite на PostgreSQL (если пользователей > 10)
- Two-factor authentication для админки
- Sync с мобильными клиентами (тут нужен push — Vaultwarden поддерживает Bitwarden's push, но требует настройки)
История изменений¶
| Дата | Что |
|---|---|
| 2026-08-05 | Первая версия. Задокументированы docker-compose, .env, nginx, бэкапы (ежедневный GPG-скрипт), fail2ban. Найдены пробелы: off-site бэкапов нет, test restore не делали, SMTP не настроен. |
| 2026-08-05 | Добавлен раздел про DPI провайдера Inetcom: vault.alatyr-service.ru блокируется по SNI с домашнего Wi-Fi (ERR_CONNECTION_RESET). Обход — AmneziaWG VPN или fallback по IP https://46.17.99.183:8443/. Мобильные операторы не блокируют. |