Що саме ми побудуємо
Сценарій: є systemd-сервіс (наприклад, ваш застосунок), якому потрібен секрет (DATABASE_URL або API-ключ). Ми налаштуємо Vault Agent з автоаутентифікацією (AppRole), який:
- отримує секрет з Vault;
- рендерить файл середовища /etc/myapp/secret.env з потрібними змінними;
- автоматично перезапускає або перезавантажує сервіс при ротації секрету;
- дотримується мінімальних прав доступу Linux.
Передумови: у вас є доступний Vault (адреса VAULT_ADDR), налаштована політика для читання потрібного шляху та створений AppRole. На хості — systemd v245+ і root-доступ у термінал Linux.
Крок 1. Встановлюємо інструменти
Поставимо клієнт Vault і створимо каталоги для конфігів та секретів. Приклади для Debian/Ubuntu (apt). На інших дистрибутивах використайте відповідні менеджери пакетів.
# 1) Встановлення Vault CLI (офіційний репозиторій або пакет з дистрибутива)
sudo apt update
sudo apt install -y vault
# 2) Каталоги для агента і секретів
sudo install -d -m 0750 /etc/vault
sudo install -d -m 0750 /etc/myapp
Крок 2. Створюємо політику та AppRole у Vault
Увійдіть у Vault як адміністратор і дозвольте читання конкретного шляху. Для прикладу секрет лежить у kv v2 за адресою secret/data/myapp.
# Авторизація (приклад)
export VAULT_ADDR='https://vault.example.com'
vault login
# Політика для читання секрету
echo 'path "secret/data/myapp" {
capabilities = ["read"]
}' | vault policy write myapp-read -
# Створюємо AppRole з прив'язкою політики
vault auth enable approle || true
vault write auth/approle/role/myapp \
token_policies='myapp-read' \
token_ttl='1h' token_max_ttl='24h'
# Отримуємо role_id та secret_id
vault read auth/approle/role/myapp/role-id
vault write -f auth/approle/role/myapp/secret-id
Збережіть role_id та secret_id у безпечному місці. В ідеалі — передавайте secret_id на хост через одноразовий канал або автоматизацію.
Крок 3. Налаштовуємо Vault Agent з шаблоном
Створимо конфіг агента, який:
- автоаутентифікується по AppRole;
- рендерить файл середовища з секретом;
- перезавантажує ваш сервіс при оновленні секрету.
# Конфіг агента /etc/vault/myapp-agent.hcl
cat | sudo tee /etc/vault/myapp-agent.hcl >/dev/null <<'HCL'
exit_after_auth = false
pid_file = "/run/vault-agent-myapp.pid"
auto_auth {
method "approle" {
mount_path = "auth/approle"
config = {
role_id_file_path = "/etc/vault/myapp-role-id"
secret_id_file_path = "/etc/vault/myapp-secret-id"
}
}
sink "file" {
config = {
path = "/run/vault/myapp-token"
}
}
}
template {
source = "/etc/vault/myapp-secret.tpl"
destination = "/etc/myapp/secret.env"
perms = 0600
command = "systemctl reload myapp.service || systemctl restart myapp.service"
}
vault {
address = "https://vault.example.com"
}
HCL
# Файли з обліковими даними AppRole (тільки root читає!)
echo 'paste_your_role_id_here' | sudo tee /etc/vault/myapp-role-id >/dev/null
echo 'paste_your_secret_id_here' | sudo tee /etc/vault/myapp-secret-id >/dev/null
sudo chmod 600 /etc/vault/myapp-role-id /etc/vault/myapp-secret-id
# Шаблон, який перетворює секрет на змінні середовища
cat | sudo tee /etc/vault/myapp-secret.tpl >/dev/null <<'TPL'
# tpl читає поле database_url зі сховища kv v2
DATABASE_URL='{{ with secret "secret/data/myapp" }}{{ .Data.data.database_url }}{{ end }}'
TPL
sudo chmod 600 /etc/vault/myapp-secret.tpl
У шаблоні можна описати й інші ключі (наприклад, API_TOKEN), просто додаючи нові рядки.
Крок 4. systemd-сервіс агента та підключення до вашого застосунку
Створимо unit для Vault Agent і підкажемо вашому застосунку читати EnvironmentFile.
# Unit для агента: /etc/systemd/system/vault-agent-myapp.service
cat | sudo tee /etc/systemd/system/vault-agent-myapp.service >/dev/null <<'UNIT'
[Unit]
Description=Vault Agent for myapp
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/vault agent -config /etc/vault/myapp-agent.hcl
Restart=on-failure
User=root
Group=root
RuntimeDirectory=vault
RuntimeDirectoryMode=0750
[Install]
WantedBy=multi-user.target
UNIT
# Drop-in для вашого сервісу, щоб він читав секрети з файлу
# Приклад: myapp.service вже існує. Додаємо 10-env.conf
sudo systemctl edit myapp.service
# У відкритому редакторі вставте:
# [Service]
# EnvironmentFile=/etc/myapp/secret.env
# Запускаємо агента і перезапускаємо застосунок
sudo systemctl daemon-reload
sudo systemctl enable --now vault-agent-myapp.service
sudo systemctl restart myapp.service
Тепер при кожній ротації секрету Vault Agent оновить файл /etc/myapp/secret.env і спробує виконати systemctl reload myapp.service, а якщо перезавантаження не підтримується — зробить restart.
Крок 5. Перевіряємо ротацію
# Оновимо секрет у Vault (kv v2)
vault kv put secret/myapp database_url='postgres://new_user:pwd@db:5432/app'
# Подивимось журнал агента
journalctl -u vault-agent-myapp.service -f
# Переконайтесь, що застосунок підхопив нове значення
sudo systemctl show myapp.service | grep EnvironmentFile -n || true
Якщо у застосунка є ендпойнт status або healthcheck, перевірте, що він працює без даунтайму 🚀
Альтернативні способи
1) systemd Path Unit замість команди в шаблоні
Замість виклику systemctl з агента, можна створити watcher, який реагує на зміну файлу секретів.
# /etc/systemd/system/myapp-env.path
[Unit]
Description=Watch myapp secret env
[Path]
PathChanged=/etc/myapp/secret.env
Unit=myapp-env.service
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/myapp-env.service
[Unit]
Description=Reload myapp on secret change
[Service]
Type=oneshot
ExecStart=/bin/systemctl reload myapp.service || /bin/systemctl restart myapp.service
# Активувати
sudo systemctl daemon-reload
sudo systemctl enable --now myapp-env.path
2) LoadCredential у systemd
Нові версії systemd підтримують LoadCredential, коли секрети підвантажуються як тимчасові файли. Можна писати з Vault Agent у захищене місце та підключати через LoadCredential=secret.env:/etc/myapp/secret.env. Застосунок тоді читає файл прямо з $CREDENTIALS_DIRECTORY.
3) Оновлення за розкладом через cron та systemd timers
Якщо вам не підходить постійний агент, зробіть oneshot-сервіс і таймер, який періодично тягне секрети vault CLI або bash скрипти й оновлює файл, після чого виконує перезавантаження сервісу. Це менш реактивно, але просто й надійно для деяких кейсів.
GUI-спосіб (для перевірки вмісту)
У веб-інтерфейсі HashiCorp Vault відкрийте ваш kv-секрет (наприклад, secret/myapp), оновіть ключі та збережіть. Далі перевірте журнали vault-agent-myapp.service: ви побачите оновлення шаблону та подальший reload/restart системного сервісу. Зручно для швидкої валідації, коли немає під рукою CLI у термінал Linux.
FAQ
Vault Agent не може аутентифікуватися. Що робити?
Перевірте VAULT_ADDR, доступ до мережі, коректність role_id/secret_id і політику. Переконайтеся, що час на сервері синхронізований (NTP), інакше TTL токенів може поводитись дивно.
Помилка доступу при записі /etc/myapp/secret.env.
Звірте власника/права: файл і каталог повинні бути доступні root і сервісу для читання (перевірте User= у myapp.service). Рекомендовано 0600 для файлу секрету і 0750 для каталогу.
Мій застосунок не підтримує reload. Як бути?
Використовуйте перезапуск: або залиште команду шаблону з fallback на restart, або налаштуйте Path Unit, що виконає restart.
Динамічні секрети (наприклад, БД) швидко протерміновуються.
Переконайтеся, що Vault Agent встигає оновити файл до закінчення lease, а застосунок підтримує прозорий reconnection. За потреби збільшіть TTL у ролі/бекенді або зменшіть кешування конектора.
Чи безпечно зберігати секрет у EnvironmentFile?
Це краще, ніж тримати в unit-файлі, але пам'ятайте: збережений файл існує на диску. Обмежте права доступу, ізолюйте сервіс (ProtectSystem, ProtectHome, PrivateTmp), а для підвищеної безпеки розгляньте LoadCredential.
systemctl reload з агента не спрацьовує.
Перевірте sudoers (якщо агент не від root), наявність ExecReload у вашому сервісі та журнали systemd. Альтернатива — Path Unit.
Як логувати події ротації?
journalctl -u vault-agent-myapp.service, а також додайте логування у ваш застосунок на сигнал перезавантаження.
Чи підходить це для сервер Linux із кількома сервісами?
Так. Рекомедовано робити окремі роль/політику/агента/шаблон на кожен сервіс з принципом найменших привілеїв.
Порада від Kernelka
Тримайте один секрет — один файл. Так простіше керувати правами й перезавантаженнями. І не забудьте задокументувати, який саме сигнал обробляє ваш сервіс для м'якого reload — це економить нерви й секунди простою 😊
Підсумок
- Встановили Vault CLI та підготували каталоги з правильними правами.
- Створили політику та AppRole для мінімально необхідного доступу.
- Налаштували Vault Agent із шаблоном, що пише EnvironmentFile.
- Підключили файл до systemd-сервісу та організували reload/restart.
- Перевірили ротацію через оновлення секрету у Vault.
- Знайшли альтернативи: Path Unit, LoadCredential, systemd timers у стилі cron та systemd timers.