Vault
AppRole
Для интеграции сервисов и инструментов (напр. Ansible) с Vault можно использовать метод доступа AppRole. Суть сводится к созданию политики, роли и секрета на стороне Vault, а на сторону сервиса передается идентификаторы роли и секрета.
Шаг 1 - создаем политику, напр. ansible-policy
path "kv/*" { capabilities = ["read"] }
Шаг 2 - активируем метод доступа и создаем роль (нам потребуется ид-р роли)
vault auth enable approle vault write auth/approle/role/ansible token_policies="ansible-policy" vault read auth/approle/role/ansible/role-id # Key Value # --- ----- # role_id 39aef9d2-...
Шаг 3 - создаем секрет
vault write -f auth/approle/role/ansible/secret-id # Key Value # --- ----- # secret_id 469f6928-...
Шаг 4 - авторизуемся на клиенте с использованием role_id
и secret_id
# --- получаем токен vault write auth/approle/login \ role_id="39aef9d2-..." \ secret_id="469f6928-..." # Key Value # --- ----- # token hvs.CAESIO...
Используем токен для работы
JWT и Gitlab CI
Секреты можно забирать из Vault при выполнении линии сборки Gitlab CI через механизм JWT. Пример конфигурации:
Активируем метод доступа
vault auth enable jwt
Конфигурация метода доступа
vault write auth/jwt/config -<<EOF { "jwks_url": "https://gitlab.example.com/oauth/discovery/keys", "bound_issuer": "https://gitlab.example.com" } EOF
Создаем роль для метода доступа
# --- для проекта vault write auth/jwt/role/ci-backend - <<EOF { "role_type": "jwt", "policies": ["some-policy"], "token_explicit_max_ttl": 60, "user_claim": "user_email", "bound_audiences": ["https://vault.example.com"], "bound_claims": { "project_path": "project/path" } } EOF # --- для группы проектов vault write auth/jwt/role/ci-backend - <<EOF { "role_type": "jwt", "policies": ["some-policy"], "token_explicit_max_ttl": 60, "user_claim": "user_email", "bound_audiences": ["https://vault.example.com"], "bound_claims": { "namespace_path": "project_group/path" } } EOF
Конфигурация джобы в .gitlab-ci.yml
test_vault: stage: test image: name: hashicorp/vault:1.19 id_tokens: JWT: aud: https://vault.example.com script: - export VAULT_ADDR=https://vault.example.com - export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=ci-backend jwt=$JWT)" - export MY_SECRET="$(vault kv get -field=my_secret kv/test/app)" - echo $MY_SECRET
Kubernetes аутентификация и External Secrets
Крайне полезно иметь прозрачный механизм доставки секретов из Vault в Kubernetes, например https://external-secrets.io. Ниже приводится инструкция создания секретов в пространстве имен external-secrets, но для практического использования лучше использовать пространство имен приложения. Подразумевается:
- Vault развернут отдельно от Kubernetes и имеет адрес https://vault.example.com
- в Vault существует политика dev
- в Vault секреты смонтированы по пути kv
- в Kubernetes установлен контроллер ExternalSecrets
Создаем сервисную учетную запись
kubectl create serviceaccount vault-auth -n external-secrets kubectl apply -n external-secrets -f - <<EOF apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vault-auth-delegator roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: vault-auth namespace: external-secrets EOF kubectl apply -n external-secrets -f - <<EOF apiVersion: v1 kind: Secret metadata: name: vault-auth-token annotations: kubernetes.io/service-account.name: vault-auth type: kubernetes.io/service-account-token EOF
Настраивем Kubernetes аутентификацию на стороне Vault. Учитывая, что Vault может обслуживать произвольное количество кластеров, явно задаем путь монтирования - k8s-v2
vault auth enable -path=k8s-v2 kubernetes kubectl get secret vault-auth-token -n external-secrets -o jsonpath='{.data.token}' | base64 -d > token.jwt kubectl get secret vault-auth-token -n external-secrets -o jsonpath='{.data.ca\.crt}' | base64 -d > ca.crt KUBE_API=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}') vault write auth/k8s-v2/config \ token_reviewer_jwt=@token.jwt \ kubernetes_host="$KUBE_API" \ kubernetes_ca_cert=@ca.crt vault write auth/k8s-v2/role/v2-dev \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=external-secrets \ policies=dev \ ttl=1h
Создаем сущности ExternalSecrets
kubectl apply -n external-secrets -f - <<EOF apiVersion: external-secrets.io/v1 kind: SecretStore metadata: name: vault-store namespace: external-secrets spec: provider: vault: server: "https://vault.examle.com" path: "kv" # <---- точка монтирования секретов version: "v2" auth: kubernetes: mountPath: "k8s-v2" role: "v2-dev" serviceAccountRef: name: vault-auth EOF kubectl apply -n external-secrets -f - <<EOF apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: example-secret spec: refreshInterval: 1h secretStoreRef: name: vault-store kind: SecretStore target: name: my-secret # <--- имя создаваемого секрета в Kubernetes dataFrom: - extract: key: foo/bar # <----- путь ключа в Vault EOF
OIDC аутентификация
В качестве OIDC провайдера можно использовать Keycloak. Там создаем Realm роли и назначаем из пользователю. Также необходимо сделать так, чтобы назначенные роли попали в id_token, который выдается со стороны Keycloak после аутентификации (информация есть здесь https://stackoverflow.com/questions/68741412/grafana-generic-oauth-role-assignment).
Создаем oidc метод доступа (обязательно указываем роль по умолчанию)
vault auth enable oidc
Создаем роль для oidc
vault write auth/oidc/role/default -<<EOF { "allowed_redirect_uris": ["https://vault.example.com/ui/vault/auth/oidc/oidc/callback"], "user_claim":"email", "groups_claim":"roles", "bound_audiences": "vault", "oidc_scopes": "openid roles", "ttl": "8h" } EOF
Команда выше подразумевает, что id_token из Keycloak содержит поле roles с массивом Realm ролей.
Уже можно пробовать зайти через OIDC и получить доступ в объеме default политики.
Допустим уже существует политика admin, которая предоставляет административный доступ к Vault. Чтобы к ней привязаться ролью vault-admin из Keycloak нам потребуется создать т.н. identity/group
и identity/group-alias
.
Кроме того, нам потребуется получить т.н. mount_accessor
для oidc
vault auth list -detailed Path Plugin Accessor ------- ------ -------- oidc/ oidc auth_oidc_123 <------------ mount_accessor ...
Создаем identity/group
(идентификатор группы пригодится в следующей команде)
vault write identity/group \ name="vault-admin-group" \ type="external" \ policies="admin" Key Value --- ----- id 123123 name vault-admin-group
Создаем identity/group-alias
vault write identity/group-alias \ name="vault-admin" \ mount_accessor="auth_oidc_123" \ canonical_id="123123"
Здесь значения для mount_accessor
и cacnonical_id
определяются в командах выше.
В данной конфигурации, пользователю с Realm ролью vault-admin будет назначена политика admin в Vault.
Для добавления новых ролей необходимо выполнить:
- на стороне Keycloak создаем роль и назначаем ее пользователю
- на стороне Vault создаем политику для этой роли, а также соответствующие
identity/group
иidentity/group-alias