Привет, Хабр! Я Руслан Гайфутдинов, ведущий пресейл-инженер в команде системы управления секретами 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, но хотелось бы отметить, что и через веб-интерфейс также можно.

  1. Включаем метод аутентификации 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-конвейеров, так и для управления аутентификацией пользователей — вполне посильная задача. Представленные шаги помогут заложить надежный фундамент для вашей системы безопасности.

Комментарии (0)