Управление секретами в конфигурации агента
На этой странице:
- Как это работает
- Выбор способа
- Встроенные хранилища: secret_backend_type
- Несколько хранилищ: multi_secret_backends
- Собственная программа: secret_backend_command
- Обновление секретов без перезапуска
- Справочник параметров
- Проверка и диагностика
Как это работает
Вместо значения секрета в конфигурации указывается ссылка вида ENC[<идентификатор>]. При запуске агент собирает все такие ссылки, запрашивает значения у источника секретов и подставляет их в конфигурацию в памяти. В файлах на диске секреты не появляются.
# /etc/protoobp-agent/protoobp.yaml
api_key: ENC[protoobp_api_key]
# /etc/protoobp-agent/conf.d/postgres.d/conf.yaml
instances:
- host: localhost
port: 5432
username: protoobp
password: ENC[postgres_password]
Правила использования ссылок:
ENC[...]должна занимать всё значение параметра. Значение"pass-ENC[x]"или"ENC[x]/path"не распознаётся.- Ссылки работают в
protoobp.yaml, в файлах интеграцийconf.d/<интеграция>.d/conf.yamlи в шаблонах автообнаружения (лейблы контейнеров и аннотации подов). - Идентификатор внутри скобок передаётся источнику секретов без изменений; его формат зависит от выбранного источника и описан ниже.
Выбор способа
Источник секретов задаётся одним из трёх параметров. Если задано несколько, действует первый по порядку, остальные игнорируются с предупреждением в журнале агента.
| Приоритет | Параметр | Когда использовать |
|---|---|---|
| 1 | secret_backend_command | Нужен источник, которого нет среди встроенных, или уже есть собственный скрипт |
| 2 | secret_backend_type | Все секреты лежат в одном поддерживаемом хранилище |
| 3 | multi_secret_backends | Секреты распределены по нескольким хранилищам |
Встроенные хранилища: secret_backend_type
Для встроенных хранилищ писать программу не нужно: агент использует поставляемый в пакете исполняемый файл secret-generic-connector (Linux — /opt/protoobp-agent/embedded/bin/, Windows — каталог bin\agent установки агента). Тип хранилища задаётся в secret_backend_type, его параметры — в secret_backend_config.
Файл YAML или JSON
Секреты хранятся в одном файле в виде плоского словаря «идентификатор: значение».
# /etc/protoobp-agent/secrets.yaml — права 600, владелец pobp-agent
protoobp_api_key: "<ВАШ_API_KEY>"
postgres_password: "s3cr3t"
# protoobp.yaml
secret_backend_type: file.yaml # для JSON-файла — file.json
secret_backend_config:
file_path: /etc/protoobp-agent/secrets.yaml
api_key: ENC[protoobp_api_key]
Идентификатор в ENC[] — ключ верхнего уровня в файле. Вложенные структуры не поддерживаются. Размер файла ограничен параметром max_file_read_size в secret_backend_config (в байтах).
Каталог с файлами
Каждый секрет хранится в отдельном файле; идентификатор — имя файла относительно каталога. Подходит для секретов, смонтированных в контейнер как файлы.
secret_backend_type: file.text
secret_backend_config:
secrets_path: /etc/protoobp-agent/secrets
# значение берётся из /etc/protoobp-agent/secrets/postgres_password
password: ENC[postgres_password]
Ссылки за пределы secrets_path (например, ENC[../../etc/shadow]) отклоняются.
Kubernetes Secrets
Агент читает секреты через API Kubernetes. Идентификатор имеет вид <namespace>/<имя секрета>;<ключ>:
secret_backend_type: k8s.secrets
password: ENC[monitoring/postgres-credentials;password]
По умолчанию используются токен сервисного аккаунта пода и адрес API из окружения пода. Переопределить их можно параметрами token_path, ca_path и api_server в secret_backend_config. Сервисному аккаунту агента нужны права get на соответствующие объекты Secret (Role и RoleBinding в пространстве имён секрета).
HashiCorp Vault
Идентификатор задаётся в одном из двух форматов:
<путь к секрету>;<ключ>— например,ENC[secret/protoobp/postgres;password];vault://<путь>#<JSON-указатель>— для вложенных значений, напримерENC[vault://secret/protoobp/db#/postgres/password].
Версия хранилища KV (v1 или v2) определяется автоматически.
secret_backend_type: hashicorp.vault
secret_backend_config:
vault_address: https://vault.corp.local:8200
vault_namespace: monitoring # для Vault Enterprise, необязательно
vault_tls_config:
ca_cert: /etc/protoobp-agent/vault-ca.pem
vault_session:
vault_role_id: <ROLE_ID> # аутентификация AppRole
vault_secret_id: <SECRET_ID>
| Аутентификация | Параметры |
|---|---|
| Токен | vault_token в secret_backend_config |
| AppRole | vault_session.vault_role_id, vault_session.vault_secret_id |
| Логин и пароль (userpass) | vault_session.vault_username, vault_session.vault_password |
| LDAP | vault_session.vault_ldap_username, vault_session.vault_ldap_password |
| Kubernetes | vault_session.vault_auth_type: kubernetes, vault_session.vault_kubernetes_role; при необходимости vault_kubernetes_jwt_path и vault_kubernetes_mount_path |
Адрес Vault можно также передать стандартной переменной окружения VAULT_ADDR, она имеет приоритет над vault_address. Параметры TLS-клиента: ca_cert, ca_path, client_cert, client_key, tls_server, insecure в секции vault_tls_config.
Другие типы
secret_backend_type | Источник |
|---|---|
file.yaml, file.json | Один файл со словарём секретов |
file.text | Каталог, один файл на секрет |
k8s.secrets | Объекты Secret через API Kubernetes |
k8s.file | Секреты, смонтированные в под как файлы |
docker.secrets | Секреты Docker Swarm (/run/secrets) |
hashicorp.vault | HashiCorp Vault |
akeyless | Akeyless |
aws.secrets, aws.ssm | Облачные хранилища секретов AWS |
azure.keyvault | Azure Key Vault |
gcp.secretmanager | Google Secret Manager |
Несколько хранилищ: multi_secret_backends
Каждому хранилищу присваивается имя, которое указывается в ссылке перед точкой с запятой: ENC[<имя хранилища>;<идентификатор>].
multi_secret_backends:
local:
type: file.yaml
config:
file_path: /etc/protoobp-agent/secrets.yaml
vault:
type: hashicorp.vault
config:
vault_address: https://vault.corp.local:8200
vault_token: <VAULT_TOKEN>
api_key: ENC[local;protoobp_api_key]
password: ENC[vault;secret/protoobp/postgres;password]
Ссылка без имени хранилища (ENC[protoobp_api_key]) при использовании только multi_secret_backends отклоняется.
Собственная программа: secret_backend_command
Если нужного хранилища нет среди встроенных, укажите путь к собственному исполняемому файлу (скрипт или программа на любом языке):
secret_backend_command: /etc/protoobp-agent/fetch-secrets.py
secret_backend_arguments: # необязательно
- --profile
- monitoring
secret_backend_timeout: 30 # секунд
Протокол обмена
Агент запускает программу, передаёт ей на стандартный ввод JSON со списком нужных идентификаторов и ждёт ответ на стандартном выводе.
Запрос:
{"version": "1.1", "secrets": ["protoobp_api_key", "postgres_password"], "secret_backend_timeout": 30}
Ответ — объект, где для каждого идентификатора указано значение либо текст ошибки:
{
"protoobp_api_key": {"value": "<ВАШ_API_KEY>"},
"postgres_password": {"error": "секрет не найден"}
}
Требования к программе:
- проверять старшую часть версии протокола (
1) и завершаться с ошибкой при несовместимой версии; - уложиться в
secret_backend_timeoutсекунд; - выводить не больше
secret_backend_output_max_sizeбайт (по умолчанию 1 МиБ); - не выводить на стандартный вывод ничего, кроме JSON-ответа; диагностические сообщения писать в stderr.
Если секрет не получен, параметр сохраняет значение ENC[...], а агент пишет ошибку в журнал. Интеграция с таким параметром обычно не сможет подключиться к сервису.
Минимальный пример на Python, читающий секреты из каталога:
#!/usr/bin/env python3
# /etc/protoobp-agent/fetch-secrets.py
import json
import os
import sys
SECRETS_DIR = "/etc/protoobp-agent/secrets"
request = json.load(sys.stdin)
if not request.get("version", "").startswith("1."):
sys.exit("unsupported protocol version: %s" % request.get("version"))
response = {}
for handle in request["secrets"]:
path = os.path.join(SECRETS_DIR, handle)
if os.path.dirname(os.path.normpath(path)) != SECRETS_DIR:
response[handle] = {"error": "invalid handle"}
continue
try:
with open(path) as f:
response[handle] = {"value": f.read().rstrip("\n")}
except OSError as e:
response[handle] = {"error": str(e)}
json.dump(response, sys.stdout)
Для такой задачи проще использовать встроенный тип file.text; пример показывает формат обмена.
Требования к правам на файл
Агент отказывается запускать программу, если к ней есть доступ у посторонних пользователей:
Linux: файл принадлежит пользователю, от которого работает агент (
pobp-agent), и не имеет прав для группы и остальных:chown pobp-agent:pobp-agent /etc/protoobp-agent/fetch-secrets.py chmod 700 /etc/protoobp-agent/fetch-secrets.pyЕсли программу нужно разделить между несколькими пользователями через группу, включите
secret_backend_command_allow_group_exec_perm: true— тогда группе разрешено чтение и выполнение (chmod 750), но не запись.Windows: в списке доступа файла могут быть только учётная запись службы агента (по умолчанию
pobpagentuser), группаAdministratorsиLOCAL SYSTEM; явных запретов (Deny) для этих учётных записей быть не должно.
На встроенный secret-generic-connector эти требования не распространяются.
Готовый помощник для контейнеров
В образе агента есть скрипты для чтения секретов, смонтированных в контейнер:
| Скрипт | Поведение |
|---|---|
/readsecret.sh <каталог> | Идентификатор — имя файла в указанном каталоге |
/readsecret_multiple_providers.sh | Идентификатор с префиксом: file@/путь/к/файлу или k8s_secret@<namespace>/<секрет>/<ключ> |
В Helm-чарте агента они подключаются параметрами protoobp.secretBackend.command и protoobp.secretBackend.arguments:
protoobp:
secretBackend:
command: "/readsecret_multiple_providers.sh"
confd:
postgres.yaml: |-
instances:
- host: postgres.db.svc
username: protoobp
password: ENC[k8s_secret@db/postgres-credentials/password]
При /readsecret_multiple_providers.sh чарт по умолчанию выдаёт агенту права на чтение секретов во всём кластере (protoobp.secretBackend.enableGlobalPermissions: true). Чтобы ограничить доступ, отключите этот параметр и выдайте права только на нужные пространства имён, а также используйте secret_allowed_k8s_namespace или secret_scope_integration_to_their_k8s_namespace (см. справочник).
Обновление секретов без перезапуска
По умолчанию секреты запрашиваются один раз при запуске агента. Чтобы ключ API можно было сменить без перезапуска:
secret_refresh_interval: 3600 # секунд; повторно запрашивать ключ API каждый час
secret_refresh_scatter: true # случайный сдвиг первого обновления
secret_refresh_on_api_key_failure_interval: 5 # минут; внеочередное обновление при отказе бэкенда в ключе
secret_refresh_interval— периодическое обновление ключей API;0отключает.secret_refresh_scatter— первое обновление выполняется в случайный момент интервала, чтобы множество агентов не обращалось к хранилищу одновременно.secret_refresh_on_api_key_failure_interval— если бэкенд отвечает403или проверка ключа не проходит, агент запрашивает секреты заново, но не чаще одного раза в указанное число минут;0отключает.
Обновить секреты вручную: protoobp-agent secret refresh.
Справочник параметров
Все параметры задаются в protoobp.yaml или переменными окружения с префиксом POBP_ (например, POBP_SECRET_BACKEND_TYPE). Параметры-объекты (secret_backend_config, multi_secret_backends) передаются в переменной окружения как JSON.
| Параметр | По умолчанию | Назначение |
|---|---|---|
secret_backend_type | — | Тип встроенного хранилища |
secret_backend_config | {} | Параметры встроенного хранилища |
multi_secret_backends | {} | Именованные хранилища |
secret_backend_command | — | Путь к собственной программе |
secret_backend_arguments | [] | Аргументы программы |
secret_backend_timeout | 30 | Тайм-аут запроса к источнику, секунд |
secret_backend_output_max_size | 1048576 | Максимальный размер ответа программы, байт |
secret_backend_remove_trailing_line_break | false | Удалять завершающий перевод строки из значений |
secret_backend_skip_checks | false | Не разрешать ENC[] в конфигурациях интеграций |
secret_backend_command_allow_group_exec_perm | false | Разрешить группе чтение и выполнение программы (Linux) |
secret_refresh_interval | 0 | Период обновления ключей API, секунд |
secret_refresh_scatter | true | Случайный сдвиг первого обновления |
secret_refresh_on_api_key_failure_interval | 0 | Минимальный интервал внеочередного обновления при отказе в ключе, минут |
secret_allowed_k8s_namespace | [] | Пространства имён, из которых разрешено читать k8s_secret@ |
secret_scope_integration_to_their_k8s_namespace | false | Интеграции читают k8s_secret@ только из своего пространства имён |
secret_image_to_handle | {} | Явный список секретов k8s_secret@, доступных каждому образу |
Три последних параметра взаимоисключающие.
Проверка и диагностика
Команда protoobp-agent secret (в контейнере — agent secret) показывает:
- какой источник секретов используется и права на исполняемый файл;
- каждый идентификатор и места в конфигурации, где он используется.
Значения секретов команда не выводит. Сводка по секретам также есть в блоке Secrets вывода protoobp-agent status.
Частые ошибки:
| Сообщение в журнале | Причина |
|---|---|
invalid executable ... 'group' or 'others' have rights on it | Права на программу шире допустимых — см. требования к правам |
... returned invalid JSON | Программа выводит в stdout что-то кроме JSON-ответа |
... takes precedence over ... | Заданы одновременно несколько способов; действует первый по приоритету |
secrets path ... is not a directory | Для file.text в secrets_path указан не каталог |
invalid secret format, expected 'secret_path;key' ... | Для Vault идентификатор задан без ;<ключ> или #<указатель> |