![](https://habrastorage.org/webt/ge/q3/ey/geq3eyqujybgzgmalsngtz3azuu.png)
Все верно, после релиза Hashicorp Consul 1.5.0 в начале мая 2019 года в Consul можно делать авторизацию приложений и служб, запущенных в Kubernetes, нативно.
В этом руководстве мы шаг за шагом создадим POC (Проверка концепции (Proof of concept, PoC — доказательство [осуществимости] концепции — прим. перев.), продемонстрировав эту новую функцию. От вас ожидаются базовые знания о Kubernetes и Hashicorp’s Consul. И хотя вы можете использовать любую облачную платформу или локальную среду, в этом руководстве мы будем использовать Google’s Cloud Platform.
Обзор
Если мы перейдем к документации Consul по его методу авторизации, мы получим краткий обзор его назначения и варианта использования, а также некоторые технические детали и общий обзор логики. Я настоятельно рекомендую прочитать ее по крайней мере один раз, прежде чем продолжить, так как сейчас я буду все это объяснять и разжевывать.
![](https://habrastorage.org/webt/ca/xp/mp/caxpmprm5u-8bupb1_fpjgehvcw.png)
Схема 1: Официальный обзор метода авторизации Consul
Давайте посмотрим в документацию для конкретного метода авторизации Kubernetes.
Конечно, там есть полезная информация, но нет руководства о том, как на самом деле все это использовать. Поэтому, как любой здравомыслящий человек, вы прочесываете Интернет в поисках руководства. А потом… Терпите поражение. Так бывает. Давайте это исправим.
Прежде чем мы перейдем к созданию нашего POC, давайте вернемся к обзору методов авторизации Consul (Схема 1) и уточним его в контексте Kubernetes.
Архитектура
В этом руководстве мы будем создавать Consul-сервер на отдельной машине, которая будет взаимодействовать с кластером Kubernetes с установленным клиентом Consul. Затем мы создадим наше фиктивное приложение в поде и используем наш настроенный метод авторизации для чтения из нашего Consul key/value хранилища.
Схема ниже детально показывает архитектуру, которую мы создаем в этом руководстве, а также логику метода авторизации, которая будет объяснена позже.
![](https://habrastorage.org/webt/az/zn/kj/azznkjy-wrxrndezbwxezarbs-4.png)
Схема 2: Обзор метода авторизации в Kubernetes
Небольшое замечание: Consul-серверу не нужно жить за пределами кластера Kubernetes, чтобы это работало. Но да, он может и так и сяк.
Итак, взяв обзорную схему Consul (Схема 1) и применив к ней Kubernetes, мы получаем схему выше (Схема 2), и тут логика будет следующая:
- К каждому поду будет прикреплена служебная учетная запись, содержащая токен JWT, сгенерированный и известный Kubernetes. Этот токен также вставляется в под по умолчанию.
- Наше приложение или сервис внутри пода инициирует команду входа в наш Consul-клиент. В запросе на вход в систему также будет указан наш токен и указано имя специально созданного метода авторизации (типа Kubernetes). Этот шаг № 2 соответствует шагу 1 схемы Consul (Схема 1).
- Наш Consul-клиент затем направит этот запрос на наш Consul-сервер.
- МАГИЯ! Именно здесь Consul-сервер проверяет подлинность запроса, собирает сведения об идентичности запроса и сравнивает их с какими-нибудь ассоциированными предопределенными правилами. Ниже будет другая схема, чтобы это проиллюстрировать. Этот шаг соответствует шагам 3, 4 и 5 обзорной схемы Consul (Схема 1).
- Наш Consul-сервер генерирует Consul токен с разрешениями в соответствии с указанными нами правилами метода авторизации (которые мы определили) в отношении личности запрашивающего. Затем он отправит этот токен обратно. Это соответствует шагу 6 схемы Consul (Схема 1).
- Наш Consul-клиент перенаправляет токен запрашивающему приложению или сервису.
Наше приложение или сервис теперь могут использовать этот Consul токен для связи с нашими данными Consul, как было определено привилегиями токена.
Волшебство раскрыто!
Для тех из вас, кто не доволен всего лишь кроликом из шляпы и хочет знать, как это работает… позвольте мне «показать вам, насколько глубока кроличья нора».
Как упоминалось ранее, наш «магический» шаг (Схема 2: Шаг 4) заключается в том, что Consul-сервер проверяет подлинность запроса, собирает сведения об запросе и сравнивает их с любыми ассоциированными предопределенными правилами. Этот шаг соответствует шагам 3, 4 и 5 обзорной схемы Consul (Схема 1). Ниже приведена схема (Схема 3), цель которой наглядно показать, что на самом деле происходит под капотом конкретного метода авторизации Kubernetes.
![](https://habrastorage.org/webt/5e/5w/eg/5e5wegybhhtjkwkotfs1disgzc8.png)
Схема 3: Волшебство раскрыто!
- В качестве отправной точки, наш Consul-клиент перенаправляет запрос на вход на наш Consul-сервер с токеном учетной записи Kubernetes и конкретным именем инстанса метода авторизации, который был создан ранее. Этот шаг соответствует шагу 3 в предыдущем объяснении схемы.
- Теперь Consul-серверу (или лидеру) необходимо проверить подлинность полученного токена. Поэтому он проконсультируется с кластером Kubernetes (через Consul-клиент) и, при наличии соответствующих разрешений, мы выясним, является ли токен подлинным, и кому он принадлежит.
- Затем проверенный запрос возвращается к Consul-лидеру, и на сервере Consul выполняется поиск инстанса метода авторизации с указанным именем из запроса на вход в систему (и типа Kubernetes).
- Consul-лидер определяет указанный инстанс метода авторизации (если он найден) и читает набор правил привязки, которые к нему прикреплены. Затем он читает эти правила и сравнивает их с проверенными атрибутами идентичности.
- Тада! Переходим к шагу 5 в предыдущем объяснении схемы.
Запустите Consul-server на обычной виртуальной машине
С этого момента я в основном буду давать инструкции по созданию этого POC, часто в пунктах, без пояснительных целых предложений. Также, как отмечалось ранее, я буду использовать GCP для создания всей инфраструктуры, но вы можете создать такую же инфраструктуру в любом другом месте.
- Запустите виртуальную машину (инстанс / сервер).
![](https://habrastorage.org/webt/gg/5m/j-/gg5mj-dyw9frdaglrfwizvbkna8.png)
- Создайте правило для firewall (группа безопасности в AWS):
- Мне нравится присваивать одно и то же имя машины правилу и сетевому тегу, в данном случае это «skywiz-consul-server-poc».
- Найдите IP-адрес вашего локального компьютера и добавьте его в список исходных IP-адресов, чтобы мы могли получить доступ к интерфейсу пользователя (UI).
- Откройте порт 8500 для UI. Нажмите Create (Создать). Мы скоро опять изменим этот firewall [ссылка].
- Добавьте правило для firewall к инстансу. Вернитесь на панель мониторинга VM на Consul-сервере и добавьте «skywiz-consul-server-poc» в поле сетевых тегов. Нажмите Save (Сохранить).
![](https://habrastorage.org/webt/o3/7i/52/o37i52gc_8k8fxu4ve113oh7ljc.png)
- Установите Consul на виртуальную машину, проверьте здесь. Помните, что вам нужна версия Consul ≥ 1.5 [ссылка]
- Создадим single node Consul — конфигурация следующая.
groupadd --system consul
useradd -s /sbin/nologin --system -g consul consul
mkdir -p /var/lib/consul
chown -R consul:consul /var/lib/consul
chmod -R 775 /var/lib/consul
mkdir /etc/consul.d
chown -R consul:consul /etc/consul.d
- Более подробное руководство по установке Consul и настройке кластера из 3 нод см. здесь.
- Создайте файл /etc/consul.d/agent.json следующим образом [ссылка]:
### /etc/consul.d/agent.json
{
"acl" : {
"enabled": true,
"default_policy": "deny",
"enable_token_persistence": true
}
}
- Запустите наш Consul-сервер:
consul agent \
-server \
-ui \
-client 0.0.0.0 \
-data-dir=/var/lib/consul \
-bootstrap-expect=1 \
-config-dir=/etc/consul.d
- Вы должны увидеть кучу выходных данных и в конечном итоге “… update blocked by ACLs”.
- Найдите внешний IP-адрес Consul-сервера и откройте браузер с этим IP-адресом на порту 8500. Убедитесь, что открывается UI.
- Попробуйте добавить пару ключ/значение. Должна быть ошибка. Это потому, что мы загрузили Consul-сервер с помощью ACL и запретили все правила.
- Вернитесь к своей оболочке на Consul-сервере и запустите процесс в фоновом режиме или каким-либо другим способом, чтобы он работал, и введите следующее:
consul acl bootstrap
- Найдите значение «SecretID» и вернитесь к UI. На вкладке «ACL» введите секретный идентификатор токена, который вы только что скопировали. Скопируйте SecretID еще куда-нибудь, он понадобится нам позже.
- Теперь добавьте пару ключ/значение. Для этого POC добавим следующее: ключ: «custom-ns/test_key», значение: «I’m in the custom-ns folder!»
Запуск Kubernetes-кластера для нашего приложения с Consul-клиентом в качестве Daemonset
- Создайте кластер K8s (Kubernetes). Мы создадим его в той же зоне, что и сервер, для более быстрого доступа, и поэтому мы можем использовать ту же подсеть для простого подключения с внутренними IP-адресами. Мы назовем его «skywiz-app-with-consul-client-poc».
![](https://habrastorage.org/webt/_l/vp/gu/_lvpgupywaktqzykauebirhibm0.png)
- Как примечание, вот хорошее руководство, с которым я столкнулся при настройке POC Consul-кластера с Consul Connect.
- Мы также будем использовать Hashicorp helm chart с расширенным файлом значений.
- Установите и сконфигурируйте Helm. Шаги конфигурации:
kubectl create serviceaccount tiller --namespace kube-system
kubectl create clusterrolebinding tiller-admin-binding \
--clusterrole=cluster-admin --serviceaccount=kube-system:tiller
./helm init --service-account=tiller
./helm update
- helm chart: https://www.consul.io/docs/platform/k8s/helm.html
- Используйте следующий файл значений (обратите внимание, большинство я отключил):
### poc-helm-consul-values.yaml
global:
enabled: false
image: "consul:latest"
# Expose the Consul UI through this LoadBalancer
ui:
enabled: false
# Allow Consul to inject the Connect proxy into Kubernetes containers
connectInject:
enabled: false
# Configure a Consul client on Kubernetes nodes. GRPC listener is required for Connect.
client:
enabled: true
join: ["<PRIVATE_IP_CONSUL_SERVER>"]
extraConfig: |
{
"acl" : {
"enabled": true,
"default_policy": "deny",
"enable_token_persistence": true
}
}
# Minimal Consul configuration. Not suitable for production.
server:
enabled: false
# Sync Kubernetes and Consul services
syncCatalog:
enabled: false
- Примените helm chart:
./helm install -f poc-helm-consul-values.yaml ./consul-helm - name skywiz-app-with-consul-client-poc
- При попытке запуска, ему понадобятся разрешения для Consul-сервера, так что давайте добавим их.
- Обратите внимание на «диапазон адресов Pod», расположенный на приборной панели кластера, и вернитесь к нашему правилу для firewall «skywiz-consul-server-poc».
- Добавьте диапазон адресов для пода в список IP-адресов и откройте порты 8301 и 8300.
![](https://habrastorage.org/webt/gh/wu/bi/ghwubingk53amwe3qnbiendjgvm.png)
- Перейдите к Consul UI, и через несколько минут вы увидите, что наш кластер появится на вкладке нод.
![](https://habrastorage.org/webt/j1/l_/h5/j1l_h5fj5iusg3ea3rxc7qc_sb8.png)
Настройка метода авторизации путем интеграции Consul с Kubernetes
- Вернитесь в оболочку Consul-сервера и экспортируйте токен, который вы сохранили ранее:
export CONSUL_HTTP_TOKEN=<SecretID>
- Нам понадобится информация из нашего Kubernetes-кластера, чтобы создать инстанс метода auth:
- kubernetes-host
kubectl get endpoints | grep kubernetes
- kubernetes-service-account-jwt
kubectl get sa <helm_deployment_name>-consul-client -o yaml | grep "\- name:"
kubectl get secret <secret_name_from_prev_command> -o yaml | grep token:
- Токен закодирован в base64, поэтому расшифруйте его с помощью вашего любимого инструмента [ссылка]
- kubernetes-ca-cert
kubectl get secret <secret_name_from_prev_command> -o yaml | grep ca.crt:
- Возьмите сертификат “ca.crt” (после декодирования с base64) и впишите его в файл “ca.crt”.
- Теперь создайте инстанс метода auth, заменив placeholders на значения, которые вы только что получили.
consul acl auth-method create \
-type "kubernetes" \
-name "auth-method-skywiz-consul-poc" \
-description "This is an auth method using kubernetes for the cluster skywiz-app-with-consul-client-poc" \
-kubernetes-host "<k8s_endpoint_retrieved earlier>" \
-kubernetes-ca-cert=@ca.crt \
-kubernetes-service-account-
jwt="<decoded_token_retrieved_earlier>"
- Далее мы должны создать правило и прикрепить его к новой роли. Для этой части вы можете использовать Consul UI, но мы будем использовать командную строку.
- Напишите правило
### kv-custom-ns-policy.hcl
key_prefix "custom-ns/" {
policy = "write"
}
- Примените правило
consul acl policy create \
-name kv-custom-ns-policy \
-description "This is an example policy for kv at custom-ns/" \
-rules @kv-custom-ns-policy.hcl
- Найдите идентификатор правила, которое вы только что создали, из выходных данных.
- Создайте роль с новым правилом.
consul acl role create \
-name "custom-ns-role" \
-description "This is an example role for custom-ns namespace" \
-policy-id <policy_id>
- Теперь мы свяжем нашу новую роль с инстансом метода auth. Обратите внимание, что флаг «селектор» определяет, будет ли наш запрос на вход получать эту роль. Проверьте здесь другие опции селектора: https://www.consul.io/docs/acl/auth-methods/kubernetes.html#trusted-identity-attributes
consul acl binding-rule create \
-method=auth-method-skywiz-consul-poc \
-bind-type=role \
-bind-name='custom-ns-role' \
-selector='serviceaccount.namespace=="custom-ns"'
Конфигурации напоследок
Права доступа
- Создайте права доступа. Нам нужно дать разрешение Consul на проверку и идентификацию удостоверения токена учетной записи службы K8s.
- Запишите в файл следующее [ссылку]:
###skywiz-poc-consul-server_rbac.yaml
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: review-tokens
namespace: default
subjects:
- kind: ServiceAccount
name: skywiz-app-with-consul-client-poc-consul-client
namespace: default
roleRef:
kind: ClusterRole
name: system:auth-delegator
apiGroup: rbac.authorization.k8s.io
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: service-account-getter
namespace: default
rules:
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: get-service-accounts
namespace: default
subjects:
- kind: ServiceAccount
name: skywiz-app-with-consul-client-poc-consul-client
namespace: default
roleRef:
kind: ClusterRole
name: service-account-getter
apiGroup: rbac.authorization.k8s.io
- Создадим права доступа
kubectl create -f skywiz-poc-consul-server_rbac.yaml
Подключение к Consul Client
- Как отмечалось здесь, есть несколько опций для подключения к daemonset, но мы перейдем к следующему простому решению:
- Примените следующий файл [ссылка].
### poc-consul-client-ds-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: consul-ds-client
spec:
selector:
app: consul
chart: consul-helm
component: client
hasDNS: "true"
release: skywiz-app-with-consul-client-poc
ports:
- protocol: TCP
port: 80
targetPort: 8500
- Затем примените следующую встроенную команду для создания configmap [ссылка]. Обратите внимание, что мы ссылаемся на название нашего сервиса, замените его при необходимости.
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
labels:
addonmanager.kubernetes.io/mode: EnsureExists
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"consul": ["$(kubectl get svc consul-ds-client -o jsonpath='{.spec.clusterIP}')"]}
EOF
Тестирование auth-метода
Теперь давайте посмотрим на магию в действии!
- Создайте еще несколько ключевых папок с тем же ключом верхнего уровня (т. е. <new_folder>/sample_key) и значением по вашему выбору. Создайте соответствующие политики и роли для новых ключевых путей. Мы сделаем привязки позже.
![](https://habrastorage.org/webt/qm/yi/jn/qmyijnz6f7yy4kx-3l6owupoulk.png)
Пользовательский тест пространства имен:
- Создадим наше собственное пространство имен:
kubectl create namespace custom-ns
- Создадим под в нашем новом пространстве имен. Напишите конфигурацию для пода.
###poc-ubuntu-custom-ns.yaml
apiVersion: v1
kind: Pod
metadata:
name: poc-ubuntu-custom-ns
namespace: custom-ns
spec:
containers:
- name: poc-ubuntu-custom-ns
image: ubuntu
command: ["/bin/bash", "-ec", "sleep infinity"]
restartPolicy: Never
- Создайте под:
kubectl create -f poc-ubuntu-custom-ns.yaml
- Как только контейнер запустится, зайдите туда и установите curl.
kubectl exec poc-ubuntu-custom-ns -n custom-ns -it /bin/bash
apt-get update && apt-get install curl -y
- Теперь мы отправим запрос на вход в Consul, используя метод авторизации, который мы создали ранее [ссылка].
- Чтобы просмотреть введенный токен из своей учетной записи службы:
cat /run/secrets/kubernetes.io/serviceaccount/token
- Напишите следующее в файл внутри контейнера:
### payload.json
{
"AuthMethod": "auth-method-test",
"BearerToken": "<jwt_token>"
}
- Login!
curl \
--request POST \
--data @payload.json \
consul-ds-client.default.svc.cluster.local/v1/acl/login
- Чтобы выполнить вышеуказанные шаги в одной строке (поскольку мы будем выполнять несколько тестов), вы можете сделать следующее:
echo "{ \
\"AuthMethod\": \"auth-method-skywiz-consul-poc\", \
\"BearerToken\": \"$(cat /run/secrets/kubernetes.io/serviceaccount/token)\" \
}" \
| curl \
--request POST \
--data @- \
consul-ds-client.default.svc.cluster.local/v1/acl/login
- Работает! Должно, по крайней мере. Теперь возьмите SecretID и попробуйте получить доступ к ключу/значению, к которому у нас должен быть доступ.
curl \
consul-ds-client.default.svc.cluster.local/v1/kv/custom-ns/test_key --header “X-Consul-Token: <SecretID_from_prev_response>”
- Вы можете декодировать «Value» base64 и увидеть, что оно соответствует значению в custom-ns/test_key в UI. Если вы использовали то же значение, приведенное выше в этом руководстве, ваше кодированное значение будет IkknbSBpbiB0aGUgY3VzdG9tLW5zIGZvbGRlciEi.
Тест учетной записи пользовательской службы:
- Создайте пользовательский ServiceAccount с помощью следующей команды [ссылка].
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: custom-sa
EOF
- Создайте новый файл конфигурации для пода. Обратите внимание, что я включил установку curl для экономии труда :)
###poc-ubuntu-custom-sa.yaml
apiVersion: v1
kind: Pod
metadata:
name: poc-ubuntu-custom-sa
namespace: default
spec:
serviceAccountName: custom-sa
containers:
- name: poc-ubuntu-custom-sa
image: ubuntu
command: ["/bin/bash","-ec"]
args: ["apt-get update && apt-get install curl -y; sleep infinity"]
restartPolicy: Never
- После этого запустите оболочку внутри контейнера.
kubectl exec -it poc-ubuntu-custom-sa /bin/bash
- Login!
echo "{ \
\"AuthMethod\": \"auth-method-skywiz-consul-poc\", \
\"BearerToken\": \"$(cat /run/secrets/kubernetes.io/serviceaccount/token)\" \
}" \
| curl \
--request POST \
--data @- \
consul-ds-client.default.svc.cluster.local/v1/acl/login
- Permission denied. О, мы забыли добавить новую привязку правил с соответствующими разрешениями, давайте сделаем это сейчас.
Повторите предыдущие шаги выше:
а) Создайте идентичную Политику для префикса «custom-sa/».
б) Создайте Role, назовите ее «custom-sa-role»
в) Прикрепите Политику к Role.
- Создайте Rule-Binding (возможно только из cli / api). Обратите внимание на другое значение флага селектора.
consul acl binding-rule create \
-method=auth-method-skywiz-consul-poc \
-bind-type=role \
-bind-name='custom-sa-role' \
-selector='serviceaccount.name=="custom-sa"'
- Повторите вход в систему из контейнера «poc-ubuntu-custom-sa». Success!
- Проверьте наш доступ к ключевому пути custom-sa/.
curl \
consul-ds-client.default.svc.cluster.local/v1/kv/custom-sa/test_key --header “X-Consul-Token: <SecretID>”
- Вы также можете убедиться, что этот токен не предоставляет доступ к kv в «custom-ns/». Просто повторите приведенную выше команду после замены «custom-sa» на префикс «custom-ns».
Permission denied.
Пример оверлея:
- Стоит отметить, что все сопоставления rule-binding будут добавлены в токен с этими правами.
- Наш контейнер «poc-ubuntu-custom-sa» находится в пространстве имен по умолчанию — так что давайте использовать его для другой rule-binding.
- Повторите предыдущие шаги:
а) Создайте идентичную Политику для префикса ключа «default/».
б) Создайте Role, назовите ее “default-ns-role”
в) Прикрепите Политику к Role. - Создайте Rule-Binding (возможно только из cli / api)
consul acl binding-rule create \
-method=auth-method-skywiz-consul-poc \
-bind-type=role \
-bind-name='default-ns-role' \
-selector='serviceaccount.namespace=="default"'
- Вернитесь к нашему контейнеру «poc-ubuntu-custom-sa» и попробуйте получить доступ к пути «default/» kv.
- Permission denied.
Вы можете просмотреть указанные учетные данные для каждого токена в UI в разделе ACL > Tokens. Как видите, к нашему текущему токену прикреплена только одна «custom-sa-role». Токен, который мы используем в настоящее время, был сгенерирован, когда мы вошли в систему, и тогда было только одно rule-binding, которое тогда соответствовало. Нам нужно снова войти в систему и использовать новый токен. - Убедитесь, что вы можете читать как из путей «custom-sa/», так и «default/» kv.
Success!
Это связано с тем, что наш «poc-ubuntu-custom-sa» соответствует привязкам правил «custom-sa» и «default-ns».
Заключение
TTL token mgmt?
На момент написания этой статьи не существует интегрированного способа определения TTL для токенов, сгенерированных этим методом авторизации. Это была бы фантастическая возможность — обеспечить безопасную автоматизацию авторизации Consul.
Существует возможность вручную создать токен с TTL:
- https://www.consul.io/docs/acl/acl-system.html#acl-tokens
Expiration Time (Время истечения) — время, когда этот токен будет отозван. (Необязательно; добавлено в Consul 1.5.0) - Существует только для ручного создания / обновления https://www.consul.io/api/acl/tokens.html#expirationtime
Надеюсь, в ближайшее время мы сможем контролировать, как генерируются токены (для каждого правила или метода авторизации), и добавлять TTL.
До тех пор предлагается использовать в своей логике конечную точку выхода из системы.
- https://www.consul.io/api/acl/acl.html#logout-from-auth-method
- https://www.consul.io/docs/acl/acl-auth-methods.html#overall-login-process
Также читайте другие статьи в нашем блоге:
- К чему привела миграция с ClickHouse без авторизации на ClickHouse с авторизацией
- Как запустить несколько пайплайнов с помощью GitLab CI/CD
- Три простых приема для уменьшения Docker-образов
- Traefik как Ingress-контроллер для K8S
- Резервное копирование большого количества разнородных web-проектов
- Telegram-бот для Redmine. Как упростить жизнь себе и людям