HashiCorp Vault — инструмент для централизованного хранения и управления секретами: паролями, API-ключами, сертификатами и токенами. Это руководство описывает, как развернуть Vault в Docker-контейнере и интегрировать его с Ринго, чтобы устранить хранение чувствительных данных в открытом виде.
После выполнения всех шагов секреты будут находиться только в защищённом хранилище Vault, а запуск контейнеров Ринго — осуществляться через скрипт, который запрашивает актуальные данные при каждом старте.
В типичных Docker-проектах секреты хранятся в файле .env на хосте в открытом виде. Это создаёт ряд рисков:
Vault решает эти проблемы: единое защищённое хранилище с политиками доступа, шифрованием данных при хранении и передаче, а также полным журналом операций.
После интеграции с Vault запуск контейнеров Ринго выглядит так:
run-with-vault.sh аутентифицируется в Vault через механизм AppRole..env.generated.docker compose с использованием этого файла.Таким образом, секреты присутствуют на диске несколько миллисекунд, а затем остаются только в памяти запущенных контейнеров. При каждой остановке или перезапуске скрипт выполняется заново и подтягивает актуальные данные из Vault.
AppRole — механизм Vault для аутентификации машин (скриптов, сервисов) без участия пользователя. Использует два компонента: статический
role_id(идентификатор роли) иsecret_id(динамический пароль с ограниченным сроком действия).
sudo для установки пакетов и создания системных файлов.jq и curl (установка описана в первом шаге).Выбор среды развёртывания. В этом руководстве Vault запускается в dev-режиме на том же сервере, что и Ринго. Такой подход подходит для тестовой среды. Для production рекомендуется нативная установка Vault и вынесение его на отдельный сервер.
Установите утилиты jq (для разбора JSON-ответов Vault) и curl (для HTTP-запросов к API Vault):
sudo apt update && sudo apt install jq curl -y
Создайте файл docker-compose.vault.yml в удобной директории:
services:
vault:
image: hashicorp/vault:1.15.6
container_name: vault
cap_add:
- IPC_LOCK
environment:
# Root-токен для первоначальной настройки (только dev-режим)
VAULT_DEV_ROOT_TOKEN_ID: root-token-for-testing
# Адрес, на котором Vault принимает подключения внутри контейнера
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
VAULT_ADDR: http://127.0.0.1:8200
ports:
- "8200:8200"
volumes:
- vault-data:/vault/data
# Dev-режим: автоматически распечатывает хранилище, TLS не требуется
command: server -dev
volumes:
vault-data:
В dev-режиме Vault хранит данные в памяти и автоматически распечатывает хранилище при старте. Это удобно для настройки, но не подходит для production: данные будут утеряны при перезапуске контейнера.
docker compose -f docker-compose.vault.yml up -d
Key-Value v2 (KV v2) — движок хранения секретов в Vault с поддержкой версионирования: при изменении секрета сохраняется история предыдущих значений.
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault secrets enable -path=secret kv-v2"
Если движок уже включён, будет отображаться ошибка — это ожидаемое поведение.
Error enabling: Error making API request.
URL: POST http://127.0.0.1:8200/v1/sys/mounts/secret
Code: 400. Errors:
* path is already in use at secret/
Перенесите переменные из файла .env в Vault. Замените значения в примере на реальные и добавьте все остальные переменные по аналогии:
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault kv put secret/mdm \
DB_PASSWORD='ваш_пароль' \
MICROMDM_SECRET='ваш_секрет'"
Все переменные из
.envдобавляйте в одной команде через пробел. Многострочный формат с\в конце строки — стандартный синтаксис bash для переноса длинных команд.
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault auth enable approle"
Политика mdm-policy даёт скрипту право только читать секреты по пути secret/data/mdm — без возможности записи или удаления:
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault policy write mdm-policy - <<EOF
path \"secret/data/mdm\" {
capabilities = [\"read\"]
}
EOF"
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault write auth/approle/role/mdm-role secret_id_ttl=720h token_ttl=1h token_policies=mdm-policy"
Параметры роли:
| Параметр | Значение | Описание |
|---|---|---|
secret_id_ttl |
720h |
Срок действия secret_id — 30 дней. По истечении нужно сгенерировать новый. |
token_ttl |
1h |
Срок действия токена, который получает скрипт при каждом запуске. |
token_policies |
mdm-policy |
Политика, определяющая права выданного токена. |
role_id — статический идентификатор роли. Сохраните вывод команды — он понадобится на следующем шаге:
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault read -field=role_id auth/approle/role/mdm-role/role-id"
secret_id — динамический пароль роли со сроком действия 30 дней. Сохраните вывод команды — он понадобится на следующем шаге:
docker exec -it vault sh -c "export VAULT_ADDR=http://127.0.0.1:8200 && export VAULT_TOKEN=root-token-for-testing && vault write -f -field=secret_id auth/approle/role/mdm-role/secret-id"
По истечении 30 дней нужно сгенерировать новый
secret_idэтой же командой и обновить файл/etc/ringo/.vault-approle.
Создайте защищённый файл /etc/ringo/.vault-approle, подставив полученные значения:
sudo mkdir -p /etc/ringo
sudo tee /etc/ringo/.vault-approle > /dev/null <<EOF
ROLE_ID=полученный_role_id
SECRET_ID=полученный_secret_id
EOF
Ограничьте доступ к файлу — он должен быть доступен только владельцу:
sudo chmod 600 /etc/ringo/.vault-approle
Создайте файл run-with-vault.sh в директории с docker-compose.yml Ринго:
#!/bin/bash
set -e
VAULT_ADDR="http://localhost:8200"
VAULT_SKIP_VERIFY="true"
ROLE_FILE="/etc/ringo/.vault-approle"
# Загрузка role_id и secret_id из защищённого файла
source "$ROLE_FILE"
# Аутентификация в Vault через AppRole — получение временного токена
AUTH_RESPONSE=$(curl -s --insecure -X POST -H "Content-Type: application/json" \
-d "{\"role_id\":\"$ROLE_ID\",\"secret_id\":\"$SECRET_ID\"}" \
"$VAULT_ADDR/v1/auth/approle/login")
VAULT_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.auth.client_token')
# Запрос секретов из Vault
RESPONSE=$(curl -s --insecure -H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/mdm")
# Генерация временного .env-файла с правами 600 (только владелец)
umask 077
echo "# Generated from Vault" > .env.generated
echo "$RESPONSE" | jq -r '.data.data | to_entries | .[] | "\(.key)=\(.value)"' >> .env.generated
# Запуск контейнеров Ринго с использованием временного .env
docker compose --env-file .env.generated up -d
# Немедленное удаление временного файла
rm -f .env.generated
Сделайте скрипт исполняемым:
chmod +x run-with-vault.sh
Остановите текущий стек Ринго и запустите его через Vault:
docker compose down
./run-with-vault.sh
docker compose pull && ./run-with-vault.sh
При каждом запуске скрипт получает актуальные секреты из Vault. Ручное обновление
.env-файла больше не требуется — переменные обновятся автоматически при следующем перезапуске.