Vault with k8s: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) |
||
(не показаны 33 промежуточные версии этого же участника) | |||
Строка 1: | Строка 1: | ||
[[Категория:K8s]] |
[[Категория:K8s]] |
||
[[Категория:Hashicorp_Vault]] |
[[Категория:Hashicorp_Vault]] |
||
+ | [[Категория:Vault]] |
||
[[Категория:Linux]] |
[[Категория:Linux]] |
||
Строка 10: | Строка 11: | ||
==Схема работы== |
==Схема работы== |
||
− | 1. Создается сервисный аккаунт |
+ | 1. Создается сервисный аккаунт <BR> |
− | 2. Запускается POD с этим сервисным аккаунтом |
+ | 2. Запускается POD с этим сервисным аккаунтом <BR> |
− | 3. |
+ | 3. POD получает адрес Vault каким-то способом (в моем случае - вычитывает из Consul но это не принципиально, можно через переменную окружения или еще как-то) <BR> |
− | 4. Авторизуется в Vault под определенной ролью используя JWT |
+ | 4. Авторизуется в Vault под определенной ролью используя JWT <BR> |
− | 5 Vault возвращает |
+ | 5 Vault возвращает токен с политиками назначенными на роль <BR> |
− | 5. POD читает из Vault используя полученный токен |
+ | 5. POD читает из Vault используя полученный токен <BR> |
+ | <PRE> |
||
+ | +-------------------------------------------------------------------------------------------------------------------------------+ |
||
+ | | Google Cloud Engine | |
||
+ | | | |
||
+ | | | |
||
+ | | +-----------------------------------------------------------+ | |
||
+ | | | K8S Cluster (in GKE) | | |
||
+ | | | | | |
||
+ | | | | | |
||
+ | | | +---------------------------------------------------+ | | |
||
+ | | | | POD | | | |
||
+ | | | | spec: | | | |
||
+ | | | | serviceAccountName: k8s-test-service-account | | | |
||
+ | | | | ... | | | |
||
+ | | | | containers: | | | |
||
+ | | | | - name: container-name | | | |
||
+ | | | | image: ... | | +-------------------+ | |
||
+ | | | | imagePullPolicy: Always | | | VM Instance | | |
||
+ | | | | env: | | | | | |
||
+ | | | | - name: VAULT_K8S_ROLE | | | +------------+ | | |
||
+ | | | | value: k8s-test-role | ---|-->---> Token Request for role ->-->-->---| | Vault | | | |
||
+ | | | +---------------------------------------------------+ | * POD's SA JWT | | Process | | | |
||
+ | | | | * Role Name | | | | | |
||
+ | | | | | | | | | |
||
+ | | | +--------------------+ | Verify POS's Service Account JWT | | | | | |
||
+ | | | | K8S API Endpoint |<--------<--------<----------------|-( auth with Vault's Service Account )-<--| | | | | |
||
+ | | | +--------------------+ | | +------------+ | | |
||
+ | | | | | | | |
||
+ | | +-----------------------------------------------------------+ +-------------------+ | |
||
+ | | | |
||
+ | +-------------------------------------------------------------------------------------------------------------------------------+ |
||
+ | </PRE> |
||
+ | ==Настройка== |
||
+ | ===k8s=== |
||
+ | ====Сервисный аккаунт ==== |
||
+ | Это аккаунт с которым Vault подключается к API k8s для валидации JWT |
||
+ | <PRE> |
||
+ | --- |
||
+ | apiVersion: v1 |
||
+ | kind: ServiceAccount |
||
+ | metadata: |
||
+ | name: vault-tokenreview-service-account |
||
+ | namespace: test-namespace |
||
+ | </PRE> |
||
+ | ====Права сервисного аккаунта==== |
||
+ | Для того что б аккаунт мог проверить JWT у других сервисных аккаунтов ему назначается |
||
+ | ClusterRole <B>system:auth-delegator</B> |
||
+ | <BR> |
||
+ | <B>Важно:</B> Назначить права нужно в 2 неймспейсах - если назначить только в <B>test-namespace</B> то прав для проверки JWT недостаточно (что совершенно не очевидно из конфигурации) |
||
+ | <PRE> |
||
+ | apiVersion: rbac.authorization.k8s.io/v1 |
||
+ | kind: ClusterRoleBinding |
||
+ | metadata: |
||
+ | name: vault-tokenreview-service-account-role-binding |
||
+ | namespace: test-namespace |
||
+ | roleRef: |
||
+ | apiGroup: rbac.authorization.k8s.io |
||
+ | kind: ClusterRole |
||
+ | name: system:auth-delegator |
||
+ | subjects: |
||
+ | - kind: ServiceAccount |
||
+ | name: vault-tokenreview-service-account |
||
+ | namespace: test-namespace |
||
+ | |||
+ | --- |
||
+ | apiVersion: rbac.authorization.k8s.io/v1beta1 |
||
+ | kind: ClusterRoleBinding |
||
+ | metadata: |
||
+ | name: vault-tokenreview-service-account-role-binding-2 |
||
+ | namespace: default |
||
+ | roleRef: |
||
+ | apiGroup: rbac.authorization.k8s.io |
||
+ | kind: ClusterRole |
||
+ | name: system:auth-delegator |
||
+ | subjects: |
||
+ | - kind: ServiceAccount |
||
+ | name: vault-tokenreview-service-account |
||
+ | namespace: test-namespace |
||
+ | </PRE> |
||
+ | |||
+ | ===Vault=== |
||
+ | |||
+ | Для настройки Vault потребуется токен с рутовыми правами адрес Vault дан для примера. |
||
+ | В моем случае он вычитывается из Consul. |
||
+ | <PRE> |
||
+ | export VAULT_TOKEN="<ROOT_TOKEN>" |
||
+ | export VAULT_ADDR="http://192.168.1.4:8200" |
||
+ | </PRE> |
||
+ | |||
+ | ====Включить поддержку k8s в Vault==== |
||
+ | <PRE> |
||
+ | vault auth-enable kubernetes |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | ====Получение необходимых секретов из K8S==== |
||
+ | Так-как в моем случае все PODы находятся в отдельном namespace то: |
||
+ | <PRE> |
||
+ | export NAMESPACE="my-namespace-name" |
||
+ | </PRE> |
||
+ | Получить имя "секрета" для сервисного аккаунта vault-tokenreview-service-accoun (аккаунт под которым Vault работает с K8S API) |
||
+ | <PRE> |
||
+ | export SECRET_NAME=$( \ |
||
+ | kubectl \ |
||
+ | --namespace ${NAMESPACE} \ |
||
+ | get \ |
||
+ | serviceaccount \ |
||
+ | vault-tokenreview-service-account \ |
||
+ | -o jsonpath='{.secrets[0].name}' ) |
||
+ | </PRE> |
||
+ | Зная имя секрета можно получить его токен: |
||
+ | <PRE> |
||
+ | export ACCOUNT_TOKEN=$( \ |
||
+ | kubectl \ |
||
+ | --namespace ${NAMESPACE} \ |
||
+ | get \ |
||
+ | secret ${SECRET_NAME} \ |
||
+ | -o jsonpath='{.data.token}' \ |
||
+ | | base64 --decode ) |
||
+ | </PRE> |
||
+ | |||
+ | В том же секрете содержится сертификат СА: |
||
+ | <PRE> |
||
+ | export SERVICE_ACCOUNT_CA_CRT=$( \ |
||
+ | kubectl \ |
||
+ | --namespace ${NAMESPACE} \ |
||
+ | get \ |
||
+ | secret ${SECRET_NAME} \ |
||
+ | -o jsonpath="{.data['ca\.crt']}" \ |
||
+ | | base64 --decode; echo ) |
||
+ | </PRE> |
||
+ | ====Настройка доступа Vault в K8S==== |
||
+ | Настроить vault (адрес API можно взять из конфигурации kubectl) |
||
+ | <PRE> |
||
+ | vault \ |
||
+ | write \ |
||
+ | auth/kubernetes/config \ |
||
+ | kubernetes_host="https://192.168.191.2:443" \ |
||
+ | kubernetes_ca_cert="${SERVICE_ACCOUNT_CA_CRT}" \ |
||
+ | token_reviewer_jwt="${ACCOUNT_TOKEN}" |
||
+ | </PRE> |
||
+ | |||
+ | Обратить внимание: |
||
+ | |||
+ | * Service Account - это аккаунт с которым будет деплоиться POD. Это аккаунт в k8s а не в Vault. |
||
+ | * Роль существует только в пределах Vault (а не в K8S) |
||
+ | * При авторизации POD запрашивает токен для <B>роли</B> |
||
+ | * На токен будут назначены политики из привязки |
||
+ | |||
+ | ====Привязка сервисных аккаунтов K8S к ролям Vault==== |
||
+ | <PRE> |
||
+ | export VAULT_K8S_SERVICE_ACCOUNT="k8s-test-service-account" |
||
+ | export VAULT_K8S_ROLE="k8s-test-role" |
||
+ | </PRE> |
||
+ | Здесь VAULT_K8S_SERVICE_ACCOUNT - это сервисный аккаунт в k8s который можно создать например так: |
||
+ | <PRE> |
||
+ | --- |
||
+ | apiVersion: v1 |
||
+ | kind: ServiceAccount |
||
+ | metadata: |
||
+ | name: k8s-service-account |
||
+ | namespace: my-namespace-name |
||
+ | </PRE> |
||
+ | <B>Важно:</B> Это сервисный аккаунт для запуска PODов, и это НЕ ТОТ сервисный аккаунт который используется Vault |
||
+ | <BR> |
||
+ | Создать привязку |
||
+ | <PRE> |
||
+ | vault \ |
||
+ | write \ |
||
+ | auth/kubernetes/role/${VAULT_K8S_ROLE} \ |
||
+ | bound_service_account_names=${VAULT_K8S_SERVICE_ACCOUNT} \ |
||
+ | bound_service_account_namespaces=${NAMESPACE} \ |
||
+ | policies=policy1,policy2,default \ |
||
+ | ttl=1h |
||
+ | </PRE> |
||
+ | Обратить внимание - политики <B>policy1,policy2</B> уже существуют (созданы до этого). Если политик нет их необходимо создать. |
||
+ | |||
+ | ==Деплоймент в K8S== |
||
+ | Примерная конфигурация PODа |
||
+ | <PRE> |
||
+ | apiVersion: extensions/v1beta1 |
||
+ | kind: Deployment |
||
+ | metadata: |
||
+ | name: my-deployment |
||
+ | spec: |
||
+ | replicas: 1 |
||
+ | selector: |
||
+ | matchLabels: |
||
+ | app: my-deployment-pod |
||
+ | template: |
||
+ | metadata: |
||
+ | labels: |
||
+ | app: my-deployment-pod |
||
+ | spec: |
||
+ | serviceAccountName: "k8s-test-service-account" |
||
+ | containers: |
||
+ | - name: my-container-name |
||
+ | image: ... |
||
+ | imagePullPolicy: Always |
||
+ | args: ... |
||
+ | env: |
||
+ | - name: VAULT_K8S_ROLE |
||
+ | value: "k8s-test-role" |
||
+ | </PRE> |
||
+ | |||
+ | ==Получение токена== |
||
+ | Адрес Vault известен (через переменную окружения или из Consul)<BR> |
||
+ | Токен сервисного аккаунта монтируется как вольюм |
||
+ | <BR> |
||
+ | Изнутри контейнера: |
||
+ | <PRE> |
||
+ | export VAULT_ADDR="http://192.168.1.4:8200" |
||
+ | export JWT="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" |
||
+ | </PRE> |
||
+ | |||
+ | |||
+ | ===Авторизация используя утилиту vault=== |
||
+ | <PRE> |
||
+ | vault write auth/kubernetes/login role=k8s-test-role jwt=${JWT} |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | Key Value |
||
+ | --- ----- |
||
+ | token s.SyMDLTmAYLtEA13vBVfjqyfq |
||
+ | token_accessor 3pQbarDfUWChuIgWZRnnKMMB |
||
+ | token_duration 1h |
||
+ | token_renewable true |
||
+ | token_policies ["policy1" "policy2" "default" ] |
||
+ | identity_policies [] |
||
+ | policies token_policies ["policy1" "policy2" "default" ] |
||
+ | token_meta_service_account_name k8s-test-service-account |
||
+ | token_meta_service_account_namespace test-namespace |
||
+ | token_meta_service_account_secret_name k8s-test-service-account-token-klp4w |
||
+ | token_meta_service_account_uid fe7fc17f-2305-11e9-b347-4201c0a8bf08 |
||
+ | token_meta_role k8s-testi-role |
||
+ | |||
+ | </PRE> |
||
+ | |||
+ | ===Авторизация через Curl=== |
||
+ | <PRE> |
||
+ | curl \ |
||
+ | --request POST \ |
||
+ | --data '{"jwt": "'"${JWT}"'", "role": "k8s-test-role"}' \ |
||
+ | ${VAULT_ADDR}/v1/auth/kubernetes/login |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | { |
||
+ | "request_id": "8d524407-ee6b-a6c2-b011-d02572ae2ce9", |
||
+ | "lease_id": "", |
||
+ | "renewable": false, |
||
+ | "lease_duration": 0, |
||
+ | "data": null, |
||
+ | "wrap_info": null, |
||
+ | "warnings": null, |
||
+ | "auth": { |
||
+ | "client_token": "s.3YsrjrkBZLOXF5sAmR2ceqrs", |
||
+ | "accessor": "I532wSMIIXYSiAocKcShEhe7", |
||
+ | "policies": [ |
||
+ | "policy1", |
||
+ | "policy2", |
||
+ | "default" |
||
+ | ], |
||
+ | "token_policies": [ |
||
+ | "policy1", |
||
+ | "policy2", |
||
+ | "default" |
||
+ | ], |
||
+ | "metadata": { |
||
+ | "role": "k8s-test-role", |
||
+ | "service_account_name": "k8s-test-service-account", |
||
+ | "service_account_namespace": "test-namespace", |
||
+ | "service_account_secret_name": "k8s-test-service-account-token-klp4w", |
||
+ | "service_account_uid": "fe7fc17f-2305-11e9-b347-4201c0a8bf08" |
||
+ | }, |
||
+ | "lease_duration": 3600, |
||
+ | "renewable": true, |
||
+ | "entity_id": "5d4ec129-0298-54a6-bf03-a51dbda0a917", |
||
+ | "token_type": "service" |
||
+ | } |
||
+ | } |
||
+ | </PRE> |
||
=Ссылки= |
=Ссылки= |
||
+ | |||
+ | |||
+ | * https://coreos.com/tectonic/docs/latest/vault-operator/user/kubernetes-auth-backend.html |
||
+ | * https://medium.com/@gmaliar/dynamic-secrets-on-kubernetes-pods-using-vault-35d9094d169 |
||
+ | * https://blog.openshift.com/vault-integration-using-kubernetes-authentication-method/ |
||
+ | * https://www.vaultproject.io/docs/auth/kubernetes.html |
Текущая версия на 20:33, 8 октября 2021
Авторизация контейнеров/PODов в Hashicorp Vault
Задача - использовать сервисные аккаунты кубернетиса для авторизации а Hashicorp Vault
Схема работы
1. Создается сервисный аккаунт
2. Запускается POD с этим сервисным аккаунтом
3. POD получает адрес Vault каким-то способом (в моем случае - вычитывает из Consul но это не принципиально, можно через переменную окружения или еще как-то)
4. Авторизуется в Vault под определенной ролью используя JWT
5 Vault возвращает токен с политиками назначенными на роль
5. POD читает из Vault используя полученный токен
+-------------------------------------------------------------------------------------------------------------------------------+ | Google Cloud Engine | | | | | | +-----------------------------------------------------------+ | | | K8S Cluster (in GKE) | | | | | | | | | | | | +---------------------------------------------------+ | | | | | POD | | | | | | spec: | | | | | | serviceAccountName: k8s-test-service-account | | | | | | ... | | | | | | containers: | | | | | | - name: container-name | | | | | | image: ... | | +-------------------+ | | | | imagePullPolicy: Always | | | VM Instance | | | | | env: | | | | | | | | - name: VAULT_K8S_ROLE | | | +------------+ | | | | | value: k8s-test-role | ---|-->---> Token Request for role ->-->-->---| | Vault | | | | | +---------------------------------------------------+ | * POD's SA JWT | | Process | | | | | | * Role Name | | | | | | | | | | | | | | | +--------------------+ | Verify POS's Service Account JWT | | | | | | | | K8S API Endpoint |<--------<--------<----------------|-( auth with Vault's Service Account )-<--| | | | | | | +--------------------+ | | +------------+ | | | | | | | | | +-----------------------------------------------------------+ +-------------------+ | | | +-------------------------------------------------------------------------------------------------------------------------------+
Настройка
k8s
Сервисный аккаунт
Это аккаунт с которым Vault подключается к API k8s для валидации JWT
--- apiVersion: v1 kind: ServiceAccount metadata: name: vault-tokenreview-service-account namespace: test-namespace
Права сервисного аккаунта
Для того что б аккаунт мог проверить JWT у других сервисных аккаунтов ему назначается
ClusterRole system:auth-delegator
Важно: Назначить права нужно в 2 неймспейсах - если назначить только в test-namespace то прав для проверки JWT недостаточно (что совершенно не очевидно из конфигурации)
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vault-tokenreview-service-account-role-binding namespace: test-namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: vault-tokenreview-service-account namespace: test-namespace --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: vault-tokenreview-service-account-role-binding-2 namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: vault-tokenreview-service-account namespace: test-namespace
Vault
Для настройки Vault потребуется токен с рутовыми правами адрес Vault дан для примера. В моем случае он вычитывается из Consul.
export VAULT_TOKEN="<ROOT_TOKEN>" export VAULT_ADDR="http://192.168.1.4:8200"
Включить поддержку k8s в Vault
vault auth-enable kubernetes
Получение необходимых секретов из K8S
Так-как в моем случае все PODы находятся в отдельном namespace то:
export NAMESPACE="my-namespace-name"
Получить имя "секрета" для сервисного аккаунта vault-tokenreview-service-accoun (аккаунт под которым Vault работает с K8S API)
export SECRET_NAME=$( \ kubectl \ --namespace ${NAMESPACE} \ get \ serviceaccount \ vault-tokenreview-service-account \ -o jsonpath='{.secrets[0].name}' )
Зная имя секрета можно получить его токен:
export ACCOUNT_TOKEN=$( \ kubectl \ --namespace ${NAMESPACE} \ get \ secret ${SECRET_NAME} \ -o jsonpath='{.data.token}' \ | base64 --decode )
В том же секрете содержится сертификат СА:
export SERVICE_ACCOUNT_CA_CRT=$( \ kubectl \ --namespace ${NAMESPACE} \ get \ secret ${SECRET_NAME} \ -o jsonpath="{.data['ca\.crt']}" \ | base64 --decode; echo )
Настройка доступа Vault в K8S
Настроить vault (адрес API можно взять из конфигурации kubectl)
vault \ write \ auth/kubernetes/config \ kubernetes_host="https://192.168.191.2:443" \ kubernetes_ca_cert="${SERVICE_ACCOUNT_CA_CRT}" \ token_reviewer_jwt="${ACCOUNT_TOKEN}"
Обратить внимание:
- Service Account - это аккаунт с которым будет деплоиться POD. Это аккаунт в k8s а не в Vault.
- Роль существует только в пределах Vault (а не в K8S)
- При авторизации POD запрашивает токен для роли
- На токен будут назначены политики из привязки
Привязка сервисных аккаунтов K8S к ролям Vault
export VAULT_K8S_SERVICE_ACCOUNT="k8s-test-service-account" export VAULT_K8S_ROLE="k8s-test-role"
Здесь VAULT_K8S_SERVICE_ACCOUNT - это сервисный аккаунт в k8s который можно создать например так:
--- apiVersion: v1 kind: ServiceAccount metadata: name: k8s-service-account namespace: my-namespace-name
Важно: Это сервисный аккаунт для запуска PODов, и это НЕ ТОТ сервисный аккаунт который используется Vault
Создать привязку
vault \ write \ auth/kubernetes/role/${VAULT_K8S_ROLE} \ bound_service_account_names=${VAULT_K8S_SERVICE_ACCOUNT} \ bound_service_account_namespaces=${NAMESPACE} \ policies=policy1,policy2,default \ ttl=1h
Обратить внимание - политики policy1,policy2 уже существуют (созданы до этого). Если политик нет их необходимо создать.
Деплоймент в K8S
Примерная конфигурация PODа
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-deployment spec: replicas: 1 selector: matchLabels: app: my-deployment-pod template: metadata: labels: app: my-deployment-pod spec: serviceAccountName: "k8s-test-service-account" containers: - name: my-container-name image: ... imagePullPolicy: Always args: ... env: - name: VAULT_K8S_ROLE value: "k8s-test-role"
Получение токена
Адрес Vault известен (через переменную окружения или из Consul)
Токен сервисного аккаунта монтируется как вольюм
Изнутри контейнера:
export VAULT_ADDR="http://192.168.1.4:8200" export JWT="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
Авторизация используя утилиту vault
vault write auth/kubernetes/login role=k8s-test-role jwt=${JWT}
Key Value --- ----- token s.SyMDLTmAYLtEA13vBVfjqyfq token_accessor 3pQbarDfUWChuIgWZRnnKMMB token_duration 1h token_renewable true token_policies ["policy1" "policy2" "default" ] identity_policies [] policies token_policies ["policy1" "policy2" "default" ] token_meta_service_account_name k8s-test-service-account token_meta_service_account_namespace test-namespace token_meta_service_account_secret_name k8s-test-service-account-token-klp4w token_meta_service_account_uid fe7fc17f-2305-11e9-b347-4201c0a8bf08 token_meta_role k8s-testi-role
Авторизация через Curl
curl \ --request POST \ --data '{"jwt": "'"${JWT}"'", "role": "k8s-test-role"}' \ ${VAULT_ADDR}/v1/auth/kubernetes/login
{ "request_id": "8d524407-ee6b-a6c2-b011-d02572ae2ce9", "lease_id": "", "renewable": false, "lease_duration": 0, "data": null, "wrap_info": null, "warnings": null, "auth": { "client_token": "s.3YsrjrkBZLOXF5sAmR2ceqrs", "accessor": "I532wSMIIXYSiAocKcShEhe7", "policies": [ "policy1", "policy2", "default" ], "token_policies": [ "policy1", "policy2", "default" ], "metadata": { "role": "k8s-test-role", "service_account_name": "k8s-test-service-account", "service_account_namespace": "test-namespace", "service_account_secret_name": "k8s-test-service-account-token-klp4w", "service_account_uid": "fe7fc17f-2305-11e9-b347-4201c0a8bf08" }, "lease_duration": 3600, "renewable": true, "entity_id": "5d4ec129-0298-54a6-bf03-a51dbda0a917", "token_type": "service" } }
Ссылки
- https://coreos.com/tectonic/docs/latest/vault-operator/user/kubernetes-auth-backend.html
- https://medium.com/@gmaliar/dynamic-secrets-on-kubernetes-pods-using-vault-35d9094d169
- https://blog.openshift.com/vault-integration-using-kubernetes-authentication-method/
- https://www.vaultproject.io/docs/auth/kubernetes.html