Skip to content

Auth Gitlab + JWT + Vault

Разберём как подключаться к vault из CI pipeline, используя JWT токены

Cначала нужно создать репозиторий/проект в Gitlab.

Сгенерированный Gitlab project_id (см. скриншот ниже) для этого репозитория будет использоваться для указания Vault, к каким секретам у этого проекта есть доступ. alt text

alt text

Шаг 1. Настройка Vault and secrets

Включить метод jwt в vault: vault auth enable jwt и создадим тестовый секрет vault kv put secret/gitlab/db1 password='pa$$w0rd'

JWT signatures будут проверяться публичными ключами issuer-a. Этот процесс может быть осуществлен тремя способами: static keys, JWKS, OIDC Discovery.

Мы будет использовать OIDC Discovery, потому что:

Для OIDC discovery будет по-стандарту опрашиваться путь /.well-known/openid-configuration, откуда будет получен jwks_uri с публичными ключами для проверки подписи токенов

Чтобы использовать OIDC discovery for JWT аутентификации:

  • oidc_client_id и oidc_client_secret должны быть пустыми.
  • вместо gitlab.domain.ru может быть ссылка на OIDC провайдер, например на keycloak или MYDOMAIN.eu.auth0.com
vault write auth/jwt/config \
    oidc_discovery_url="https://gitlab.<domain>/" \
    oidc_client_id="" \
    oidc_client_secret="" \
    bound_issuer="https://gitlab.<domain>"

oidc_discovery_url: URL-адрес конечной точки JKWS указывает Vault, "как" проверить JWT (Для OIDC discovery будет по-стандарту опрашиваться путь /.well-known/openid-configuration, откуда будет получен jwks_uri с публичными ключами для проверки подписи токенов )

bound_issuer: "кто" может использовать этот метод аутентификации

Шаг 2. Настройка gitlab pipeline, Генерирование JWT токена

В момент когда ci тригерится на запуск - генерируется JWT токен. Обычно gitlab jwt будет включать информацию о gitlab сервер, user выполняющий pipeline job, gitlab project id, vault secret path

Примерно как выглядит jwt payload: alt text

gitlab-ci.yml
stages:
  - "vault"

read_secrets:
  stage: vault
  image: vault:1.13.3
  variables:
    VAULT_ADDR: https://vault.<domain>.ru
    VAULT_SKIP_VERIFY: 'true'
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.<domain>.ru
  script:
    - echo $CI_COMMIT_REF_NAME
    - echo $CI_COMMIT_REF_PROTECTED

    - echo $VAULT_ID_TOKEN

    - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=gitlabci-role jwt=$VAULT_ID_TOKEN)"
    - export PASSWORD="$(vault kv get -field=password secret/gitlab/db1)"
    - echo $PASSWORD

Шаг 3 и Шаг 4. Authenticate to Vault + Vault verify JWT

Vault проверит подпись JWT, сравнив ее с подписью, находящейся в предоставленной конечной точке jwks_uri полученной из oidc_discovery_url(/.well-known/openid-configuration).

Шаг 5. Vault checks bounded claims and attached policies

project_id — идентификатор проекта в GitLab, к которому будет привязана данная роль. Другие проекты не смогут взаимодействовать с ней и получать доступ к секретам. создаем роль

vault write auth/jwt/role/gitlabci-role - <<EOF
{
  "role_type": "jwt",
  "policies": ["gitlabci-policy"],
  "token_explicit_max_ttl": 60,
  "user_claim": "user_email",
  "bound_claims_type": "glob",
  "bound_claims": {
    "project_id": "121",
    "ref_protected": "true",
    "ref_type": "branch",
    "ref": "main"
  }
}
EOF
После проверки полезной нагрузки JWT Vault извлечет роль Vault.

Роль Vault указывает, что она может использоваться для аутентификации по JWT, у нее прикреплена политика gitlabci-policy, и есть раздел bound_claims, который определяет дополнительные параметры для "кого" может быть присвоена эта роль.

Раздел bound_claims указывает, что должны быть выполнены следующие критерии: только проекту/репозиторию Gitlab с project_id 121 может быть присвоена эта роль, и только сборке CI/CD для основной ветки (main) может быть присвоена эта роль.

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

Наконец, предполагая, что JWT содержит необходимые критерии для этой роли, Vault создаст токен. Этот токен Vault получит возможность присвоить эту роль.

Шаги 6,7. Vault returns token + Gitlab runner reads secret from Vault

настраиваем политику доступа к конкретному секрету

vault policy write gitlabci-policy - <<EOF
path "secret/data/gitlab/db1" {
  capabilities = [ "read" ]
}
EOF