docs: create DevOps/Hashicorp-Vault/Gitlab-with-vault
parent
3cc2b962b0
commit
27a7baa660
|
|
@ -0,0 +1,196 @@
|
||||||
|
---
|
||||||
|
title: Аутентификация и чтение секретов в HashiCorp's Vault через GitLab CI
|
||||||
|
description:
|
||||||
|
published: true
|
||||||
|
date: 2024-07-31T12:59:36.391Z
|
||||||
|
tags: vault, gitlab
|
||||||
|
editor: markdown
|
||||||
|
dateCreated: 2024-07-31T12:59:36.391Z
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
Источник: https://habr.com/ru/companies/nixys/articles/512754/
|
||||||
|
Этот туториал демонстрирует пример аутентификации, конфигурации и чтения секретов с HashiCorp’s Vault через GitLab CI/CD.
|
||||||
|
|
||||||
|
**Требования**
|
||||||
|
|
||||||
|
Туториал предполагает, что вы знакомы с GitLab CI/CD и Vault.
|
||||||
|
|
||||||
|
Чтобы ему последовать, вам понадобятся:
|
||||||
|
|
||||||
|
- Аккаунт в GItLab
|
||||||
|
- Запущенный Vault сервер и доступ для настройки аутентификации и создания ролей и политик.
|
||||||
|
Для каждой задачи (job) генерируется свой уникальный токен JSON Web Token (JWT), доступный только как значение переменной окружения CI_JOB_JWT конкретной задачи. Данный JWT может быть использован для аутентификации в Vault при помощи метода JWT Auth.
|
||||||
|
Пример того, как выглядит JWT в дешифрованном виде:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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, которые дают доступ на чтение к нужным нам секретам:
|
||||||
|
```bash
|
||||||
|
$ 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:
|
||||||
|
```json
|
||||||
|
$ 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:
|
||||||
|
```json
|
||||||
|
$ 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/:
|
||||||
|
```yaml
|
||||||
|
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/:
|
||||||
|
```yaml
|
||||||
|
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
|
||||||
|
```
|
||||||
|

|
||||||
|
На этом все, надеюсь данный туториал окажется вам полезен!
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue