- PVSM.RU - https://www.pvsm.ru -
Прим. перев.: Первая часть [1] этого цикла была посвящена знакомству с возможностями Istio и их демонстрации в действии, вторая [2] — тонко настраиваемой маршрутизации и управлению сетевым трафиком. Теперь же речь пойдёт про безопасность: для демонстрации связанных с ней базовых функций автор использует identity-сервис Auth0, однако по аналогии с ним могут настраиваться и другие провайдеры.
Мы настроили Kubernetes-кластер, в котором развернули Istio и пример микросервисного приложения Sentiment Analysis, — так были продемонстрированы возможности Istio.
С помощью Istio нам удалось сохранить небольшой размер сервисов, поскольку они не нуждаются в реализации таких «слоёв», как повторные попытки подключения (Retries), таймауты (Timeouts), автоматический выключатели (Circuit Breakers), трассировка (Tracing), мониторинг (Monitoring). Кроме того, мы задействовали техники продвинутого тестирования и деплоя: A/B-тестирование, зеркалирование и канареечные выкаты.
В новом материале мы разберёмся с финальными слоями на пути к business value: аутентификацией и авторизацией — и в Istio это сплошное удовольствие!
Никогда бы не поверил, что вдохновлюсь аутентификацией и авторизацией. Что же такого с технологической точки зрения может предложить Istio для того, чтобы сделать эти темы увлекательными и даже более того — чтобы они вдохновили и вас?
Ответ прост: Istio переносит ответственность за эти возможности с ваших сервисов на прокси Envoy. Ко времени, когда запросы достигают сервисов, они уже аутентифицированы и авторизованы, так что вам остаётся просто писать полезный для бизнеса код.
Звучит неплохо? Заглянем же внутрь!
В качестве сервера для управления идентификацией и доступом будем использовать Auth0, у которого есть пробная версия, который интуитивно понятен в использовании и попросту нравится мне. Впрочем, те же самые принципы можно применить и по отношению к любой другой реализации OpenID Connect [3]: KeyCloak, IdentityServer и многим другим.
Для начала зайдите на Auth0 Portal [4] со своим аккаунтом, создайте tenant (tenant — «арендатор», логическая единица изоляции, подробнее см. в документации [5] — прим. перев.) и зайдите в Applications > Default App, выбрав Domain, как показано на скриншоте ниже:
Укажите этот домен в файле resource-manifests/istio/security/auth-policy.yaml
(исходник [6]):
apiVersion: authentication.istio.io/v1alpha1
kind: Policy
metadata:
name: auth-policy
spec:
targets:
- name: sa-web-app
- name: sa-feedback
origins:
- jwt:
issuer: "https://{YOUR_DOMAIN}/"
jwksUri: "https://{YOUR_DOMAIN}/.well-known/jwks.json"
principalBinding: USE_ORIGIN
Располагая таким ресурсом, Pilot (один из трёх базовых компонентов Control Plane в Istio — прим. перев.) настраивает Envoy'и на аутентификацию запросов перед тем, как перенаправлять их на сервисы: sa-web-app
и sa-feedback
. В то же самое время конфигурация не применяется к Envoy'ям сервиса sa-frontend
, позволяя нам оставить фронтенд неаутентифицированным. Чтобы применить политику (Policy), выполните команду:
$ kubectl apply -f resource-manifests/istio/security/auth-policy.yaml
policy.authentication.istio.io “auth-policy” created
Вернитесь на страницу и сделайте запрос — увидите, что он закончится статусом 401 Unauthorized. Теперь перенаправим пользователей фронтенда на аутентификацию с Auth0.
Чтобы аутентифицировать запросы конечного пользователя, необходимо создать API в Auth0, который будет представлять аутентифицированные сервисы (reviews, details и ratings). Для создания API перейдите в Auth0 Portal > APIs > Create API и заполните форму:
Важной информацией здесь является Identifier, который позже мы будем использовать в скрипте. Выпишем его себе так:
Оставшиеся нужные нам детали расположены на Auth0 Portal в разделе Applications — выберите Test Application (создаётся автоматически вместе с API).
Здесь мы запишем:
Прокрутите в Test Application до текстового поля Allowed Callback URLs (разрешённые URL'ы для callback'а), в котором мы укажем URL, куда должен отправляться вызов после того, как аутентификация завершена. В нашем случае это:
http://{EXTERNAL_IP}/callback
А для Allowed Logout URLs (разрешённые URL'ы для разлогинивания) добавим:
http://{EXTERNAL_IP}/logout
Перейдём к фронтенду.
Переключитесь на ветку auth0
репозитория [istio-mastery]
. В этой ветке код фронтенда изменён так, чтобы перенаправлять пользователей в Auth0 для аутентификации и использовать JWT-токен в запросах к остальным сервисам. Последнее реализовано следующим образом (App.js [7]):
analyzeSentence() {
fetch('/sentiment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth.getAccessToken()}` // Access Token
},
body: JSON.stringify({ sentence: this.textField.getValue() })
})
.then(response => response.json())
.then(data => this.setState(data));
}
Чтобы перевести фронтенд на использование данных tenant'а в Auth0, откройте sa-frontend/src/services/Auth.js
и замените в нём значения, которые мы записали выше (Auth.js [8]):
const Config = {
clientID: '{YOUR_CLIENT_ID}',
domain:'{YOUR_DOMAIN}',
audience: '{YOUR_AUDIENCE}',
ingressIP: '{EXTERNAL_IP}' // Используется для редиректа после аутентификации
}
Приложение готово. Укажите свой Docker ID в командах ниже при сборке и деплое произведённых изменений:
$ docker build -f sa-frontend/Dockerfile
-t $DOCKER_USER_ID/sentiment-analysis-frontend:istio-auth0
sa-frontend
$ docker push $DOCKER_USER_ID/sentiment-analysis-frontend:istio-auth0
$ kubectl set image deployment/sa-frontend
sa-frontend=$DOCKER_USER_ID/sentiment-analysis-frontend:istio-auth0
Попробуйте приложение! Вас перенаправят на Auth0, где необходимо залогиниться (или зарегистрироваться), после чего вас отправят обратно на страницу, с которой будут производиться уже аутентифицированные запросы. Если же вы попробуете упомянутые в первых частях статьи команды с curl — получите код 401 Status Code, сигнализирующий о том, что запрос не авторизован.
Сделаем следующий шаг — авторизуем запросы.
Аутентификация позволяет нам понять, кем является пользователь, но для того, чтобы узнать, к чему у него есть доступ, требуется авторизация. Istio предлагает инструменты и для этого.
В качестве примера создадим две группы пользователей (см. на схеме ниже):
Концепция авторизации
Для создания этих групп воспользуемся расширением Auth0 Authorization и с помощью Istio предоставим им разные уровни доступа.
На портале Auth0 перейдите к расширениям (Extensions) и установите Auth0 Authorization. После установки перейдите к Authorization Extension, а там — к конфигурации tenant'а по клику справа наверху и выбору соответствующей опции меню (Configuration). Активируйте группы (Groups) и нажмите на кнопку публикации правила (Publish rule).
В Authorization Extension перейдите в Groups и создайте группу Moderators. Поскольку мы будем рассматривать всех аутентифицированных пользователей как обычных, потребности в создании для них дополнительной группы нет.
Выберите группу Moderators, нажмите на Add Members, добавьте свой основной аккаунт. Оставьте некоторых пользователей без какой-либо группы, чтобы убедиться, что доступ для них запрещён. (Новых пользователей можно создать вручную через Auth0 Portal > Users > Create User.)
Пользователи добавлены в группы, однако эта информация должна быть отражена и в токенах для доступа. Чтобы соответствовать OpenID Connect и в то же время возвращать группы, которые нам нужны, токену потребуется добавлять свой custom claim [9]. Реализуется через правила Auth0.
Для создания правила перейдите на Auth0 Portal к Rules, нажмите на Create Rule и выберите пустое правило из шаблонов.
Скопируйте код ниже и сохраните его как новое правило Add Group Claim (namespacedGroup.js [10]):
function (user, context, callback) {
context.accessToken['https://sa.io/group'] = user.groups[0];
return callback(null, user, context);
}
Примечание: этот код берёт первую группу пользователя, определённую в Authorization Extension, и добавляет её в access-токен как custom claim (под своим пространством имён, как того требует Auth0).
Вернитесь к странице Rules и проверьте, что у вас есть два правила, записанные в следующем порядке:
Порядок важен, потому что поле группы асинхронно получает правило auth0-authorization-extension и после этого добавляется как claim вторым правилом. В результате получается такой access-токен:
{
"https://sa.io/group": "Moderators",
"iss": "https://sentiment-analysis.eu.auth0.com/",
"sub": "google-oauth2|196405271625531691872"
// [сокращено для наглядности]
}
Теперь необходимо настроить Envoy-прокси на проверку пользовательского доступа, для чего группа будет вытаскиваться из claim (https://sa.io/group
) в возвращаемом access-токене. Это тема для следующего раздела статьи.
Чтобы авторизация заработала, необходимо включить RBAC для Istio. Для этого воспользуемся следующей конфигурацией:
apiVersion: "rbac.istio.io/v1alpha1"
kind: RbacConfig
metadata:
name: default
spec:
mode: 'ON_WITH_INCLUSION' # 1
inclusion:
services: # 2
- "sa-frontend.default.svc.cluster.local"
- "sa-web-app.default.svc.cluster.local"
- "sa-feedback.default.svc.cluster.local"
Пояснения:
Inclusion
;Применим конфигурацию такой командой:
$ kubectl apply -f resource-manifests/istio/security/enable-rbac.yaml
rbacconfig.rbac.istio.io/default created
Теперь все сервисы требуют управления доступом на основе ролей (Role-Based Access Control). Другими словами, доступ ко всем сервисам запрещён и приведёт к ответу RBAC: access denied
. Теперь разрешим доступ авторизованным пользователям.
Все пользователи должны иметь доступ к сервисам SA-Frontend и SA-WebApp. Реализуется с помощью следующих ресурсов Istio:
Для обычных пользователей разрешим доступ к определённым сервисам (servicerole.yaml [11]):
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
name: regular-user
namespace: default
spec:
rules:
- services:
- "sa-frontend.default.svc.cluster.local"
- "sa-web-app.default.svc.cluster.local"
paths: ["*"]
methods: ["*"]
А через regular-user-binding
применим ServiceRole ко всем посетителям страницы (regular-user-service-role-binding.yaml [12]):
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
name: regular-user-binding
namespace: default
spec:
subjects:
- user: "*"
roleRef:
kind: ServiceRole
name: "regular-user"
Означает ли «все пользователи», что и неаутентифицированные пользователи получат доступ к SA WebApp? Нет, политика проверит валидность JWT-токена.
Применим конфигурации:
$ kubectl apply -f resource-manifests/istio/security/user-role.yaml
servicerole.rbac.istio.io/regular-user created
servicerolebinding.rbac.istio.io/regular-user-binding created
Для модераторов мы хотим включить доступ ко всем сервисам (mod-service-role.yaml [13]):
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
name: mod-user
namespace: default
spec:
rules:
- services: ["*"]
paths: ["*"]
methods: ["*"]
Но мы хотим таких прав только для тех пользователей, в access-токене которых есть claim https://sa.io/group
со значением Moderators
(mod-service-role-binding.yaml [14]):
apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
name: mod-user-binding
namespace: default
spec:
subjects:
- properties:
request.auth.claims[https://sa.io/group]: "Moderators"
roleRef:
kind: ServiceRole
name: "mod-user"
Применим конфигурации:
$ kubectl apply -f resource-manifests/istio/security/mod-role.yaml
servicerole.rbac.istio.io/mod-user created
servicerolebinding.rbac.istio.io/mod-user-binding created
Из-за кэширования в envoy'ях для вступления правил авторизации в силу может потребоваться пара минут. После этого вы сможете убедиться, что у пользователей и модераторов разные уровни доступа.
Ну вот серьёзно: вы где-нибудь видели более простой, не требующий усилий, масштабируемый и безопасный подход к аутентификации и авторизации?
Всего лишь три ресурса Istio (RbacConfig, ServiceRole, and ServiceRoleBinding) потребовались для того, чтобы добиться тонкого контроля над аутентификацией и авторизацией доступа конечных пользователей к сервисам.
Вдобавок, мы вынесли заботу об этих проблемах из наших сервисов в envoy'и, добившись:
Istio позволяет командам сфокусировать свои ресурсы на важных для бизнеса задачах, не добавляя накладные расходы сервисам, возвращая их к статусу «микро».
Статья (в трёх частях) предоставила базовые знания и готовую практическую инструкцию для начала работы с Istio в реальных проектах.
Читайте также в нашем блоге:
Автор: Wimbo
Источник [17]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/312997
Ссылки в тексте:
[1] Первая часть: https://habr.com/ru/company/flant/blog/438426/
[2] вторая: https://habr.com/ru/company/flant/blog/440378/
[3] реализации OpenID Connect: https://openid.net/developers/certified/
[4] Auth0 Portal: https://manage.auth0.com/
[5] документации: https://auth0.com/docs/getting-started/the-basics#account-and-tenants
[6] исходник: https://gist.github.com/rinormaloku/df5bdd7908898c98e51497ce6ee7f48b#file-auth-policy-yaml
[7] App.js: https://gist.github.com/rinormaloku/928719c2e62b56e5a31a82cc1bf32cbb#file-app-js
[8] Auth.js: https://gist.github.com/rinormaloku/f300ff273a1c4860fa515f2633cbdfd2#file-auth-js
[9] custom claim: https://auth0.com/docs/tokens/add-custom-claims
[10] namespacedGroup.js: https://gist.github.com/rinormaloku/a95a252b8c6851dd42ce258a57f7d0c6#file-namespacedgroup-js
[11] servicerole.yaml: https://gist.github.com/rinormaloku/0b14169b9aaffce1ec9daf025077bd4c#file-servicerole-yaml
[12] regular-user-service-role-binding.yaml: https://gist.github.com/rinormaloku/9226ab3e5e4dce5e233e9de1fe19ce70#file-regular-user-service-role-binding-yaml
[13] mod-service-role.yaml: https://gist.github.com/rinormaloku/19c03fde85646ef8438cc9082bc387ad#file-mod-service-role-yaml
[14] mod-service-role-binding.yaml: https://gist.github.com/rinormaloku/ae0e91667d88f3501303741aa76d3b6c#file-mod-service-role-binding-yaml
[15] Conduit — легковесный service mesh для Kubernetes: https://habr.com/ru/company/flant/blog/349496/
[16] Что такое service mesh и почему он мне нужен [для облачного приложения с микросервисами]?: https://habr.com/ru/company/flant/blog/327536/
[17] Источник: https://habr.com/ru/post/443668/?utm_campaign=443668
Нажмите здесь для печати.