Files
homelab-docs/docs/vaultwarden-secrets.md
Andrey 16c254510a Update documentation to centralize Vaultwarden integration details and enhance backup scripts
Refactor README, architecture, and backup documentation to emphasize the use of Vaultwarden for credential management across various services. Update scripts for Nextcloud, Gitea, Paperless, and others to reference Vaultwarden for sensitive information. Remove outdated references to previous backup strategies and ensure clarity on credential retrieval processes. This improves security practices and streamlines backup operations.
2026-02-28 00:52:56 +03:00

288 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Vaultwarden и использование секретов
Краткое руководство по **Vaultwarden** в homelab и по тому, как получать секреты из него в скриптах и при восстановлении.
---
## Что такое Vaultwarden
**Vaultwarden** — это self-hosted реализация API Bitwarden: менеджер паролей, совместимый с официальными клиентами Bitwarden (десктоп, мобильные приложения, браузерные расширения). Данные хранятся на вашем сервере, а не в облаке Bitwarden.
В нашей схеме Vaultwarden развёрнут на **контейнере 103** (Gitea). Доступ:
- **Веб:** https://vault.katykhin.ru (из LAN и по VPN; из интернета без VPN закрыт).
- **По IP в LAN:** http://192.168.1.103:8280.
Подробнее про установку, порты и NPM — в [Контейнер 103 (Gitea, Vaultwarden)](containers/container-103.md#5-vaultwarden-менеджер-паролей).
**Зачем хранить секреты в Vaultwarden:**
- Один источник правды для паролей хоста, БД, API-ключей и т.д.
- При восстановлении после сбоя не нужно искать креды по разным файлам.
- Скрипты бэкапов и уведомлений могут брать секреты через Bitwarden CLI без хранения паролей в репозитории.
---
## Доступ к секретам: веб и CLI
- **Веб-интерфейс** — для ручного просмотра и редактирования записей (логины, пароли, кастомные поля). Вход по email и мастер-паролю.
- **Bitwarden CLI (`bw`)** — для скриптов и командной строки: разблокировка хранилища, получение логина/пароля/полей по имени записи.
Далее в статье речь идёт в основном о **CLI**.
---
## Установка и настройка Bitwarden CLI (bw)
На машине, с которой нужно получать секреты (например, хост Proxmox), должны быть установлены **`bw`** и **`jq`**.
### Установка bw (Linux, вручную)
1. Скачать архив с [релизов Bitwarden CLI](https://github.com/bitwarden/cli/releases) (например `bw-linux-1.22.1.zip` для x86_64).
2. Распаковать и положить бинарник в PATH, например:
```bash
unzip bw-linux-*.zip
install -m 755 bw /usr/local/bin/bw
```
3. Установить `jq` (для разбора кастомных полей):
```bash
apt install jq # Debian/Proxmox
```
### Настройка сервера и первый вход
1. Указать URL вашего Vaultwarden:
```bash
bw config server https://vault.katykhin.ru
```
2. Войти (интерактивно, один раз):
```bash
bw login
```
Ввести email и мастер-пароль от vault.katykhin.ru. Данные сессии сохранятся локально.
3. Проверить:
```bash
bw status
bw sync
```
После ввода мастер-пароля (если хранилище было locked) синхронизация подтянет актуальные данные с сервера.
### Разблокировка для скриптов (файл с мастер-паролем)
В cron или в скриптах пароль вводить вручную нельзя. Используют **файл с мастер-паролем** с строгими правами:
```bash
echo -n 'ВАШ_МАСТЕРАРОЛЬ' > /root/.bw-master
chmod 600 /root/.bw-master
```
- Файл **не коммитить** в репозиторий и не копировать в открытые места.
- Владелец — пользователь, под которым запускаются скрипты (например `root`); только он должен иметь доступ к файлу.
Проверка разблокировки без интерактивного ввода:
```bash
bw unlock "$(cat /root/.bw-master)" --raw
```
Команда должна вернуть длинную строку (session key). Эту строку скрипты передают в `BW_SESSION` (см. ниже).
---
## Как получать секреты из Vaultwarden
### Состояние и синхронизация
- **`bw status`** — показать URL сервера, последнюю синхронизацию, email пользователя и состояние: `unlocked` / `locked`.
- **`bw sync`** — обновить локальный кэш с сервера (при необходимости перед чтением актуальных данных).
Если хранилище **locked**, перед любыми `bw get ...` нужно разблокировать:
```bash
export BW_SESSION=$(bw unlock --passwordfile /root/.bw-master --raw)
```
Дальше в этой же сессии (пока переменная `BW_SESSION` экспортирована) можно вызывать `bw get ...`.
### Логин и пароль записи
Для записей типа «логин» (Login) в Vaultwarden:
- **Логин (username):**
`bw get username "ИМЯ_ЗАПИСИ"`
- **Пароль:**
`bw get password "ИМЯ_ЗАПИСИ"`
Примеры: `bw get password "GITEA"`, `bw get username "PAPERLESS"`. Имя записи — то, как она называется в веб-интерфейсе (чувствительно к регистру).
### Кастомные поля (custom fields)
В Bitwarden/Vaultwarden у записи могут быть **кастомные поля** (например `RESTIC_REPOSITORY`, `TELEGRAM_SELF_CHAT_ID`). Они не выводятся через `bw get username/password`, их достают через **`bw get item`** и **`jq`**:
```bash
bw get item "ИМЯ_ЗАПИСИ" | jq -r '.fields[] | select(.name=="ИМЯ_ПОЛЯ") | .value'
```
Примеры:
- Поле `RESTIC_BACKUP_KEY` из записи **RESTIC:**
`bw get item "RESTIC" | jq -r '.fields[] | select(.name=="RESTIC_BACKUP_KEY") | .value'`
- Поле `TELEGRAM_SELF_CHAT_ID` из **RESTIC:**
`bw get item "RESTIC" | jq -r '.fields[] | select(.name=="TELEGRAM_SELF_CHAT_ID") | .value'`
Полный JSON записи (все поля):
`bw get item "ИМЯ_ЗАПИСИ" | jq '.'`
---
## Использование в скриптах
### Общий подход
1. В начале скрипта (если ещё не разблокировано) прочитать мастер-пароль из файла и разблокировать:
```bash
BW_MASTER_PASSWORD_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}"
if [ -f "$BW_MASTER_PASSWORD_FILE" ]; then
export BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_PASSWORD_FILE" --raw)
fi
```
2. При необходимости выполнить `bw sync`.
3. Получить нужные значения через `bw get username`, `bw get password`, `bw get item ... | jq ...` и присвоить переменным окружения или использовать в командах.
Переменная **`BW_SESSION`** передаётся в дочерние процессы, поэтому все вызовы `bw` в том же процессе и в дочерних скриптах будут видеть разблокированное хранилище.
### Пример: Restic (репозиторий и ключ из Vaultwarden)
```bash
# Разблокировать (мастер-пароль из файла с chmod 600)
BW_MASTER_PASSWORD_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}"
if [ -f "$BW_MASTER_PASSWORD_FILE" ]; then
export BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_PASSWORD_FILE" --raw)
fi
ITEM=$(bw get item "RESTIC")
export RESTIC_REPOSITORY=$(echo "$ITEM" | jq -r '.fields[] | select(.name=="RESTIC_REPOSITORY") | .value')
export AWS_ACCESS_KEY_ID=$(echo "$ITEM" | jq -r '.fields[] | select(.name=="AWS_ACCESS_KEY_ID") | .value')
export AWS_SECRET_ACCESS_KEY=$(echo "$ITEM" | jq -r '.fields[] | select(.name=="AWS_SECRET_ACCESS_KEY") | .value')
export AWS_DEFAULT_REGION=$(echo "$ITEM" | jq -r '.fields[] | select(.name=="AWS_DEFAULT_REGION") | .value')
RESTIC_PASSWORD=$(echo "$ITEM" | jq -r '.fields[] | select(.name=="RESTIC_BACKUP_KEY") | .value')
export RESTIC_PASSWORD_FILE=$(mktemp -u)
echo -n "$RESTIC_PASSWORD" > "$RESTIC_PASSWORD_FILE"
chmod 600 "$RESTIC_PASSWORD_FILE"
trap 'rm -f "$RESTIC_PASSWORD_FILE"' EXIT
# Дальше: restic backup ... и т.д.
```
Пароль restic в файле — временный; `trap` удаляет его по выходу из скрипта.
### Пример: Telegram (токен и chat_id из Vaultwarden)
Токен бота хранится в записи **HOME_BOT_TOKEN** (пароль = токен); chat_id — в записи **RESTIC**, поле `TELEGRAM_SELF_CHAT_ID`:
```bash
BW_MASTER_PASSWORD_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}"
if [ -f "$BW_MASTER_PASSWORD_FILE" ]; then
export BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_PASSWORD_FILE" --raw)
fi
TELEGRAM_BOT_TOKEN=$(bw get password "HOME_BOT_TOKEN")
TELEGRAM_CHAT_ID=$(bw get item "RESTIC" | jq -r '.fields[] | select(.name=="TELEGRAM_SELF_CHAT_ID") | .value')
# Дальше: curl к Telegram API с TELEGRAM_BOT_TOKEN и TELEGRAM_CHAT_ID
```
### Пример: Beget (certbot DNS-01, CT 100)
Скрипт `deploy-beget-credentials.sh` на Proxmox генерирует `beget.ini` из объекта **beget** (username → `dns_beget_api_username`, password → `dns_beget_api_password`), атомарно пушит в CT 100 (`beget.ini.tmp` → `mv` → `beget.ini`), ставит chmod 600. Pre-hook certbot проверяет наличие файла и права перед каждым renew. **Ротация:** сменил пароль в Vaultwarden → `deploy-beget-credentials.sh` → готово.
### Пример: Invidious (CT 107)
Скрипт `deploy-invidious-credentials.sh` генерирует `.env` из объекта **INVIDIOUS** (username, password, поля `SERVER_SECRET_KEY`, `HMAC_KEY`), атомарно пушит в CT 107, запускает `docker compose up -d --force-recreate`. **Ротация:** сменил пароль/ключи в Vaultwarden → запустил скрипт.
### Пример: Paperless (CT 104)
Скрипт `deploy-paperless-credentials.sh` генерирует `docker-compose.env` из объекта **PAPERLESS** (password = POSTGRES_PASSWORD; поля `PAPERLESS_URL`, `PAPERLESS_SECRET_KEY`, `PAPERLESS_TIME_ZONE`, `PAPERLESS_OCR_LANGUAGE`, `PAPERLESS_OCR_LANGUAGES`), пушит compose и env в CT 104, запускает `docker compose up -d --force-recreate`. **Ротация:** сменил пароль/ключи в Vaultwarden → запустил скрипт.
### Пример: RAG-service (CT 105)
Скрипт `deploy-rag-credentials.sh` генерирует `.env` из объекта **RAG_SERVICE** (поле `RAG_API_KEY`), атомарно пушит в CT 105, запускает `docker compose up -d --force-recreate`. **Перед первым запуском:** создать в Vaultwarden запись **RAG_SERVICE** (тип Login), добавить кастомное поле `RAG_API_KEY` (hidden) с текущим ключом из `/home/rag-service/.env`. **Ротация:** сменил ключ в Vaultwarden → запустил скрипт.
### Пример: Gitea (CT 103)
Скрипт `deploy-gitea-credentials.sh` генерирует `.env` из объекта **GITEA** (password = POSTGRES_PASSWORD; поле `GITEA_RUNNER_REGISTRATION_TOKEN`), пушит compose и env в CT 103, запускает `docker compose up -d --force-recreate`. **Ротация:** сменил пароль/токен в Vaultwarden → запустил скрипт.
### Пример: Nextcloud (CT 101)
Скрипт `deploy-nextcloud-credentials.sh` генерирует `.env` и `docker-compose.yml` из объекта **NEXTCLOUD** (password = POSTGRES_PASSWORD; поля `dbpassword`, `secret`, `passwordsalt`, `instanceid`), пушит в CT 101, обновляет config.php через occ, запускает compose. **Ротация:** сменил в Vaultwarden → запустил скрипт.
### Пример: Galene (CT 108)
Скрипт `deploy-galene-credentials.sh` берёт поле `config` (JSON ice-servers) из объекта **GALENE**, записывает в `/opt/galene-data/data/ice-servers.json`, перезапускает `galene.service`. **Ротация:** сменил TURN username/credential в Vaultwarden → запустил скрипт.
### Пример: Immich (VM 200)
Скрипт `deploy-immich-credentials.sh` генерирует `.env` для Immich и immich-deduper из объектов **IMMICH** и **IMMICH_DEDUPER**, пушит по SSH на VM 200, запускает compose. **Требования:** SSH без пароля root@Proxmox → admin@192.168.1.200. **Ротация:** сменил в Vaultwarden → запустил скрипт.
### Пример: WireGuard (CT 109)
Скрипт `deploy-wireguard-credentials.sh` берёт поле `wg0_conf` (полный конфиг) из объекта **LOCAL_VPN_SERVER_WG**, записывает в `/etc/wireguard/wg0.conf`, перезапускает `wg-quick@wg0`. **Перед первым запуском:** создать в Vaultwarden запись **LOCAL_VPN_SERVER_WG**, добавить кастомное поле `wg0_conf` (hidden) с содержимым текущего `/etc/wireguard/wg0.conf` (скопировать с CT 109). **Ротация:** сменил ключи в Vaultwarden → запустил скрипт.
### Пример: VPN Route Check (деплой с Proxmox в CT 100)
Скрипт `deploy-vpn-route-check.sh` на хосте Proxmox:
1. Разблокирует bw (или переиспользует сессию).
2. Получает из объекта **localhost**: `ROUTER_TELNET_HOST` (кастомное поле), `ROUTER_TELNET_USER` (username), `ROUTER_TELNET_PASSWORD` (password).
3. Генерирует `.env` во временный файл, атомарно (`mv .env.tmp .env`) пушит в CT 100.
4. Запускает `docker compose up -d` в каталоге vpn-route-check.
Режим проверки без записи: `deploy-vpn-route-check.sh --dry-run`. Подробнее: [Контейнер 100](containers/container-100.md#7-vpn-route-check).
### Fallback на старые конфиги
Если Vaultwarden недоступен или разблокировка не удалась, скрипты могут загружать креды из прежних файлов (например `/root/.telegram-notify.env`, `/root/.restic-yandex.env`). Так можно обеспечить работу бэкапов даже при временной недоступности vault.
---
## Безопасность
- **Файл с мастер-паролем:** только владелец (например root), права `chmod 600`. Не хранить в git и не копировать на общие ресурсы.
- **Переменная BW_SESSION:** не логировать и не выводить в скриптах; не передавать в ненадёжные процессы.
- **Временные файлы с паролями** (как RESTIC_PASSWORD_FILE выше): создавать с `chmod 600`, удалять по завершении (`trap ... EXIT`).
- **Бэкап данных Vaultwarden:** каталог `/opt/docker/vaultwarden/data` на CT 103 входит в план бэкапов (restic → Yandex), см. [backup-howto](backup/backup-howto.md). Без этого при потере сервера теряется и хранилище паролей.
---
## Инвентаризация записей и полей
В Vaultwarden удобно хранить записи с именами, совпадающими с сервисами: **RESTIC**, **GITEA**, **PAPERLESS**, **NEXTCLOUD**, **HOME_BOT_TOKEN**, **VAULTWARDEN**, **MIRAN_S3**, **RAG_SERVICE**, **ADGUARD**, **NPM_ADMIN** и т.д. У записей типа «логин» — логин/пароль; у записей с множеством значений — кастомные поля (например `RESTIC_REPOSITORY`, `AWS_ACCESS_KEY_ID`, `RAG_API_KEY`).
**ADGUARD** — веб-интерфейс AdGuard Home (https://adguard.katykhin.ru): username = логин администратора, password = пароль. Тип записи: Login.
**NPM_ADMIN** — админка Nginx Proxy Manager (http://192.168.1.100:81): username = email (используется как identity при входе), password = пароль. Тип записи: Login. Скрипты `npm-add-proxy.sh`, `npm-add-proxy-vault.sh` используют `NPM_EMAIL` и `NPM_PASSWORD` — брать из этого объекта.
### Команды bw по объектам (для скриптов бэкапов и деплоя)
| Объект | Логин / пароль | Кастомные поля |
|--------|----------------|----------------|
| **ADGUARD** | `bw get username "ADGUARD"`, `bw get password "ADGUARD"` | — |
| **beget** | `bw get username "beget"`, `bw get password "beget"` | — |
| **GALENE** | — | `bw get item "GALENE" \| jq -r '.fields[] \| select(.name=="config") \| .value'` |
| **GITEA** | `bw get username "GITEA"`, `bw get password "GITEA"` | GITEA_RUNNER_REGISTRATION_TOKEN и др. |
| **HOME_BOT_TOKEN** | — | пароль = токен: `bw get password "HOME_BOT_TOKEN"` |
| **localhost** | `bw get username "localhost"`, `bw get password "localhost"` | ROUTER_TELNET_HOST |
| **NEXTCLOUD** | `bw get username "NEXTCLOUD"`, `bw get password "NEXTCLOUD"` | dbpassword, secret, passwordsalt, instanceid |
| **NPM_ADMIN** | username = email, `bw get password "NPM_ADMIN"` | — |
| **PAPERLESS** | `bw get password "PAPERLESS"` (= POSTGRES_PASSWORD) | PAPERLESS_SECRET_KEY, PAPERLESS_URL и др. |
| **RESTIC** | — | RESTIC_BACKUP_KEY, RESTIC_REPOSITORY, AWS_*, TELEGRAM_SELF_CHAT_ID |
| **VAULTWARDEN** | — | пароль = ADMIN_TOKEN: `bw get password "VAULTWARDEN"` |
Универсальный шаблон для поля: `bw get item "ИМЯ" | jq -r '.fields[] | select(.name=="ПОЛЕ") | .value'`
---
## См. также
- [Контейнер 103 (Gitea, Vaultwarden)](containers/container-103.md) — развёртывание Vaultwarden, порты, домен, NPM.
- [backup-howto](backup/backup-howto.md) — общий план бэкапов и восстановления, в том числе данных Vaultwarden.