Аутентификация и чтение секретов в HashiCorp’s Vault через GitLab CI

от автора

Доброго времени суток, читатель!

22 апреля в GitLab выпустили релиз 12.10 и сообщили о том, что теперь CI-процесс может авторизовываться в Hashicorp’s Vault через JSON Web Token (JWT), и для авторизации нет необходимости хранить токен для доступа к нужным policy в переменных окружения (или где-либо ещё).

Данная фича показалась нам полезной, поэтому предлагаем перевод соотвествующего туториала из официальной документации GitLab:


Этот туториал демонстрирует пример аутентификации, конфигурации и чтения секретов с HashiCorp’s Vault через GitLab CI/CD.

Требования

Туториал предполагает, что вы знакомы с GitLab CI/CD и Vault.

Чтобы ему последовать, вам понадобятся:

  • Аккаунт в GItLab
  • Запущенный Vault сервер и доступ для настройки аутентификации и создания ролей и политик.

Примечание: вам нужно заменить URL vault.example.com на URL вашего Vault сервера и gitlab.example.com на название вашего GitLab инстанса.

Как это работает

Для каждой задачи (job) генерируется свой уникальный токен JSON Web Token (JWT), доступный только как значение переменной окружения CI_JOB_JWT конкретной задачи. Данный JWT может быть использован для аутентификации в Vault при помощи метода JWT Auth.

Пример того, как выглядит JWT в дешифрованном виде:

{   "jti": "c82eeb0c-5c6f-4a33-abf5-4c474b92b558", # Уникальный идентификатор токена   "iss": "gitlab.example.com",                   # Issuer, т.е. домен вашего инстанса GitLab   "iat": 1585710286,                             # Время выдачи    "nbf": 1585798372,                             # Не валиден до   "exp": 1585713886,                             # Не велиден после   "sub": "22",                                   # Subject (project id)   "namespace_id": "1",   "namespace_path": "mygroup",   "project_id": "22",   "project_path": "mygroup/myproject",   "user_id": "42",   "user_login": "myuser",   "user_email": "myuser@example.com"   "pipeline_id": "1212",   "job_id": "1212",   "ref": "auto-deploy-2020-04-01",               # Название Git-refs для этой задачи   "ref_type": "branch",                          # Тип Git-refs, это либо(branch) либо тег(tag)   "ref_protected": "true"                        # true, если это ветка protected, иначе false }  

Токен кодируется по стандарту RS256 и подписывается приватным ключом OpenID Connect вашего GitLab инстанса, причем этот ключ периодически изменяется без вашего ведома. И, если приватный ключ был изменен, при cледующем запуске задания новый JWT будет подписан с новым ключом. Срок валидности токена будет устанавливаться в соотвествии с таймаутом вашего задания, а если он не задан, то срок валидности будет 5 минут.

Вы можете использовать этот JWT-токен и URL (https://gitlab.example.com/-/jwks) как конечную точку JWKS для аутентификации на Vault сервере, если для него настроен соответствующий метод JWT-аутентификации.

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

Для получения доступа к Vault можно использовать либо его CLI, либо выполнять запросы к API (через curl или другой клиент).

Пример

Предположим, у вас есть пароли для ваших баз данных, отличающиеся для production и staging окружений и они хранятся в Vault, который доступен по адресу http://vault.example.com:8200. Ваш пароль для stage это pa$$w0rd и real-pa$$w0rd для prod:

$ vault kv get -field=password secret/myproject/staging/db pa$$w0rd  $ vault kv get -field=password secret/myproject/production/db real-pa$$w0rd

Разрешим для нашего Vault-сервера метод аутентификации через JWT:

$ vault auth enable jwt Success! Enabled jwt auth method at: jwt/

Создаем policy, которые дают доступ на чтение к нужным нам секретам:

$ vault policy write myproject-staging - <<EOF # Policy name: myproject-staging # # Read-only permission on 'secret/data/myproject/staging/*' path path "secret/data/myproject/staging/*" {   capabilities = [ "read" ] } EOF Success! Uploaded policy: myproject-staging  $ vault policy write myproject-production - <<EOF # Policy name: myproject-production # # Read-only permission on 'secret/data/myproject/production/*' path path "secret/data/myproject/production/*" {   capabilities = [ "read" ] } EOF Success! Uploaded policy: myproject-production

Теперь нам нужны роли, которые будут связывать созданные политики с JWT-токенами.

Для stage под названием myproject-staging:

$ vault write auth/jwt/role/myproject-staging - <<EOF {   "role_type": "jwt",   "policies": ["myproject-staging"],   "token_explicit_max_ttl": 60,   "user_claim": "user_email",   "bound_claims": {     "project_id": "22",     "ref": "master",     "ref_type": "branch"   } } EOF

И для production под названием myproject-production:

$ vault write auth/jwt/role/myproject-production - <<EOF {   "role_type": "jwt",   "policies": ["myproject-production"],   "token_explicit_max_ttl": 60,   "user_claim": "user_email",   "bound_claims_type": "glob",   "bound_claims": {     "project_id": "22",     "ref_protected": "true",     "ref_type": "branch",     "ref": "auto-deploy-*"   } } EOF

В этом примере использовались bound_claims указывающие, что только JWT-токены с соответствующими значениями смогут пройти аутентификацию.

В сочетании с GitLab protected branches, можно разграничивать тех, кто может проходить аутентификацию и читать секреты.

Token_explicit_max_ttl определяет, что выпущенный Vault токен, после аутентификации имеет время жизни 60 секунд.

User_claim определяет имя пользователя, которое будет использовать Vault при успешной авторизации. (Прим. переводчика — В нашем случае "user_claim" = "user_email", то есть имя пользователя будет иметь значение user_email из JWT-токена. То есть определяться как (известный GitLab) email пользователя, запустившего задание.)

Параметр bound_claims_type задает формат значений bound_claims. Если он установлен в “glob”, то значения будут интерпретироваться в формате glob и * будет означать любое количество символов. (Прим. переводчика — Также доступно значение “string”, в этом случае данные будут хранится в формате строк и * будет означать только *.)
Полный лист опций можно посмотреть в Vault’s Create Role documentation.

Предупреждение: обязательно устанавливайте либо project_id либо namespace_id, иначе любой токен, сгенерированный этим инстансом GitLab, сможет использовать эту роль.

Теперь зададим метод аутентификации через JWT:

$ vault write auth/jwt/config \     jwks_url="https://gitlab.example.com/-/jwks" \     bound_issuer="gitlab.example.com"

bound_issuer определяет, что только токены, выпущенные от имени домена gitlab.example.com (iss claim) могут использовать этот метод аутентификации, и в качестве точки валидации токена JWKS должна использоваться страница https://gitlab.example.com/-/jwks .

Полный список доступных опций можно посмотреть в Vault’s API documentation.

Теперь создадим джобу для master-ветки, у которой будут права на чтение secret/myproject/staging/, но при этом не будет прав на чтение secret/myproject/production/:

read_secrets:   script:     # Проверяем имя ref джобы     - echo $CI_COMMIT_REF_NAME     # и является ли она protected     - echo $CI_COMMIT_REF_PROTECTED     # Адрес Vault может быть передан через переменную в CI     - export VAULT_ADDR=http://vault.example.com:8200     # Проходим аутентификацию и получаем токен. Время истечения жизни токена и другие       # его параметры можно задать при конфигурации      # JWT Auth - https://www.vaultproject.io/api/auth/jwt#parameters-1     - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-staging jwt=$CI_JOB_JWT)"     # Теперь используем VAULT_TOKEN для чтения секретов и сохранения их в перемнных окружения     - export PASSWORD="$(vault kv get -field=password secret/myproject/staging/db)"     # Используем секрет     - echo $PASSWORD     # Здесь получить секрет не получится, потому что роль myproject-staging не может      # читать секреты из secret/myproject/production/*     - export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"

Следующее задание сможет пройти аутентификацию через роль myproject-production и прочитать секрет в /secret/myproject/production/:

read_secrets:   script:     # Проверяем имя ref джобы     - echo $CI_COMMIT_REF_NAME     # и является ли она protected     - echo $CI_COMMIT_REF_PROTECTED     # Адрес Vault может быть передан через переменную в CI     - export VAULT_ADDR=http://vault.example.com:8200     # Проходим аутентификацию и получаем токен. Время истечения жизни токена и другие       # его параметры можно задать при конфигурации      # JWT Auth - https://www.vaultproject.io/api/auth/jwt#parameters-1     - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-production jwt=$CI_JOB_JWT)"     # Теперь используем VAULT_TOKEN для чтения секретов и сохранения их в перемнных окружения     - export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"     # Используем секрет     - echo $PASSWORD


На этом все, надеюсь данный туториал окажется вам полезен!

Мы также ждем следующей фичи GitLab, где можно будет авторизовываться в Vault средствами GitLab в версии 13.4, которая выйдет 22 сентября 2020.

ссылка на оригинал статьи https://habr.com/ru/company/nixys/blog/512754/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *