Auth Gitlab + JWT + Vault¶
Разберём как подключаться к vault из CI pipeline, используя JWT токены¶
Cначала нужно создать репозиторий/проект в Gitlab.
Сгенерированный Gitlab project_id (см. скриншот ниже) для этого репозитория будет использоваться для указания Vault, к каким секретам у этого проекта есть доступ.
Шаг 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, потому что:
- Депрекейтятся старые версии JWT, вместо которых в >=15.7 появились другие JWT, которые назвали ID Tokens, которые и будем использовать
- Будет удалён путь /-/jwks, который был алиасом к пути /oauth/discovery/keys, и который был частью старых JWT
- kube-api-server не использует путь /-/jwks.
Для 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:
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
Роль 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