Приложение, которое читает пароли из конфигурационного файла и не умеет обращаться к API Stronghold, можно подключить без изменения кода. Stronghold Agent аутентифицируется в Stronghold, рендерит конфигурационный файл по шаблону и перезагружает приложение при изменении секретов.

Цель

Настроить на виртуальной машине Stronghold Agent под управлением systemd, который входит по AppRole с одноразовым обёрнутым secret_id, формирует файл application.properties и выполняет systemctl reload myapp при изменении секретов.

Предварительные требования

Перед началом подготовьте:

  • Виртуальная машина Linux с systemd и приложением myapp, запущенным как сервис myapp.service.
  • Исполняемый файл Stronghold на ВМ (в примерах /usr/local/bin/stronghold) и сетевой доступ к Stronghold.
  • Токен Stronghold с правами на настройку AppRole, политик и KV.
  • Механизм секретов KV версии 2, включённый по пути secret.

Шаг 1. Сохраните секрет и создайте политику

Запишите учётные данные базы данных в KV и создайте политику, которая разрешает их чтение:

d8 stronghold kv put -mount=secret myapp/config db_user=app_user db_password='S3cure-P@ss'

d8 stronghold policy write myapp-vm - <<'POLICY'
path "secret/data/myapp/config" {
  capabilities = ["read"]
}
POLICY

Шаг 2. Создайте роль AppRole

Включите метод аутентификации AppRole и создайте роль для ВМ:

d8 stronghold auth enable approle

d8 stronghold write auth/approle/role/myapp-vm token_policies=myapp-vm token_ttl=1h token_max_ttl=24h secret_id_ttl=720h secret_id_bound_cidrs="10.0.10.15/32"

Параметр secret_id_bound_cidrs разрешает вход только с адреса ВМ. secret_id_ttl ограничивает срок, в течение которого Agent может повторно входить с одним secret_id.

Шаг 3. Доставьте role_id и обёрнутый secret_id на ВМ

Agent входит по role_id и одноразовому обёрнутому secret_id. Подготовьте оба файла и скопируйте их на ВМ.

  1. Получите role_id — он не является секретом и может распространяться системой управления конфигурацией:

    d8 stronghold read -field=role_id auth/approle/role/myapp-vm/role-id > role-id
  2. Выпустите secret_id в обёрнутом виде. Вместо самого secret_id возвращается одноразовый токен с TTL 10 минут:

    d8 stronghold write -wrap-ttl=10m -field=wrapping_token -f auth/approle/role/myapp-vm/secret-id > secret-id
  3. Скопируйте оба файла на ВМ в директорию /etc/stronghold-agent/ и ограничьте доступ. Владельцем директории должен быть пользователь stronghold-agent: Agent удаляет файл secret_id после чтения.

    sudo install -d -o stronghold-agent -g stronghold-agent -m 0750 /etc/stronghold-agent
    sudo install -o stronghold-agent -g stronghold-agent -m 0640 role-id /etc/stronghold-agent/role-id
    sudo install -o stronghold-agent -g stronghold-agent -m 0600 secret-id /etc/stronghold-agent/secret-id

Если токен обёртывания перехвачен и развёрнут кем-то другим, Agent не сможет его развернуть — это признак инцидента. Порядок проверки описан в разделе «Обёртывание ответа».

Шаг 4. Создайте шаблон

Создайте файл /etc/myapp/templates/application.properties.ctmpl:

{{ with secret "secret/data/myapp/config" }}
spring.datasource.username={{ .Data.data.db_user }}
spring.datasource.password={{ .Data.data.db_password }}
{{ end }}

Шаг 5. Настройте Agent

Создайте файл /etc/stronghold-agent/agent.hcl:

stronghold {
  address = "https://stronghold.example.com"
}

auto_auth {
  method "approle" {
    mount_path = "auth/approle"
    config = {
      role_id_file_path                   = "/etc/stronghold-agent/role-id"
      secret_id_file_path                 = "/etc/stronghold-agent/secret-id"
      secret_id_response_wrapping_path    = "auth/approle/role/myapp-vm/secret-id"
      remove_secret_id_file_after_reading = true
    }
  }

  sink "file" {
    config = {
      path = "/var/run/stronghold-agent/token"
      mode = 0600
    }
  }
}

template_config {
  static_secret_render_interval = "5m"
}

template {
  source               = "/etc/myapp/templates/application.properties.ctmpl"
  destination          = "/etc/myapp/application.properties"
  perms                = "0640"
  group                = "myapp"
  command              = "/usr/bin/systemctl reload myapp"
  command_timeout      = "30s"
  error_on_missing_key = true
}
  • secret_id_response_wrapping_path — Agent распознаёт обёрнутый secret_id, проверяет путь его создания и разворачивает его;
  • remove_secret_id_file_after_reading = true — файл с secret_id удаляется после чтения;
  • static_secret_render_interval — как часто Agent перечитывает статические секреты KV;
  • perms и group — владельцем файла становится пользователь stronghold-agent и группа myapp, поэтому приложение может его прочитать. Agent может назначить только группу, в которую входит пользователь stronghold-agent. Добавьте пользователя в группу командой sudo usermod -aG myapp stronghold-agent;
  • command — выполняется после каждой записи файла. Команда выполняется без sudo: unit Agent запускается с NoNewPrivileges=true, поэтому sudo не работает. Право на перезагрузку приложения выдаёт правило polkit из шага 6.

Развёрнутый secret_id хранится только в памяти Agent. После перезапуска Agent или ВМ, а также по истечении secret_id_ttl ему потребуется новый обёрнутый secret_id. Встройте выпуск и доставку secret_id в процесс развёртывания, например в CI/CD.

Шаг 6. Запустите Agent под systemd

Настройте unit Agent, разрешите Agent перезагружать приложение и запустите сервис.

  1. Создайте unit-файл /etc/systemd/system/stronghold-agent.service по примеру из раздела «Запуск и управление». В ReadWritePaths укажите директории /var/run/stronghold-agent и /etc/myapp.

  2. Разрешите Agent удалять файл secret_id. Unit использует ProtectSystem=strict, поэтому добавьте директорию /etc/stronghold-agent в ReadWritePaths в drop-in-файле. Выполните sudo systemctl edit stronghold-agent и добавьте строки:

    [Service]
    ReadWritePaths=/etc/stronghold-agent
  3. Разрешите пользователю stronghold-agent выполнять только перезагрузку myapp.service. Создайте файл /etc/polkit-1/rules.d/50-stronghold-agent-myapp.rules:

    polkit.addRule(function(action, subject) {
        if (action.id == "org.freedesktop.systemd1.manage-units" &&
            subject.user == "stronghold-agent" &&
            action.lookup("unit") == "myapp.service" &&
            action.lookup("verb") == "reload") {
            return polkit.Result.YES;
        }
    });

    polkit читает файлы правил автоматически. Параметры unit и verb передаёт systemd версии 226 и выше. Если приложение не поддерживает перезагрузку, замените в правиле "reload" на "restart", а в command — reload на restart.

  4. Запустите сервис:

    sudo systemctl daemon-reload
    sudo systemctl enable --now stronghold-agent

Проверка

Проверьте, что Agent вошёл в Stronghold, сформировал файл и реагирует на изменение секрета.

  1. Проверьте журнал Agent — должны быть сообщения об успешной аутентификации и рендеринге:

    sudo journalctl -u stronghold-agent -n 50
  2. Убедитесь, что файл секрета удалён, а конфигурация сформирована:

    ls -la /etc/stronghold-agent/secret-id /etc/myapp/application.properties
  3. Измените секрет и дождитесь перерисовки файла и перезагрузки приложения (не дольше static_secret_render_interval):

    d8 stronghold kv patch -mount=secret myapp/config db_password='N3w-P@ss'
    sudo journalctl -u myapp -f

Очистка

Чтобы удалить конфигурацию примера, выполните следующие действия:

  1. Остановите и отключите Agent: sudo systemctl disable --now stronghold-agent.

  2. Удалите файлы /etc/stronghold-agent/role-id, /etc/myapp/application.properties и файл токена в /var/run/stronghold-agent/.

  3. Удалите файл /etc/polkit-1/rules.d/50-stronghold-agent-myapp.rules и drop-in-файл Agent: sudo systemctl revert stronghold-agent.

  4. Удалите роль и политику:

    d8 stronghold delete auth/approle/role/myapp-vm
    d8 stronghold policy delete myapp-vm

Дополнительные ресурсы