Привет, Хабр! Я Руслан Гайфутдинов, ведущий пресейл-инженер в команде системы управления секретами StarVault в Orion soft. Сегодня я хотел бы рассказать о том, как обеспечить соответствие требованиям безопасной разработки и защитить учетные записи от компрометации, используя хранилище секретов.
В этой статье мы поговорим об общепринятых методах хранения секретов и подробно разберем процесс перевода пайплайна CI/CD из защищенного режима (когда все секреты хранятся в коде) в режим динамических паролей. Где это нужно? Например, в безопасной разработке.
Безопасная разработка
В компаниях, которые внедряют практику безопасной разработки, возникает потребность защитить доступ к различным системам еще на этапе создания продуктов.
Существует несколько паттернов работы с секретами:
Хранить секреты в коде. Это вполне общепринятые практики хардкодинга. Например, и Uber, и Toyota сталкивались с проблемами из-за хранения реквизитов доступа прямо в коде. У последних один из важных ключей 5 лет лежал в открытом репозитории. Они сами это обнаружили и исправили, но если бы нашел кто-то другой, компания бы пострадала, так как ключ из репозитория достаточно просто достать.
Хранить в контейнере. Есть мнение, что это безопасно. Но на самом деле очень легко скомпрометировать образ и достать секреты из контейнера.
Хранить по правилам. На самом деле практики РБПО подразумевают конкретные правила. Сегодня можно сертифицироваться по части безопасной разработки. Существует также ГОСТ №5639 от 2016 года, который обновили в 2024 году. В нем есть раздел 5.15, посвященный управлению секретами, StarVault полностью закрывает все требования раздела 5.15.
Во ФСТЭК при сертификации тоже рекомендуют использовать российские решения. Пока что именно рекомендуют, но неизвестно, когда рекомендация превратится в требование, поэтому компании с крупными инфраструктурами начинают смотреть на российские разработки уже сейчас.

Что сделаем
В следующем разделе детально рассматриваем процесс интеграции StarVault с GitLab с использованием JWT-аутентификации. Данный подход гарантирует высокий уровень защиты конфиденциальных данных. В рамках GitLab Pipeline реализована модель работы с динамическими секретами для безопасного подключения к PostgreSQL. Это означает, что каждый запуск пайплайна автоматически генерирует уникальные, ограниченные по времени учетные данные, что полностью исключает риски, связанные с компрометацией статических паролей.
Механизм работы следующий: при каждом запуске пайплайна создается JWT-токен. Специально настроенная роль в StarVault, обладающая политиками доступа к секретам PostgreSQL, использует этот токен для генерации одноразового пароля с временем жизни длительностью в 1 минуту, обеспечивая тем самым безопасное и кратковременное подключение.
Настройка переменных окружения GitLab
От нас требуется лишь две вещи — задать переменную окружения STARVAULT_ADDR, в которой будет храниться адрес для подключения к хранилищу секретов: https://starvault.company.local. И это правда все, больше никакие секреты не хранятся ни в переменных окружения, ни в коде приложения. Только в StarVault.

Ну и, конечно же, нам потребуется создать базовый пайплайн для наглядной демонстрации работы системы. Важное примечание: в реальных условиях мы настоятельно не рекомендуем выводить в логах какую-либо информацию, связанную с секретами и учетными данными.
```yaml
stages:
- deploy
variables:
ANSIBLE_FORCE_COLOR: "1"
starvault_odd_db:
stage: deploy
rules:
- when: manual
id_tokens:
STARVAULT_ID_TOKEN:
aud: $STARVAULT_ADDR
script:
- |
echo "... Получение токена StarVault через JWT ...\"
AUTH_RESPONSE=$(curl -sS -X POST \
-H "Content-Type: application/json" \
-d '{"role": "gitlab-ci-dynamic-password", "jwt": "'"$STARVAULT_ID_TOKEN"'"}' \
"${STARVAULT_ADDR}/v1/auth/jwt/login")
STARVAULT_TOKEN=$(echo "$AUTH_RESPONSE" | jq -r '.auth.client_token')
echo "... Получение динамического пароля для пользователя ruslan ..."
CREDS_RESPONSE=$(curl -sS -H "X-Vault-Token: $STARVAULT_TOKEN" \
"${STARVAULT_ADDR}/v1/database/creds/ruslan-dynamic-password")
echo "Получение секрета для Postgres из StarVault"
RAW_POSTGRES=$(curl -H "X-Vault-Token: $STARVAULT_TOKEN" ${STARVAULT_ADDR}/v1/credentials/data/postgres)
POSTGRES_HOST=$(echo $RAW_POSTGRES | jq -r '.data.data.POSTGRES_HOST')
POSTGRES_PORT=$(echo $RAW_POSTGRES | jq -r '.data.data.POSTGRES_PORT')
POSTGRES_USER=$(echo $RAW_POSTGRES | jq -r '.data.data.POSTGRES_USER')
POSTGRES_DB_NAME=$(echo $RAW_POSTGRES | jq -r '.data.data.TARGET_DB_NAME')
# Извлекаем динамический пароль
POSTGRES_PASSWORD=$(echo "$CREDS_RESPONSE" | jq -r '.data.password')
LEASE_DURATION=$(echo "$CREDS_RESPONSE" | jq -r '.lease_duration')
echo "✅ ДИНАМИЧЕСКИЕ УЧЕТНЫЕ ДАННЫЕ ПОЛУЧЕНЫ!"
echo "=========================================="
echo "? Имя пользователя: $POSTGRES_USER"
echo "? Пароль: $POSTGRES_PASSWORD"
echo "? IP адрес: $POSTGRES_HOST"
echo "? Порт: $POSTGRES_PORT"
echo "⏰ Время жизни: $LEASE_DURATION секунд"
echo "?️ База данных: $POSTGRES_DB_NAME"
echo "=========================================="
# Проверка подключения к PostgreSQL, если client установлен
if command -v psql &>/dev/null; then
echo "? Проверка подключения к PostgreSQL..."
export PGPASSWORD="$POSTGRES_PASSWORD"
# Пробуем подключиться с таймаутом
if timeout 15s psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d "$POSTGRES_DB_NAME" -c "SELECT current_user, now();"; then
echo "✅ Подключение к PostgreSQL успешно!"
# Получаем данные из базы Postgres
psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -d "$POSTGRES_DB_NAME" \
-c "SELECT encrypted_data FROM secure_data_keys;" \
--csv
echo "=========================================="
after_script:
- |
# Очистка переменных после выполнения pipeline
unset POSTGRES_PASSWORD
unset STARVAULT_TOKEN
unset PGPASSWORD
tags:
- shell-jwt
```
Теперь нам остается только осуществить настройки на стороне StarVault. Все настройки будем выполнять через CLI, но хотелось бы отметить, что и через веб-интерфейс также можно.
Включаем метод аутентификации JWT:
starvault auth enable jwt
2. Настраиваем конфигурацию и привязку к GitLab. Для определения ссылки jwks_url пройти по ссылке: http://gitlab.company.local/.well-known/openid-configuration
3. Создаем конфигурацию
starvault write auth/jwt/config \
jwks_url="http://gitlab.company.local /oauth/discovery/keys" \
bound_issuer="http://gitlab.company.local"
4. Настраиваем политики доступа к секрету, назовем gitlab-ci-dynamic-password
starvault policy write gitlab-ci-dynamic-password - <<EOF
path "database/creds/ruslan-dynamic-password" {
capabilities = ["read"]
}
path "database/roles/ruslan-dynamic-password" {
capabilities = ["read"]
}
path "sys/leases/renew" {
capabilities = ["update"]
}
path "database/roles" {
capabilities = ["list"]
}
path "credentials/data/postgres" {
capabilities = ["read", "list"]
}
path "credentials/metadata/postgres" {
capabilities = ["read", "list"]
}
EOF
5. Создание jwt роли для gitlab-ci-postgres. > Важно! Изменить project_id на id вашего проекта:
starvault write auth/jwt/role/gitlab-ci-dynamic-password - <<EOF
{
"role_type": "jwt",
"policies": ["gitlab-ci-dynamic-password"],
"token_explicit_max_ttl": 60,
"user_claim": "user_email",
"bound_audiences": "https://starvault.compamy.local",
"bound_claims": {
"project_id": "28",
"ref_protected": "true",
"ref_type": "branch",
"ref": "main"
}
}
EOF
6. Включение механизма работы с секретами database:
starvault secrets enable database
7. Создаем конфигурацию для базы данных Postgres, чтобы StarVault мог автоматически генерировать временные логины/пароли для приложений вместо использования статических учетных данных:
starvault write database/config/postgresql \
plugin_name=postgresql-database-plugin \
allowed_roles="ruslan-dynamic-password" \
connection_url="postgresql://{{username}}:{{password}}@10.252.221.26:5432/testdb?sslmode=disable" \
username="username_postgres" \
password="username_password"
8. Создаем роль с динамическим секретом для пользователя:
starvault write database/roles/ruslan-dynamic-password \
db_name=postgresql \
creation_statements="ALTER ROLE ruslan WITH PASSWORD '{{password}}';" \
renewal_statements="ALTER ROLE ruslan WITH PASSWORD '{{password}}';" \
default_ttl="1m" \
max_ttl="1m"
9. Выполним наш pipeline:

Мы успешно получили пароль нашего пользователя ruslan, со сроком жизни (TTL=1m) и уже с ним подключились к базе данных и вывели на экран запись из БД.
Заключение
Как вы могли убедиться, внедрение специализированного хранилища секретов на базе StarVault как для защиты DevOps-конвейеров, так и для управления аутентификацией пользователей — вполне посильная задача. Представленные шаги помогут заложить надежный фундамент для вашей системы безопасности.
Комментарии (5)

garrystalin
05.12.2025 14:16Я не очень разбираюсь в теме работы с секретами, но обычно сложновато продумать на перспективу иерархию хранения секретов в таких системах как Vault, она через некоторое время превращается в помойку, сюда же накладывается то что политики навешиваются на префиксы путей до секретов, и просто так при реогранизации иерархии(попытка навести порядок в очередной раз) нужно еще думать о политиках, а там есть свои приколы с порядком применения политик например, о которые люди тоже спотыкаются. Есть практики хранения шифрованных секретов в репозиториях, где такая проблема меньше выражена(вероятно там свои серьезные минусы присутствуют, я может не в теме), но про нее вообще в указанном ГОСТе (56939 а не 5639) ничего особо не сказано, или я не нашел. Это не вопрос и не упрек, просто некоторого рода крик души
trublast
Приветствую!
Часто сталкиваюсь с тем, что в статьях про Vault пишут, что в Vault и его производных поддерживается автоматическая ротация. Но как-то оставляют за скобками, ротация чего, и как она работает. И в вашей статье тоже упоминается "автоматическая ротация".
Можете ли на примере StarVault рассказать, как у вас в продукте работает автоматическая ротация секретов в KV-v2? На каком-то простом примере, вроде такого:
Допустим у меня есть mount kv-v2 который называется dev-secrets. В нем лежат секреты приложения app. Секрет для app доступен по пути /v1/dev-secrets/data/app и представляет из себя json вроде
{"apikey": "xxx",
"dataToken": "yyy"
}
Это не пароли от БД, а просто какие-то ключи API, которые я хочу хранить в хранилище для секретов.
Я предполагаю, что у меня в инфраструктуре все приложения, которые пользуются apikey и dataToken, получают эти значения из Vault. То есть, казалось бы, можно автоматически ротировать секреты в хранилище, и приложения будут пользоваться новыми версиями. Прям то самое, про что написано в ГОСТ.
Так вот, как в StarVault можно настроить автоматическую ротацию xxx и yyy? Желательно на основе password policy (чтобы можно было задать сложность секрета)
ruslangaifutdinov Автор
Добрейшего вечера!
Как в Hashicorp Vault, так и в StarVault для KV/Kv2 не поддерживается ротация секретов. Но тут всё просто, KV ничего не знает про внешние системы, в вашем примере он не сможет сам обновить apikey. Ну и KV - это же просто хранилище.
В нашем примере автоматическая ротация реализована для secret engine c типом DataBase, а вот он как раз знает о нашей конечной системе Postgres всё, так как при его первичной настройке мы делаем connection к нему.
В вашем случае можно сделать скрипт конечно, и можно увязать его с парольной политикой и ротировать по cron job. То есть через функцию generate делаем ваш new_api_key и token. Обновляем в сторонней системе и вашем dev-secrets меняем на новые.
trublast
У меня тоже такое ощущение складывалось, что Vault не умеет "ротировать" секреты, особенно в части kv, но думал, что может у вас какие-то доработки в этой части есть.
Кстати замечу, что динамические секреты, описанные в статье - это не ротация секретов. Это каждый раз создание НОВОЙ ПАРЫ логин/пароль. Ротация, как мне кажется, подразумевает, что старая пара перестанет работать после ротации (хотя на этот счёт могут быть разные мнения). А в вашем примере она перестает работать по TTL
Может быть в вашем случае лучше писать, что "ротация" поддерживается для database static roles? Чтобы не было разночтений и ложных ожиданий.
Просто в вашей табличке с требованиями ГОСТ указано, что Централизованное хранение и Ротация это KV-v2 и Настройка автоматической ротации. А по факту оказывается, что эти два пункта друг с другом вообще никак не связаны и не имеют друг к другу отношения. Секреты сами по себе, лежат в kv. А ротация сама по себе, паролей от БД, а не тех секретов, которые лежат в kv. Из таблицы это совершенно не очевидно.
DmitriyGR
В kv-хранилищах волт точно не умеет ротировать пароли. С database secret engine я не работал, но работал с ldap. Так вот в ldap можно сделать как динамические учетки, так и ротацию кредов на статических учетках. Подозреваю, что с бд будет примерно так же