Vault with k8s v2: различия между версиями
Sirmax (обсуждение | вклад) |
Sirmax (обсуждение | вклад) |
||
Строка 600: | Строка 600: | ||
type service |
type service |
||
</PRE> |
</PRE> |
||
+ | |||
+ | ====Проверить возможность чтения из разрешенных путей key/value==== |
||
+ | |||
root@test-kilda-service-acc:~# vault vault kv get kv/kilda/subpath^C |
root@test-kilda-service-acc:~# vault vault kv get kv/kilda/subpath^C |
||
root@test-kilda-service-acc:~# vault kv get kv/kilda/subpath |
root@test-kilda-service-acc:~# vault kv get kv/kilda/subpath |
||
Строка 630: | Строка 633: | ||
commonkey commonvalue |
commonkey commonvalue |
||
</PRE> |
</PRE> |
||
+ | |||
+ | |||
+ | ====Проверить невозможность чтения из запрещенных путей key/value==== |
||
=111= |
=111= |
Версия 17:09, 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
Провека
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
Получение необходимых секретов из 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
Проверка kilda-service-account
Создание POD
kubectl apply -f pod-kilda-service-account.yaml
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"
Авторизация в Vault
- Все действия из контейнера
kubectl --namespace kilda exec -ti test-kilda-service-acc -- bash
- Получение токена сервисного аккаунта k8
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
- Авторизация в Vault используя роль и токен сервисного аккаунта: получение токена
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
- логин с использованием токена
vault login s.vGsxWHAW9xHzdryEHolw3Xw1
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
- Просмотр токена и его прав ( в том числе политик)
- результат соответсвует ожидаемому: policies [default kilda-policy kv-common-policy]
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
Проверить возможность чтения из разрешенных путей key/value
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
Проверить невозможность чтения из запрещенных путей key/value
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