Эта функция позволяет настроить интеграцию с Vault-сервером и использовать секреты в CI-пайплайнах. Перед началом работы необходимо настроить Vault-сервер и подготовить соответствующие роли и политики.

Настройка Vault

  1. Включите аутентификацию через JWTT:

    vault auth enable jwt
    
    vault write auth/jwt/config \
      oidc_discovery_url="https://code.example.com" \
      bound_issuer="https://code.example.com" \
      default_role="gitlab-role"
    
  2. Создайте роль:

    vault write auth/jwt/role/gitlab-role - <<EOF
    {
      "role_type": "jwt",
      "user_claim": "sub",
      "bound_audiences": ["vault"],
      "bound_claims": {
        "project_id": "23"
      },
      "policies": ["gitlab-policy"],
      "ttl": "1h"
    }
    EOF
    

    Всегда используйте bound_claims, чтобы ограничить доступ к роли. Без этого любой JWT, выданный платформой, сможет получить доступ с этой ролью.

  3. Настройте политики:

    vault policy write gitlab-policy - <<EOF
    path "kv/data/code/vault-demo" {
      capabilities = ["read"]
    }
    EOF
    

Конфигурация CI

Переменные окружения

Для корректной работы с Vault в пайплайне CI/CD необходимо задать следующие переменные окружения:

  • VAULT_SERVER_URLобязательно. URL-адрес Vault-сервера (например, https://vault.example.com).
  • VAULT_AUTH_ROLEопционально. Название роли в Vault. Если не указано, будет использована роль по умолчанию, заданная в конфигурации метода аутентификации.
  • VAULT_AUTH_PATHопционально. Путь до метода аутентификации в Vault. По умолчанию используется jwt.
  • VAULT_NAMESPACEопционально. Namespace Vault, если используется многоуровневая иерархия.

Использование секретов в CI

Для получения секретов из Vault можно использовать следующий шаблон job’а:

stages:
  - test
vault-login:
  stage: test
  image: ruby:3.2
  id_tokens:
    VAULT_ID_TOKEN:
      aud: vault
  secrets:
    DATABASE_PASSWORD:
      vault: code/vault-demo/DATABASE_PASSWORD@kv
      token: $VAULT_ID_TOKEN
  script: echo $DATABASE_PASSWORD

Параметры секрета

Пример:

DATABASE_PASSWORD:
  vault: code/vault-demo/DATABASE_PASSWORD@kv
  token: $VAULT_ID_TOKEN
  file: false

Описание параметров:

  1. vault (обязательно) — путь к секрету в формате строки path/to/secret/KEY@ENGINE, где:
    • code/vault-demo/ — путь до секрета в Vault;
    • DATABASE_PASSWORD — имя поля внутри секрета;
    • kv — точка монтирования Secret Engine (по умолчанию — secret).

По умолчанию используется engine kv-v2. Если необходимо использовать другой engine, можно указать объект вместо строки:

DATABASE_PASSWORD:
  vault: 
    path: code/vault-demo
    field: DATABASE_PASSWORD
    engine:
      name: 'kv-v1'
      path: 'kv1'
  token: $VAULT_ID_TOKEN
  file: false
  1. token (обязательно) — JWT-токен из секции id_tokens, используемый для аутентификации в Vault.

  2. file (опционально, по умолчанию true) — определяет способ предоставления секрета:

    • true — секрет сохраняется во временный файл;
    • false — секрет передаётся как строка в переменную окружения.

Поля, включённые в JWT

Следующие поля автоматически включаются в JWT-токен и могут использоваться Vault для проверки прав доступа:

Поле Условие появления Описание
jti всегда Уникальный идентификатор токена
iss всегда Издатель токена (обычно URL Deckhouse Code)
iat всегда Время выпуска токена (Issued At)
nbf всегда Время, до которого токен считается недействительным
exp всегда Время истечения срока действия токена
sub всегда Subject токена (обычно ID задания CI)
namespace_id всегда ID пространства (группы или пользователя)
namespace_path всегда Путь до пространства (например, groups/dev)
project_id всегда ID проекта
project_path всегда Путь до проекта
user_id всегда ID пользователя
user_login всегда Логин пользователя
user_email всегда Email пользователя
pipeline_id всегда ID CI-пайплайна
pipeline_source всегда Источник запуска пайплайна (push, schedule, MR и т.д.)
job_id всегда ID задания CI
ref всегда Ссылка Git (Git reference)
ref_type всегда Тип ссылки Git (Git reference) (branch или tag)
ref_path всегда Полный путь ссылки Git (Git reference) (например, refs/heads/main)
ref_protected всегда Признак того, что объект по ссылке Git защищён
environment при наличии Название окружения (если используется)
groups_direct при наличии (<200 групп) Пути до групп, в которых состоит пользователь
environment_protected при наличии Является ли окружение защищённым
deployment_tier при наличии Тип окружения (production, staging и т.п.)
environment_action при наличии Тип действия над окружением (например, deploy)

Быстрый старт

В этом разделе приведён пример минимально необходимой настройки интеграции HashiCorp Vault с Deckhouse Code и проверки того, что CI job может получать секреты из Vault.

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

Шаг 1. Установка переменных окружения

Установите переменные окружения для Vault и Deckhouse Code.
Некоторые параметры можно оставить как есть, но VAULT_ADDR, VAULT_TOKEN, CODE_URL и PROJECT_PATH должны быть заданы вручную.

export VAULT_ADDR="https://vault.example.com"
export VAULT_TOKEN="<your-token>"

# URL-адрес Deckhouse Code.
export CODE_URL="https://code.example.com"

# Имя роли и политики Vault.
export VAULT_ROLE="code-role"
export VAULT_POLICY="code-policy"

# Путь и данные секрета.
export VAULT_SECRET_PATH="code/vault-demo"
export VAULT_SECRET_FIELD="DATABASE_PASSWORD"
export VAULT_SECRET_VALUE="super-secret-password"

# Значение claim project_path, которое будет проверять Vault.
export PROJECT_PATH="root/my-pr"

Шаг 2. Включение метода аутентификации JWT

Включите в Vault метод аутентификации JWT. Без этого Vault не сможет принимать ID-токены, которые Deckhouse Code передаёт в CI jobs.

curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST "$VAULT_ADDR/v1/sys/auth/jwt" \
  -d '{"type":"jwt"}'

Шаг 3. Настройка JWT и OIDC

Следующий запрос:

  • задаёт адрес OIDC discovery ($CODE_URL);
  • указывает ожидаемого issuer;
  • определяет роль по умолчанию, которую Vault выдаёт при аутентификации.
curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST "$VAULT_ADDR/v1/auth/jwt/config" \
  --data @- <<EOF
{
  "oidc_discovery_url": "$CODE_URL",
  "bound_issuer": "$CODE_URL",
  "default_role": "$VAULT_ROLE"
}
EOF

Шаг 4. Монтирование Secret Engine KV v2

KV v2 — это наиболее распространённый движок секретов Vault. Следующий запрос включает его по пути /kv:

curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST "$VAULT_ADDR/v1/sys/mounts/kv" \
  -d '{"type": "kv-v2"}'

Шаг 5. Создание тестового секрета

Создайте секрет, который затем должна будет прочитать CI job. Секрет размещается по пути code/vault-demo и содержит одно поле.

curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST "$VAULT_ADDR/v1/kv/data/$VAULT_SECRET_PATH" \
  --data @- <<EOF
{
  "data": {
    "$VAULT_SECRET_FIELD": "$VAULT_SECRET_VALUE"
  }
}
EOF

Шаг 6. Создание ACL-политики

Политика определяет, к каким путям в Vault разрешён доступ. В следующем примере политика предоставляет только право на чтение указанного секрета.

curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X PUT "$VAULT_ADDR/v1/sys/policies/acl/$VAULT_POLICY" \
  --data @- <<EOF
{
  "policy": "path \"kv/data/$VAULT_SECRET_PATH\" { capabilities = [\"read\"] }"
}
EOF

Шаг 7. Создание роли Vault

Роль определяет:

  • тип аутентификации;
  • обязательные claims в токене (project_path);
  • политики, которые получит аутентифицированный субъект;
  • допустимые аудитории (aud);
  • TTL токена.

Deckhouse Code будет выпускать ID-токен с aud=vault, а Vault проверит, что значение project_path соответствует указанному.

curl \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST "$VAULT_ADDR/v1/auth/jwt/role/$VAULT_ROLE" \
  --data @- <<EOF
{
  "role_type":   "jwt",
  "user_claim":  "sub",
  "bound_audiences": ["vault"],

  "bound_claims": {
    "project_path": "$PROJECT_PATH"
  },

  "policies": ["$VAULT_POLICY"],
  "ttl": "1h"
}
EOF

На этом настройка Vault завершена.

Тестирование интеграции в Deckhouse Code

  1. Откройте проект, указанный в PROJECT_PATH. CI-токен проекта должен совпадать с claim project_path. В противном случае Vault отклонит запрос на доступ к секретам.

  2. В настройках CI/CD проекта добавьте переменную VAULT_SERVER_URL со значением $VAULT_ADDR, использованным ранее. Эта переменная сообщает Deckhouse Code, куда следует направлять запросы Vault API.

  3. Создайте файл .gitlab-ci.yml. Файл запускает тестовую CI job, которая:

    • получает ID-токен с aud=vault;
    • передаёт его в Vault;
    • загружает секрет из KV;
    • выводит значение секрета.
    stages:
      - test
       
    vault-demo:
      stage: test
      image: alpine
      id_tokens:
        VAULT_ID_TOKEN:
          aud: vault
      secrets:
        DATABASE_PASSWORD:
          vault: code/vault-demo/DATABASE_PASSWORD@kv
          token: $VAULT_ID_TOKEN
          file: false
      script:
        - echo "Raw value (masked by GitLab):"
        - echo "$DATABASE_PASSWORD"
       
        - echo
        - echo "Value with spaces (not masked):"
        - printf '%s\n' "$DATABASE_PASSWORD" | sed 's/./& /g'
    

Результат

Если интеграция настроена правильно:

  • CI job успешно получит ID-токен;
  • Vault проверит значение project_path и выдаст доступ;
  • секрет будет считан и выведен в лог.

В выводе вы увидите значение поля DATABASE_PASSWORD, загруженное напрямую из Vault.