Vault with k8s v2: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) |
||
Строка 455: | Строка 455: | ||
ttl=1h |
ttl=1h |
||
− | </PRE> |
||
− | |||
− | kubectl apply -f common-service-account.yaml |
||
− | serviceaccount/common-service-account created |
||
− | |||
− | kubectl get serviceAccount --namespace kilda |
||
− | NAME SECRETS AGE |
||
− | common-service-account 1 15s |
||
− | default 1 160m |
||
− | kilda-service-account 1 23s |
||
− | kilda-vault-token-review-service-account 1 155m |
||
</PRE> |
</PRE> |
||
Версия 16:58, 10 февраля 2022
Авторизация K8S POD в Vault
Постановка задачи
- Получать "секреты" из Vault при этом не передавая в POD пароли, токены или другую секретную информацию
Предварительны условия
- Установлен и настроен Vault
(базовая настройка - https://noname.com.ua/mediawiki/index.php/Vault_Basic_Setup)
- кластер Kubernetes установлен и настроен
- kubectl настроен для работы с кластером
Принципы работы
- Создается один или несколько Service Account
- POD запускается в определенном Service Account
- POD может воспользоваться токеном этого Service Account
- В качестве входных данных POD получает НЕ СЕКРЕТНУЮ информацию
- Адрес Vault в переменной окружения VAULT_ADDR
- Имя роли в переменной окружения VAULT_K8S_ROLE
- POD авторизуется в VAULT под ролью переданной в переменной VAULT_K8S_ROLE используя для этого JWT принадлежащий Service Account
- Vault настроен на проверку прав токена PODs в API K8S (Сам процесс VAULT использует ДРУГОЙ сервисный аккаунт, отличный от того под которым запускается POD для проверки токена PODa)
- После успешной авторизации POD может читать "секреты" из VAULT согласно политик которые привязаны к роле (политики и роли тут - это объекты в Vault)
Другими словами в качестве пары логин/пароль выступает роль
(которую POD "знает" из переменных окружения, и которая не является "секретом")
и токен сервисного аккаунта (к которому имеет доступ только POD запущенный с правами этого сервисного аккаунта)
Настройка со стороны k8s
Настройка K8S состоит из следующих шагов:
- Создать отдельное пространство имен (опционально: можно использовать default)
- Создать сервисные аккаунт для проверки токенов и назначить ему права
- Создать сервисные аккаунты для запуска POD-ов
Namespace
- Создать пространство (имен если не создано)
- Имя пространства имен выбрано произвольно (kilda в этом примере)
- kubectl apply -f kilda-namespace.yaml
- Содержимое kilda-namespace.yaml:
--- apiVersion: v1 kind: Namespace metadata: name: kilda
- проверка: kubectl get namespaces
NAME STATUS AGE default Active 56d kilda Active 7s kube-public Active 56d kube-system Active 56d metallb-system Active 56d ...
Сервисный аккаунт для проверки токенов
- Это аккаунт с которым Vault подключается к API k8s для валидации JWT
- Этот аккаунт отличается от того который используется для запуска POD
- Создается в правильном пространстве имен (в частном случае может использоваться default)
- Имя аккаунта выбрано произвольно
- kubectl apply -f kilda-vault-token-review-service-account.yaml
- Содержимое файла kilda-vault-token-review-service-account.yaml:
--- apiVersion: v1 kind: ServiceAccount metadata: name: kilda-vault-token-review-service-account namespace: kilda
- Проверка: kubectl get serviceAccount --namespace kilda
NAME SECRETS AGE default 1 9m12s kilda-vault-token-review-service-account 1 3m37s
Права сервисного аккаунта
- Для того что б аккаунт мог проверить JWT у других сервисных аккаунтов ему назначается ClusterRole system:auth-delegator
Важно: Назначить права нужно в 2 пространствах имен
Если назначить только в kilda-namespace то прав для проверки JWT недостаточно (что совершенно не очевидно из конфигурации)
kilda-namespace
- Права назначаются на аккаунт созданный ранее: kilda-vault-token-review-service-account в kilda-namespace
- kubectl apply -f kilda-vault-token-review-service-account-role-binding-in-kilda-namespace.yaml
- Содержимое файла kilda-vault-token-review-service-account-role-binding-in-kilda-namespace.yaml:
--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kilda-vault-token-review-service-account-role-binding-in-kilda-namespace namespace: kilda roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: kilda-vault-token-review-service-account namespace: kilda
default-namespace
- Права назначаются на аккаунт созданный ранее: kilda-vault-token-review-service-account в default-namespace
- kubectl apply -f kilda-vault-token-review-service-account-role-binding-in-default-namespace.yaml
- Содержимое файла kilda-vault-token-review-service-account-role-binding-in-default-namespace.yaml:
--- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kilda-vault-token-review-service-account-role-binding-in-default-namespace namespace: default roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: kilda-vault-token-review-service-account namespace: kilda-namespace
Проверка
- Проверить: kubectl get clusterRoleBinding
NAME ROLE ... kilda-vault-token-review-service-account-role-binding-in-default-namespace ClusterRole/system:auth-delegator kilda-vault-token-review-service-account-role-binding-in-kilda-namespace ClusterRole/system:auth-delegator ...
Сервисные аккаунты для POD
- Это сервисные аккаунты предназначенные для запуска POD
- Аккаунты будут смаплены на разные политики Vault и иметь разные права доступа (могут читать разные ветки в key/values storage)
- Два аккаунта = common-service-account и kilda-service-account (имена могут быть любые)
common-service-account
- kubectl apply -f common-service-account.yaml
--- apiVersion: v1 kind: ServiceAccount metadata: name: common-service-account namespace: kilda
kilda-service-account
- kubectl apply -f kilda-service-account.yaml
--- apiVersion: v1 kind: ServiceAccount metadata: name: kilda-service-account namespace: kilda
Получение необходимых секретов из K8S
- В примере все объекты находятся в отдельном пространстве имен - kilda
- Токен сервисного аккаунта (нужен для того что бы Vault мог авторизоваться в кластере K8S)
- CA кластера нужен для установки https соединения (СА может быть получен и из конфига kubecl)
Получение имени "секрета"
- Получить имя "секрета" для сервисного аккаунта kilda-vault-token-review-service-account
- kilda-vault-token-review-service-account это аккаунт с которым Vault подключается к API k8s для валидации JWT (создан выше)
kubectl \ --namespace kilda \ get \ serviceaccount \ kilda-vault-token-review-service-account \ -o json
{ "apiVersion": "v1", "kind": "ServiceAccount", "metadata": { "annotations": { "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"annotations\":{},\"name\":\"kilda-vault-token-review-service-account\",\"namespace\":\"kilda\"}}\n" }, "creationTimestamp": "2022-02-09T10:17:40Z", "name": "kilda-vault-token-review-service-account", "namespace": "kilda", "resourceVersion": "8768345", "uid": "33b39f30-ba7e-4ef3-a12d-59e0cee9abee" }, "secrets": [ { "name": "kilda-vault-token-review-service-account-token-sdz49" } ] }
Тут имя секрета =.secrets[0].name, его можно получить одной командой непосредственно используя jq или встроенную в kubectl фильтрацию
kubectl \ --namespace kilda \ get \ serviceaccount \ kilda-vault-token-review-service-account \ -o jsonpath='{.secrets[0].name}'
Результат - kilda-vault-token-review-service-account-token-sdz49
Получение данных из "секрета"
- имя секрета получено на предыдущем шаге - kilda-vault-token-review-service-account-token-sdz49
- не забывать что используется отдельное пространство имен - kilda
Токен
Зная имя секрета можно получить его токен:
kubectl \ --namespace kilda \ get \ secret kilda-vault-token-review-service-account-token-sdz49 \ -o json
- Пример вывода (длинные поля заменены описанием для простоты):
{ "apiVersion": "v1", "data": { "ca.crt": "<тут base64-encoded CA>=", "namespace": "a2lsZGE=", "token": "<тут тут base64-encoded tocken>" }, "kind": "Secret", "metadata": { "annotations": { "kubernetes.io/service-account.name": "kilda-vault-token-review-service-account", "kubernetes.io/service-account.uid": "33b39f30-ba7e-4ef3-a12d-59e0cee9abee" }, "creationTimestamp": "2022-02-09T10:17:40Z", "name": "kilda-vault-token-review-service-account-token-sdz49", "namespace": "kilda", "resourceVersion": "8768344", "uid": "e509252c-3e36-4344-930f-9e988156442c" }, "type": "kubernetes.io/service-account-token" }
Здесь интересует поле .data - можно получить одной командой, не сохраняя промежуточные данные
kubectl --namespace kilda get secret kilda-vault-token-review-service-account-token-sdz49 -o jsonpath='{.data.token}' | base64 -d
Токен выглядит примерно так:
eyJhbGciOiJSU <тут вырезано >MT3cuTbiL7rZr8oiBM331iCpZ6BK-nybCpptg3lnKOnjJbs26HoMGIY2_DvunNo_TiHRzq03h3z344F5SAqgEe27LWtaiGD1xA
CA
- В том же секрете содержится сертификат СА
- Этот же сертификат содержится в конфиге kubectl
kubectl \ --namespace kilda \ get \ secret kilda-vault-token-review-service-account-token-sdz49 \ -o jsonpath="{.data['ca\.crt']}" \ | base64 --decode;
-----BEGIN CERTIFICATE----- MIIDADCCAeigAwIBAgIUXvcRvJKO5x/sOIXOxVD2VIQXY3QwDQYJKoZIhvcNAQEL BQAwGDEWMBQGA1UEAxMNa3ViZXJuZXRlcy1jYTAeFw0yMTEyMTQxNjQ5MDBaFw0z <skipped> eTh8bGZMYcccstj+sPLyprG8bueXzo6hDgaKkz4NXKlgP/b1gB/uycVKuS351mxF qOY4fQ== -----END CERTIFICATE-----
Настройка Vault
- настроить доступ к Vault (авторизация требует рутовых прав)
- создать политики доступа
- включить авторизацию через k8s
Доступ к Vault
Для настройки Vault потребуется токен с рутовыми правами.
- Токен для примера (отличается для каждой инсталляции Vault)
- В случае https - требуется добавить CA в доверенные (если не добавлен до того)
export VAULT_TOKEN="s.pRFenxR9CANXqLtGI0b6fvy3" export VAULT_ADDR="https://vault.domain.tld:8200"
Создание политик доступа
- Cоздаем 2 политики:
- kilda-policy для аккаунта kilda-service-account
- kv-common-policy для аккаунта common-service-account
kilda-policy
Используем предсозданнную политику - https://noname.com.ua/mediawiki/index.php/Vault_Basic_Setup#.D0.9F.D0.BE.D0.BB.D0.B8.D1.82.D0.B8.D0.BA.D0.B8_.D0.B4.D0.BB.D1.8F_KV
kv-common-policy
Аналогично
path "kv/data/common/*" { capabilities = ["read", "list", "create", "update"] } path "kv/data/common/readonly/*" { capabilities = ["read"] } path "kv/data/common/readonly" { capabilities = ["read"] } path "kv/data/common/read_and_update/*" { capabilities = ["read", "update"] } path "kv/data/common/read_and_update" { capabilities = ["read", "update"] } path "kv/data/common/noaccess" { capabilities = ["deny"] }
Просмотр политик
vault policy list
default kilda-policy kv-common-policy root
vault policy read kilda-policy
path "kv/data/kilda/*" { capabilities = ["read", "list", "create", "update"] } path "kv/data/kilda/readonly/*" { capabilities = ["read"] } path "kv/data/kilda/read_and_update/*" { capabilities = ["read", "update"] } path "kv/data/kilda/read_and_update" { capabilities = ["read", "update"] } path "kv/data/kilda/noaccess" { capabilities = ["deny"] }
Включить поддержку k8s в Vault
- Предполагается использование нескольких кластеров с одним Vault, для этого указывается отдельный путь -path kilda-fred для каждого кластера kubernetes.
- Имя пути (в примере kilda-fred) может быть выбрано произвольно
vault auth enable -path kilda-fred kubernetes Success! Enabled kubernetes auth method at: kilda-fred/
- проверка
vault auth list Path Type Accessor Description ---- ---- -------- ----------- kilda-fred/ kubernetes auth_kubernetes_34db5104 n/a ...
Настройка доступа Vault в K8S
На этом шаге указывается к какому кластеру "ходить" за проверкой, и с каким токеном авторизоваться в этом кластере
- ACCOUNT_TOKEN - получен на предыдущем шаге
- API - адрес API kubernetes
- SERVICE_ACCOUNT_CA_CRT - CA (получен на предыдущем шаге или взят из конфигурации kubectl - это один и тот же CA)
- kilda-fred - это путь указаный ранее
#!/bin/bash ACCOUNT_TOKEN="eyJhbGci<skipped>uZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImtpbGRhLXZhd API="https://https://vault.domain.tld::6443" SERVICE_ACCOUNT_CA_CRT="-----BEGIN CERTIFICATE----- MIIDADCCAeigAwIBAgIUXvcRvJKO5x/sOIXOxVD2VIQXY3QwDQYJKoZIhvcNAQEL <Skipped> PVGSuNobYcdns4Z+E1O2QLYbnoDsLd+sqf0r9aaFSZQZry8bP783VCuBXg6Zy0BR eTh8bGZMYcccstj+sPLyprG8bueXzo6hDgaKkz4NXKlgP/b1gB/uycVKuS351mxF qOY4fQ== -----END CERTIFICATE----- " vault \ write \ auth/kilda-fred/config \ kubernetes_host="${API}" \ kubernetes_ca_cert="${SERVICE_ACCOUNT_CA_CRT}" \ token_reviewer_jwt="${ACCOUNT_TOKEN}"
Success! Data written to: auth/kilda-fred/config
Назначение политик Vault для сервисных аккаунтов
- назначаются политики на 2 сервисных аккауната kilda-service-account и common-service-account
- Service Account - это аккаунт с которым будет деплоиться POD. Это аккаунт в k8s а не в Vault. (созданы при настройке k8s выше)
- Роль существует только в пределах Vault (а не в K8S)
- При авторизации POD запрашивает токен Vault для роли используя для авторизации токен service account из k8s
- На токен vault будут назначены политики vault из роли
Другими словами в качестве пары логин/пароль выступает роль (которую POD "знает" из переменных окружения, и которая не является "секретом") и токен сервисного аккаунта (к которому имеет доступ только POD запущенный с правами этого сервисного аккаунта)
kilda-service-account
Назначаются 3 политики vault : kilda-policy,kv-common-policy,default
export NAMESPACE="kilda" export VAULT_K8S_SERVICE_ACCOUNT="kilda-service-account" export VAULT_K8S_ROLE="kilda-role" export POLICIES="kilda-policy,kv-common-policy,default" vault \ write \ auth/kilda-fred/role/${VAULT_K8S_ROLE} \ bound_service_account_names=${VAULT_K8S_SERVICE_ACCOUNT} \ bound_service_account_namespaces=${NAMESPACE} \ policies=kilda-policy,kv-common-policy,default \
common-service-account
export NAMESPACE="kilda" export VAULT_K8S_SERVICE_ACCOUNT="common-service-account" export VAULT_K8S_ROLE="common-role" export POLICIES="kv-common-policy,default" vault \ write \ auth/kilda-fred/role/${VAULT_K8S_ROLE} \ bound_service_account_names=${VAULT_K8S_SERVICE_ACCOUNT} \ bound_service_account_namespaces=${NAMESPACE} \ policies=kv-common-policy,default \ ttl=1h
Тестирование
План тестирования
- запустить POD в сервисном аккаунте kilda-service-account
- авторизоваться в Vault используя токен сервисного аккаунта
- Проверить возможность чтения из разрешенных путей key/value
- Проверить невозможность чтения из запрещенных путей key/value
- запустить POD в сервисном аккаунте common-service-account
- авторизоваться в Vault используя токен сервисного аккаунта
- Проверить возможность чтения из разрешенных путей key/value
- Проверить невозможность чтения из запрещенных путей key/value
vault kv put kv/common/somepath commonkey=commonvalue Key Value --- ----- created_time 2022-02-09T14:23:30.169927438Z custom_metadata <nil> deletion_time n/a destroyed false
Создание POD
kubectl get pod --namespace kilda
NAME READY STATUS RESTARTS AGE test-common-service-acc 1/1 Running 0 13s test-kilda-service-acc 1/1 Running 0 51m
apiVersion: v1 kind: Pod metadata: name: test-kilda-service-acc namespace: kilda labels: role: test spec: serviceAccountName: "kilda-service-account" containers: - name: web image: ubuntu:20.04 imagePullPolicy: Always command: ["sleep", "3600000"] env: - name: VAULT_K8S_ROLE value: "kilda-role" - name: VAULT_ADDR value: "https://vault.domain.tld:8200"
kubectl apply -f pod-kilda-service-account.yaml pod/test-kilda-service-acc created
kubectl --namespace kilda exec -ti test-kilda-service-acc -- bash
export JWT="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
env | grep VAULT VAULT_K8S_ROLE=kilda-role VAULT_ADDR=https://vault.domain.tld:8200 VAULT_K8S_ROLE=kilda-role VAULT_AUTH_PATH=kilda-fred
root@test-kilda-service-acc:~# vault write auth/kilda-fred/login role=${VAULT_K8S_ROLE} jwt=${JWT} Key Value --- ----- token s.vGsxWHAW9xHzdryEHolw3Xw1 token_accessor x4JGArK99k6wkh9QqgPE6dwF token_duration 1h token_renewable true token_policies ["default" "kilda-policy" "kv-common-policy"] identity_policies [] policies ["default" "kilda-policy" "kv-common-policy"] token_meta_role kilda-role token_meta_service_account_name kilda-service-account token_meta_service_account_namespace kilda token_meta_service_account_secret_name n/a token_meta_service_account_uid 50b15622-38d9-4725-96b1-1c56f4ca0da1 root@test-kilda-service-acc:~# vault token lookup Error looking up token: Error making API request. URL: GET https://vault.fred.in.pn.telstra.com:8200/v1/auth/token/lookup-self Code: 400. Errors: * missing client token root@test-kilda-service-acc:~# vault login s.vGsxWHAW9xHzdryEHolw3Xw1 Success! You are now authenticated. The token information displayed below is already stored in the token helper. You do NOT need to run "vault login" again. Future Vault requests will automatically use this token. Key Value --- ----- token s.vGsxWHAW9xHzdryEHolw3Xw1 token_accessor x4JGArK99k6wkh9QqgPE6dwF token_duration 59m26s token_renewable true token_policies ["default" "kilda-policy" "kv-common-policy"] identity_policies [] policies ["default" "kilda-policy" "kv-common-policy"] token_meta_role kilda-role token_meta_service_account_name kilda-service-account token_meta_service_account_namespace kilda token_meta_service_account_secret_name n/a token_meta_service_account_uid 50b15622-38d9-4725-96b1-1c56f4ca0da1 root@test-kilda-service-acc:~# vault token lookup Key Value --- ----- accessor x4JGArK99k6wkh9QqgPE6dwF creation_time 1644416277 creation_ttl 1h display_name kilda-fred-kilda-kilda-service-account entity_id ab9837eb-ff5c-71e4-cb78-16065ef5a7eb expire_time 2022-02-09T15:17:57.790015655Z explicit_max_ttl 0s id s.vGsxWHAW9xHzdryEHolw3Xw1 issue_time 2022-02-09T14:17:57.79003263Z meta map[role:kilda-role service_account_name:kilda-service-account service_account_namespace:kilda service_account_secret_name: service_account_uid:50b15622-38d9-4725-96b1-1c56f4ca0da1] num_uses 0 orphan true path auth/kilda-fred/login policies [default kilda-policy kv-common-policy] renewable true ttl 59m21s type service root@test-kilda-service-acc:~# vault vault kv get kv/kilda/subpath^C root@test-kilda-service-acc:~# vault kv get kv/kilda/subpath ======= Metadata ======= Key Value --- ----- created_time 2022-02-08T14:59:04.275447933Z custom_metadata <nil> deletion_time n/a destroyed false version 1 ===== Data ===== Key Value --- ----- somekey somedata root@test-kilda-service-acc:~# vault kv get kv/common/somepath ======= Metadata ======= Key Value --- ----- created_time 2022-02-09T14:23:30.169927438Z custom_metadata <nil> deletion_time n/a destroyed false version 1 ====== Data ====== Key Value --- ----- commonkey commonvalue
111
--- apiVersion: v1 kind: ServiceAccount metadata: name: kilda-service-account namespace: kilda
--- apiVersion: v1 kind: ServiceAccount metadata: name: common-service-account namespace: kilda
kubectl apply -f kilda-service-account.yaml serviceaccount/kilda-service-account created