Привет, Хабр! История стара как мир: сначала у тебя один пет-проект, потом второй, потом подворачивается фриланс… и вот ты уже тонешь в море из десятков паролей. Логины, ключи API, доступы к базам данных — всё это начинает жить своей жизнью в заметках, текстовых файлах и, о ужас, в памяти.

В какой-то момент я понял, что мой "зоопарк" паролей стал неуправляем. А мысль, что вся эта цифровая сокровищница лежит на серверах очередной "бесплатной" корпорации, начала немного напрягать. Нет, я не параноик, но идея полного контроля над своими самыми чувствительными данными показалась мне чертовски привлекательной.

Так начался мой путь к созданию собственной "крепости" для паролей. Путь, на котором я, конечно же, собрал несколько болезненных "граблей". Эта статья — выжимка моего опыта, чтобы вы могли построить свою крепость правильно с первого раза.

Герой нашего времени: Bitwarden или Vaultwarden?

Первым делом я посмотрел в сторону Bitwarden. Это open-source, у него отличные клиенты под все платформы, и его можно поднять на своём сервере. Идеально? Почти. Когда я открыл мануал по установке официального сервера, я немного приуныл. Он написан на .NET и тащит за собой целую вереницу контейнеров и MSSQL. Для моего скромного VPS, где крутятся пет-проекты, это было явным перебором.

И тут на сцену выходит Vaultwarden (раньше он назывался bitwarden_rs). Это неофициальная реализация сервера Bitwarden, написанная на Rust. И это, друзья, просто магия.

  • Он легковесный. Серьёзно, он работает в одном крошечном Docker-контейнере и потребляет в состоянии покоя смешные 10-15 МБ оперативной памяти.

  • Он простой. Установка занимает буквально 5 минут.

  • Он щедрый. Это ключевое преимущество: Vaultwarden бесплатно отдает фичи, за которые в официальной версии Bitwarden просят денег. Сюда входят:

    • Организации с коллекциями и группами для шеринга паролей.

    • Продвинутые виды 2FA, включая YubiKey, FIDO2 WebAuthn и Duo.

    • Экстренный доступ и многое другое.

Для начинающих сисадминов и разработчиков с домашними серверами выбор очевиден. Мы не хотим тратить ресурсы нашего сервера на тяжеловесного монстра, нам нужна быстрая и эффективная рабочая лошадка.

Поднимаем крепость: как не наступить на мины в конфиге

Изначально я скопировал простейший конфиг из интернета. Он работал, но, как я понял позже, в нём было заложено две мины замедленного действия. На Хабре за такое справедливо сжигают. Давайте сразу сделаем хорошо.

  • Мина №1: Тег latest. Мой первый конфиг содержал строку image: vaultwarden/server:latest. Это самая большая ошибка. latest сегодня и latest завтра — это два совершенно разных образа. Обновление может прилететь в любой момент и всё сломать. (Да, осознаю что может быть спорным решением - дочитайте до конца!)

  • Мина №2: Отключение регистрации через конфиг. Идея "запусти, зарегистрируйся, потом поправь конфиг" — ужасна. Это классический человеческий фактор. Забудешь — и твой сервер превратится в проходной двор. Правильный путь: Использовать админ-панель.

Правильный docker-compose.yml для вашей крепости

Вот конфиг, который учитывает все грабли и лучшие практики.

Главный лайфхак: Мы не будем использовать стандартный порт 8080. Давайте сразу выберем менее популярный, например 8833. Это как поселиться не на центральной площади, а в тихом переулке по соседству — спокойнее и надежнее.

# Создаем папку для проекта
mkdir ~/vaultwarden && cd ~/vaultwarden
# Создаем файл docker-compose.yml
touch docker-compose.yml 
# Зайдем в режим редактирования
docker-compose.yml

Скопируйте это в docker-compose.yml и обязательно поменяйте vault.your-domain.com и значение ADMIN_TOKEN.

# /home/user/vaultwarden/docker-compose.yml
version: '3'

services:
  vaultwarden:
    # МИНА ОБЕЗВРЕЖЕНА: Жестко фиксируем версию!
    # На 12 июля 2025 года актуальная версия 1.34.1
    image: vaultwarden/server:1.34.1
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./data:/data
    ports:
      # МИНА №3 ОБЕЗВРЕЖЕНА: Используем неко-конфликтный порт!
      # Внутренний порт контейнера (80) связывается с портом 8833 на хосте.
      # Nginx будет обращаться именно к 8833.
      - 127.0.0.1:8833:80
    environment:
      DOMAIN: "https://vault.your-domain.com"
      # МИНА ОБЕЗВРЕЖЕНА: Устанавливаем токен для доступа к админке.
      ADMIN_TOKEN: 'your_super_secret_admin_token_here'

Лайфхак: Чтобы сгенерировать хороший токен прямо в консоли, используйте команду: openssl rand -base64 48.

Пример сгенерированной строки: Pxs5M1ojgVo1DTK2Z/zQ4X45fFgmjuPPy71xB15B+2R6QdVtM10VKnv/TRWasjO1

Ставим охранника: HTTPS — это маст-хэв

Я настроил HTTPS, получил заветный зеленый замочек в браузере и поначалу успокоился. Но, как оказалось, я просто запер парадную дверь, оставив все окна нараспашку.

Nginx: «Дверь на замке, окна нараспашку»

Nginx: Фундамент для крепости

Сначала мы построим "фундамент" для нашего охранника Nginx — минимальную конфигурацию, достаточную для того, чтобы Let's Encrypt убедился, что домен действительно наш.

Для начала создадим и откроем файл конфигурации Nginx:

sudo nano /etc/nginx/sites-available/vault.your-domain.com

Вставьте туда этот временный, минимальный конфиг. Не забудьте заменить vault.your-domain.com на свой домен.

server {
    listen 80;
    server_name vault.your-domain.com;

    # Указываем стандартную папку, куда certbot положит файл для проверки
    root /var/www/html;
}

Теперь "включаем" наш сайт и проверяем, что в конфиге нет ошибок:

# Создаем символическую ссылку, чтобы Nginx "увидел" наш конфиг
sudo ln -s /etc/nginx/sites-available/vault.your-domain.com /etc/nginx/sites-enabled/

# Проверяем синтаксис Nginx
sudo nginx -t

# Если проверка прошла успешно (видим "syntax is ok" и "test is successful"), перезагружаем Nginx
sudo systemctl reload nginx
    

Получаем SSL-сертификат с помощью Certbot

Теперь, когда фундамент готов, запускаем "строителя" — certbot. Он автоматически получит SSL-сертификат и, что самое главное, сам перепишет наш конфиг, добавив туда всё необходимое для работы HTTPS.

# Устанавливаем certbot и плагин для Nginx (для Debian/Ubuntu)
sudo apt update && sudo apt install certbot python3-certbot-nginx -y

# Запускаем certbot для нашего домена
sudo certbot --nginx -d vault.your-domain.com
    

Certbot задаст пару вопросов, включая ваш e-mail для уведомлений и согласие с условиями. В конце он спросит, нужно ли делать автоматический редирект с HTTP на HTTPS — обязательно выберите вариант 2 (Redirect).

Возводим стены: добавляем заголовки безопасности

Вот мы и подошли к ключевому моменту, где я ошибался раньше. Certbot отлично настроил SSL, но он понятия не имеет, что мы хотим перенаправлять трафик на Vaultwarden. Он просто взял наш "фундамент" и прикрутил к нему SSL.

Поэтому сейчас мы выступим в роли инженера, который доводит работу автоматики до ума.

Шаг 1: Открываем то, что нам сгенерировал Certbot

sudo nano /etc/nginx/sites-available/vault.your-domain.com

Шаг 2: Получаем сертификат (безопасным способом)

Теперь запускаем Certbot в режиме certonly. Он не будет трогать наши конфиги.

  1. Установите Certbot (если еще не установлен):

    sudo apt update && sudo apt install certbot -y
  2. Запустите получение сертификата, указав папку для проверки:

    sudo certbot certonly --webroot -w /var/www/html -d vault.your-domain.com

    Certbot задаст пару вопросов (email, согласие). Ответьте на них. Если все пройдет успешно, он сообщит, что сертификаты сохранены.

Шаг 3: Возводим финальную Крепость (Nuke & Pave)

Вот он, ключевой шаг. Мы не будем ничего редактировать. Мы полностью заменим наш временный конфиг на финальный, рабочий.

  1. Откройте ваш файл конфигурации еще раз. Сотрите в нём всё до последней строчки.

    sudo nano /etc/nginx/sites-available/vault.your-domain.com
  2. Теперь вставьте туда этот полный, финальный, единственно правильный конфиг.

    ### НАЧАЛО БЛОКА ДЛЯ HTTPS (ПОРТ 443) ###
    server {
        listen 443 ssl http2;
        server_name vault.your-domain.com; # <-- ЗАМЕНИ НА СВОЙ ДОМЕН
    
        ### ПУТИ К СЕРТИФИКАТАМ, КОТОРЫЕ МЫ ТОЛЬКО ЧТО ПОЛУЧИЛИ ###
        ssl_certificate /etc/letsencrypt/live/vault.your-domain.com/fullchain.pem; # <-- ЗАМЕНИ
        ssl_certificate_key /etc/letsencrypt/live/vault.your-domain.com/privkey.pem; # <-- ЗАМЕНИ
        include /etc/letsencrypt/options-ssl-nginx.conf; # Эти файлы создает Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;  # Этот тоже
    
        ### НАШ "МОСТ" К VAULTWARDEN ###
        location / {
            # ВНИМАНИЕ: порт 8833 - тот самый, что мы указали в docker-compose!
            proxy_pass http://127.0.0.1:8833;
            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;
        }
    }
    ### КОНЕЦ БЛОКА ДЛЯ HTTPS ###
    
    
    ### НАЧАЛО БЛОКА ДЛЯ HTTP (ПОРТ 80) - ОН НУЖЕН ТОЛЬКО ДЛЯ РЕДИРЕКТА ###
    server {
        listen 80;
        server_name vault.your-domain.com; # <-- ЗАМЕНИ НА СВОЙ ДОМЕН
        # Просто перенаправляем весь трафик на защищенную версию
        return 301 https://$host$request_uri;
    }
    ### КОНЕЦ БЛОКА ДЛЯ HTTP ###

Шаг 4: Последняя проверка

Сохраните файл и выполните финальную команду:

sudo nginx -t && sudo systemctl reload nginx

Почему этот способ лучше? Потому что он предсказуемый. Ты не редактируешь непонятно что, а полностью заменяешь временный файл на финальный, заведомо рабочий конфиг. Никаких случайных скобок и потерянных директив.

Закрываем ворота: настройка через админ-панель

Теперь, когда у нас есть правильный конфиг, последовательность действий проста и безопасна.

  1. Запускаем контейнер: docker-compose up

  2. Регистрируем свой аккаунт на https://vault.your-domain.com.

  3. Сразу же закрываем регистрацию для всех остальных!

    • Открываем админ-панель: https://vault.your-domain.com/admin.

    • Вводим наш ADMIN_TOKEN.

    • Переходим во вкладку "General settings" и снимаем галочку "Allow new signups".

    • Нажимаем "Save".

Обязательно снимаем галочку "Allow new signups"
Обязательно снимаем галочку "Allow new signups"

Правило честного компромисса: теперь с осознанными обновлениями

Self-host — это круто, но это и ответственность.

  • Вы сами отвечаете за бэкапы. Потеряете данные с диска — потеряете пароли. Сделайте бэкап, или однажды будете горько плакать. Самый простой способ — остановить контейнер и заархивировать всю вашу папку ~/vaultwarden.

  • Вы сами отвечаете за обновления. Лайфхак: Раз в месяц-два заходите на страницу релизов Vaultwarden на GitHub, читайте описание новых версий (changelog) и, если всё в порядке, меняйте номер версии в docker-compose.yml и выполняйте docker-compose up -d --force-recreate. Это и есть осознанное обновление.

  • Куда бежать, если что-то сломалось? Помните: проблемы с клиентами (Android, iOS, браузер) — это к Bitwarden. Проблемы с сервером — к Vaultwarden.

Я прошел этот путь и наступил на самые очевидные мины, чтобы вы не повторили моих ошибок. Теперь вы можете построить не просто работающую, а по-настоящему надежную и безопасную крепость для своих паролей. Это дарит не только безопасность, но и приятное чувство уверенности в своих силах.

А теперь вопрос к вам, сообщество: как вы подходите к обновлениям своих self-hosted сервисов? Пинните версии, как параноики (вроде меня), используете latest и надеетесь на лучшее, или у вас есть автоматизированные системы для тестирования и выкатки обновлений? Делитесь своими стратегиями в комментариях.

Комментарии (10)


  1. c46fd3da
    15.07.2025 08:26

    Выставлять свой менеджер паролей голой задницей в интернет не самое мудрое решение.

    И если уж выставлять, то с использованием абстрактного субдомена, который не угадать, и через wildcard сертификат, чтобы на crt.sh не засветился он.

    Все остальное IMHO это игра с огнем.


    1. natane
      15.07.2025 08:26

      >через wildcard сертификат, чтобы на crt.sh не засветился он.

      Можете расшифровать для людей, далёких от администрирования?


      1. c46fd3da
        15.07.2025 08:26

        На ресурсе https://crt.sh доступны списки выданных сертификатов.

        Чтобы субдомен там не светить надо пользоваться wildcard сертификатами и получать их через acme dns например.


  1. c46fd3da
    15.07.2025 08:26

    del


  1. nv13
    15.07.2025 08:26

    А потом vps ка по какой то из причин станет недоступной и всё.


    1. c46fd3da
      15.07.2025 08:26

      Из клиента в пару кликов можно экспортировать содержимое.

      Главное не забывать это делать. =)

      На самом деле Vaultwarden годная вещь, сам много лет пользуюсь.

      Но только во внутренней сети.


      1. nv13
        15.07.2025 08:26

        Во внутренней - ключевое слово.


    1. andreymal
      15.07.2025 08:26

      Клиенты хранят локальную копию базы, если сервер отвалится - неприятно, но не катастрофа, доступ к паролям не потеряется


    1. Nill-Ringil
      15.07.2025 08:26

      У меня Vaultwarden работал в двух копиях

      1. На VPS

      2. У меня дома на RPi

      Между ними синхронизация syncthing, домашняя копия доступна через cloudflared, все красиво

      И по какой-то причине(ордер на обыск и изъятие в рамках ОРМ по «гос.измена») стал недоступен именно домашний инстанс, а инстанс на VPS прекрасно все пережил, дождался когда я смог бежать с россии и радостно приветствовал меня в новой стране

      Так что практика показала, что менее живучая та копия, которая у тебя дома на твоем железе, а копия на VPS если все оплачено спокойно переживает что угодно


  1. m1skam
    15.07.2025 08:26

    Главный лайфхак: Мы не будем использовать стандартный порт 8080. Давайте сразу выберем менее популярный, например 8833. Это как поселиться не на центральной площади, а в тихом переулке по соседству — спокойнее и надежнее.

    Я искренне не понимаю этот "лайфхак" и почему он главный. Особенно в контексте обратного прокси на nginx.