Назад да мікрасэрвісаў разам з Istio. Частка 3

Назад да мікрасэрвісаў разам з Istio. Частка 3

Заўв. перав.: Першая частка гэтага цыклу была прысвечана знаёмству з магчымасцямі Istio і іх дэманстрацыі ў дзеянні, другая - тонка наладжвальнай маршрутызацыі і кіраванню сеткавым трафікам. Цяпер жа гаворка пойдзе пра бяспеку: для дэманстрацыі злучаных з ёй базавых функцый аўтар выкарыстае identity-сэрвіс Auth0, аднак па аналогіі з ім могуць наладжвацца і іншыя правайдэры.

Мы наладзілі Kubernetes-кластар, у якім разгарнулі Istio і прыклад мікрасэрвіснага прыкладання Sentiment Analysis, – так былі прадэманстраваны магчымасці Istio.

З дапамогай Istio нам удалося захаваць невялікі памер сэрвісаў, паколькі яны не маюць патрэбы ў рэалізацыі такіх «пластоў», як паўторныя спробы падключэння (Retries), таймаўты (Timeouts), аўтаматычны выключальнікі (Circuit Breakers), трасіроўка (Tracing), маніторынг (Monitoring) . Акрамя таго, мы задзейнічалі тэхнікі прасунутага тэсціравання і дэплою: A / B-тэставанне, люстраванне і канарэечныя выкаты.

Назад да мікрасэрвісаў разам з Istio. Частка 3

У новым матэрыяле мы разбярэмся з фінальнымі пластамі на шляху да business value: аўтэнтыфікацыяй і аўтарызацыяй - і ў Istio гэта суцэльнае задавальненне!

Аўтэнтыфікацыя і аўтарызацыя ў Istio

Ніколі б не паверыў, што натхнюся аўтэнтыфікацыяй і аўтарызацыяй. Што ж такога з тэхналагічнага пункта гледжання можа прапанаваць Istio для таго, каб зрабіць гэтыя тэмы займальнымі і нават больш за тое – каб яны натхнілі і вас?

Адказ просты: Istio пераносіць адказнасць за гэтыя магчымасці з вашых сэрвісаў на проксі Envoy. Да часу, калі запыты дасягаюць сэрвісаў, яны ўжо аўтэнтыфікаваны і аўтарызаваны, так што вам застаецца проста пісаць карысны для бізнэсу код.

Гучыць нядрэнна? Зазірнем жа ўнутр!

Аўтэнтыфікацыя з Auth0

У якасці сервера для кіравання ідэнтыфікацыяй і доступам будзем выкарыстоўваць Auth0, у якога ёсць выпрабавальная версія, які інтуітыўна зразумелы ў выкарыстанні і папросту падабаецца мне. Зрэшты, тыя ж самыя прынцыпы можна прымяніць і ў адносінах да любой іншай. рэалізацыі OpenID Connect: KeyCloak, IdentityServer і многім іншым.

Для пачатку зайдзіце на Auth0 Portal са сваім акаўнтам, стварыце tenant (tenant - «арандатар», лагічная адзінка ізаляцыі, падрабязней гл. у дакументацыі - заўв. перав.) і зайдзіце ў Applications > Default App, выбраўшы дамен, як паказана на скрыншоце ніжэй:

Назад да мікрасэрвісаў разам з Istio. Частка 3

Выберыце гэты дамен у файле resource-manifests/istio/security/auth-policy.yaml (зыходнік):

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 Несанкцыянаванае. Зараз перанакіруем карыстальнікаў фронтэнда на аўтэнтыфікацыю з Auth0.

Аўтэнтыфікацыя запытаў з Auth0

Каб аўтэнтыфікаваць запыты канчатковага карыстальніка, неабходна стварыць API у Auth0, які будзе прадстаўляць аўтэнтыфікаваныя сэрвісы (reviews, details і ratings). Для стварэння API перайдзіце ў Auth0 Portal > APIs > Create API і запоўніце форму:

Назад да мікрасэрвісаў разам з Istio. Частка 3

Важнай інфармацыяй тут з'яўляецца ідэнтыфікатар, які пазней мы будзем выкарыстоўваць у скрыпце. Выпішам яго сабе так:

  • Аўдыторыя: {YOUR_AUDIENCE}

Пакінутыя патрэбныя нам дэталі размешчаны на Auth0 Portal у падзеле прыкладанняў - выберыце Тэставае прыкладанне (ствараецца аўтаматычна разам з API).

Тут мы запішам:

  • дамен: {YOUR_DOMAIN}
  • Ідэнтыфікатар кліента: {YOUR_CLIENT_ID}

Пракруціце ў Тэставае прыкладанне да тэкставага поля 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):

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):

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 Код стану, які сігналізуе аб тым, што запыт не аўтарызаваны.

Зробім наступны крок - аўтарызуем запыты.

Аўтарызацыя з Auth0

Аўтэнтыфікацыя дазваляе нам зразумець, кім з'яўляецца карыстач, але для таго, каб пазнаць, да чаго ў яго ёсць доступ, патрабуецца аўтарызацыя. Istio прапануе інструменты і для гэтага.

У якасці прыкладу створым дзве групы карыстальнікаў (гл. на схеме ніжэй):

  • Карыстальнікі (users) - з доступам толькі да сэрвісаў SA-WebApp і SA-Frontend;
  • Мадэратары (moderators) - з доступам да ўсіх трох сэрвісаў.

Назад да мікрасэрвісаў разам з Istio. Частка 3
Канцэпцыя аўтарызацыі

Для стварэння гэтых груп скарыстаемся пашырэннем Auth0 Authorization і з дапамогай Istio дамо ім розныя ўзроўні доступу.

Ўстаноўка і канфігурацыя Auth0 Authorization

На партале Auth0 перайдзіце да пашырэнняў (Пашырэння) і ўсталюйце Auth0 Authorization. Пасля ўстаноўкі перайдзіце да Authorization Extension, а там - да канфігурацыі tenant'а па кліку справа наверсе і выбару адпаведнай опцыі меню (Канфігурацыя). Актывуйце групы (Групы) і націсніце на кнопку публікацыі правілы (Publish rule).

Назад да мікрасэрвісаў разам з Istio. Частка 3

Стварэнне груп

У Authorization Extension перайдзіце ў груп і стварыце групу мадэратары. Паколькі мы будзем разглядаць усіх аўтэнтыфікаваных карыстальнікаў як звычайных, патрэбнасці ў стварэнні для іх дадатковай групы няма.

Абярыце групу мадэратары, націсніце на Дадаць удзельнікаў, дадайце свой асноўны рахунак. Пакіньце некаторых карыстальнікаў без якой-небудзь групы, каб пераканацца, што доступ для іх забаронены. (Новых карыстальнікаў можна стварыць уручную праз Auth0 Portal > Users > Create User.)

Дадайце Group Claim у Access Token

Карыстальнікі дададзены ў групы, аднак гэтая інфармацыя павінна быць адлюстравана і ў токена для доступу. Каб адпавядаць OpenID Connect і ў той жа час вяртаць групы, якія нам патрэбныя, токену спатрэбіцца дадаваць свой custom claim. Рэалізуецца праз правілы Auth0.

Для стварэння правіла перайдзіце на Auth0 Portal да Правілы, націсніце на Стварыць правіла і абярыце пустое правіла з шаблонаў.

Назад да мікрасэрвісаў разам з Istio. Частка 3

Скапіюйце код ніжэй і захавайце яго як новае правіла Add Group Claim (namespacedGroup.js):

function (user, context, callback) {
    context.accessToken['https://sa.io/group'] = user.groups[0];
    return callback(null, user, context);
}

Заўвага: гэты код бярэ першую групу карыстальніка, вызначаную ў Authorization Extension, і дадае яе ў access-токен як custom claim (пад сваёй прасторай імёнаў, як таго патрабуе Auth0).

Калі ласка, вярніцеся да старонкі Правілы і праверце, што ў вас ёсць два правілы, запісаныя ў наступным парадку:

  • auth0-authorization-extension
  • Add Group Claim

Парадак важны, таму што поле групы асінхронна атрымлівае правіла. 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-токене. Гэта тэма для наступнага раздзела артыкула.

Канфігурацыя аўтарызацыі ў Istio

Каб аўтарызацыя пачала працаваць, неабходна ўключыць 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" 

Тлумачэнні:

  • 1 — уключаем RBAC толькі для сэрвісаў і прастор імёнаў, пералічаных у поле Inclusion;
  • 2 - пералічваем спіс нашых сэрвісаў.

Ужыем канфігурацыю такой камандай:

$ 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 - вызначае правы, якія ёсць у карыстальніка;
  • ServiceRoleBinding - Вызначае, да каго гэтая ServiceRole ставіцца.

Для звычайных карыстальнікаў дазволім доступ да пэўных сэрвісаў (servicerole.yaml):

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):

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):

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):

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'і, дамогшыся:

  • памяншэнні колькасці тыпавога кода, у якім могуць апынуцца праблемы бяспекі і багі;
  • зніжэння колькасці дурных сітуацый, у якіх адзін endpoint аказаўся даступным звонку і забыўся паведаміць пра гэта;
  • устаранення неабходнасці ў абнаўленні ўсіх сэрвісаў пры кожным даданні новай ролі або права;
  • таго, што новыя сервісы застаюцца простымі, бяспечнымі і хуткімі.

Выснова

Istio дазваляе камандам сфакусаваць свае рэсурсы на важных для бізнэсу задачах, не дадаючы накладныя выдаткі сэрвісам, вяртаючы іх да статуту "мікра".

Артыкул (у трох частках) падала базавыя веды і гатовую практычную інструкцыю для пачатку працы з Istio у рэальных праектах.

PS ад перакладчыка

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар