Перейти к основному содержимому

Vaultwarden за nginx — меняем Caddy на nginx

logo

В предыдущей заметке я описал, как поднять Vaultwarden (Bitwarden-совместимый сервер на Rust) на Docker Compose вместе с Caddy, чтобы последний автоматически получал TLS-сертификаты у Let’s Encrypt и проксировал запросы.

Такая схема работала стабильно. Однако со временем появились причины заменить Caddy на nginx. В этой статье я расскажу, как выполнил миграцию.

Зачем менять Caddy на nginx
#

Caddy — отличный инструмент, особенно для домашних серверов: минимальная конфигурация, автоматический TLS и мало зависимостей. Но у меня все завернуто через nginx все равно, лучше все в одном уж месте.

Архитектура после замены
#

Было:

Сеть → Caddy:443 (TLS + ACME) → Vaultwarden:80 (внутри Docker)

Стало:

Сеть → Nginx:443 (TLS, Let's Encrypt, статика) → Vaultwarden:8001 (HTTP)

Vaultwarden больше не занимается терминацией TLS. Он обслуживает чистый HTTP на порту 8001, а nginx обеспечивает защищенное соединение. Это соответствует официальной рекомендации разработчиков Vaultwarden: использовать обратный прокси для TLS.

Требования
#

  • Сервер с работающим Vaultwarden (в моем случае — нативная установка через DietPi).
  • Установленный nginx.
  • Доменное имя (например, vault.beaverbloger.com).
  • Действующие сертификаты Let’s Encrypt, выпущенные через dietpi-letsencrypt.
  • Открытые порты 80 и 443 на фаерволе/роутере (или использование Tailscale).

Шаг 1. Отключаем встроенный TLS в Vaultwarden
#

Если ранее Vaultwarden работал по HTTPS, необходимо убрать эту настройку. Параметры хранятся в файле окружения (для DietPi это обычно /mnt/dietpi_userdata/vaultwarden/vaultwarden.env).

Найдите и удалите или закомментируйте строку:

ROCKET_TLS={certs="./fullchain.pem",key="./privkey.pem"}

Убедитесь, что порт указан верно (по умолчанию 8001):

ROCKET_PORT=8001

Перезапустите сервис:

dietpi-services restart vaultwarden

Проверьте, что сервис слушает только HTTP:

curl http://127.0.0.1:8001

Шаг 2. Установка nginx
#

Если nginx еще не установлен, используйте DietPi Software:

dietpi-software install 85

Проверьте версию и статус службы:

nginx -v
dietpi-services status nginx

Убедитесь, что порты 80 и 443 свободны. Если вы ранее использовали Caddy, остановите и отключите его службу.

Шаг 3. Получение сертификатов через dietpi-letsencrypt
#

Вместо ручной настройки certbot используем штатную утилиту DietPi.

Запустите мастер настройки:

dietpi-letsencrypt
  1. Укажите свой домен (например, vault.beaverbloger.com).
  2. Утилита предложит настроить nginx. Поскольку мы будем использовать кастомный конфиг, можно согласиться на базовую настройку.

dietpi-letsencrypt использует certbot под капотом. Сертификаты будут обновляться автоматически по расписанию, но нам нужно научить nginx подхватывать новые файлы.

Шаг 4. Настройка nginx
#

Конфигурация состоит из upstream-блока и server-блоков

Upstream и поддержка WebSocket
#

Vaultwarden использует WebSocket для push-уведомлений. Nginx должен корректно обрабатывать апгрейд соединения. Создайте файл /etc/nginx/conf.d/vaultwarden_upstream.conf:

upstream vaultwarden {
    server 127.0.0.1:8001;
    keepalive 2;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ""      "";
}

Виртуальный хост
#

Создайте файл /etc/nginx/sites-available/vaultwarden:

# HTTP -> HTTPS редирект
server {
    listen 80;
    listen [::]:80;
    server_name vault.beaverbloger.com;
    return 301 https://$host$request_uri;
}

# HTTPS
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name vault.beaverbloger.com;

    # Пути к сертификатам (копии для nginx)
    ssl_certificate /etc/ssl/vaultwarden/fullchain.pem;
    ssl_certificate_key /etc/ssl/vaultwarden/privkey.pem;

    # Безопасные настройки TLS
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
    ssl_prefer_server_ciphers off;

    # HSTS
    add_header Strict-Transport-Security "max-age=604800; includeSubDomains" always;

    # Лимит размера загружаемых файлов (вложения)
    client_max_body_size 525M;

    location / {
        proxy_pass http://vaultwarden;
        proxy_http_version 1.1;
        
        # Заголовки для WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        
        # Стандартные прокси-заголовки
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Активируйте конфиг и проверьте синтаксис:

ln -sf /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/
nginx -t
dietpi-services restart nginx

Шаг 5. Автоматическое обновление сертификатов
#

Поскольку мы используем dietpi-letsencrypt, сертификаты обновляются автоматически. Однако nginx не увидит новые файлы, если они просто заменятся в папке /etc/letsencrypt/ (из-за прав доступа и символических ссылок).

Решение ленивое: копировать сертификаты в отдельную директорию, доступную nginx, и перезагружать веб-сервер через deploy-хук.

Создайте скрипт /etc/letsencrypt/renewal-hooks/deploy/vaultwarden.sh:

#!/bin/bash
DOMAIN="vault.beaverbloger.com"
SSL_DEST="/etc/ssl/vaultwarden"

mkdir -p "$SSL_DEST"

cp "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" "$SSL_DEST/fullchain.pem"
cp "/etc/letsencrypt/live/$DOMAIN/privkey.pem" "$SSL_DEST/privkey.pem"

chown root:www-data "$SSL_DEST/privkey.pem"
chmod 640 "$SSL_DEST/privkey.pem"
chmod 644 "$SSL_DEST/fullchain.pem"

dietpi-services restart nginx

Сделайте скрипт исполняемым:

chmod +x /etc/letsencrypt/renewal-hooks/deploy/vaultwarden.sh

Теперь при каждом успешном обновлении сертификата через dietpi-letsencrypt (который вызывает certbot) этот скрипт выполнится автоматически, скопирует свежие ключи и мягко перезагрузит nginx.

Проверка работы
#

  1. TLS:
    echo | openssl s_client -connect vault.beaverbloger.com:443 2>/dev/null | openssl x509 -noout -subject
  2. Доступность API:
    curl https://vault.beaverbloger.com/api/config
  3. WebSocket: Откройте веб-интерфейс Vaultwarden в браузере
  4. Логи:
    tail -f /var/log/nginx/error.log

Заключение
#

Миграция с Caddy на nginx в экосистеме DietPi требует немного больше ручной настройки, но дает полный контроль над параметрами. Использование штатной утилиты dietpi-letsencrypt для выпуска сертификатов и кастомного deploy-хука для их доставки в nginx позволяет сохранить автоматизацию обновлений, не жертвуя гибкостью конфигурации веб-сервера.

Related