Wróć do mikroserwisów z Istio. Część 3

Wróć do mikroserwisów z Istio. Część 3

Przyp. tłum.: Pierwsza część Ten cykl był poświęcony możliwościom Istio oraz ich prezentacji w działaniu, druga — precyzyjnie dostosowanej trasowaniu i zarządzaniu ruchem sieciowym. Teraz porozmawiamy o bezpieczeństwie: do demonstracji podstawowych funkcji związanych z tym tematem autor wykorzystuje usługę tożsamości Auth0, jednak podobnie można skonfigurować innych dostawców.

Skonfigurowaliśmy klaster Kubernetes, w którym uruchomiliśmy Istio i przykładową aplikację mikroserwisową Sentiment Analysis, ilustrując możliwości Istio.

Dzięki Istio udało nam się zachować niewielki rozmiar usług, ponieważ nie wymagają one wdrożenia takich „warstw”, jak ponowne próby połączenia (Retries), limity czasowe (Timeouts), automatyczne wyłączniki (Circuit Breakers), śledzenie (Tracing), monitorowanie (Monitoring). Ponadto zastosowaliśmy zaawansowane techniki testowania i wdrażania: A/B-testy, mirroring i kanaryczne wypuszczenia.

Wróć do mikroserwisów z Istio. Część 3

W nowym materiale przyjrzymy się ostatnim warstwom na drodze do wartości biznesowej: autoryzacji i uwierzytelnieniu — a w Istio to czysta przyjemność!

Uwierzytelnienie i autoryzacja w Istio

Nigdy bym nie pomyślał, że zainspiruje mnie autoryzacja i uwierzytelnienie. Co takiego może technologicznie zaoferować Istio, aby te tematy były frapujące, a nawet więcej — aby zainspirowały i Was?

Odpowiedź jest prosta: Istio przenosi odpowiedzialność za te możliwości z Twoich usług na proxy Envoy. W momencie, gdy zapytania docierają do usług, są już uwierzytelnione i autoryzowane, więc pozostaje Ci tylko pisać przydatny kod biznesowy.

Brzmi dobrze? Zajrzyjmy więc do środka!

Uwierzytelnienie z Auth0

Jako serwer do zarządzania tożsamością i dostępem użyjemy Auth0, który ma wersję próbną, jest intuicyjny w użyciu i po prostu mi się podoba. Jednak te same zasady można zastosować wobec każdej innej implementacji OpenID Connect: KeyCloak, IdentityServer i wielu innych.

Na początek wejdź na Auth0 Portal ze swoim kontem, utwórz tenant (tenant — „wynajmujący”, logiczna jednostka izolacji, szczegóły w dokumentacji — przyp. tłum.) i przejdź do Applications > Default App, wybierając Domena, jak pokazano na zrzucie ekranu poniżej:

Wróć do mikroserwisów z Istio. Część 3

Podaj tę domenę w pliku resource-manifests/istio/security/auth-policy.yaml (źródło):

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

Mając takie zasoby, Pilot (jeden z trzech podstawowych komponentów Control Plane w Istio — przyp. tłum.) konfiguruje Envoy'e do autoryzacji żądań przed ich przekierowaniem do usług: sa-web-app i sa-feedback. W tym samym czasie konfiguracja nie jest stosowana do Envoy'ów usługi sa-frontend, co pozwala nam zachować frontend nieuwierzytelniony. Aby zastosować politykę (Policy), wykonaj polecenie:

$ kubectl apply -f resource-manifests/istio/security/auth-policy.yaml
policy.authentication.istio.io “auth-policy” utworzona

Wróć na stronę i dokonaj żądania — zobaczysz, że zakończy się ono statusem 401 Nieautoryzowany. Teraz przekierujemy użytkowników frontendu do autoryzacji z Auth0.

Autoryzacja żądań z Auth0

Aby uwierzytelnić żądania końcowego użytkownika, należy utworzyć API w Auth0, które będzie reprezentować uwierzytelnione usługi (recenzje, szczegóły i oceny). Aby utworzyć API, przejdź do Auth0 Portal > APIs > Create API i wypełnij formularz:

Wróć do mikroserwisów z Istio. Część 3

Ważną informacją tutaj jest Identifier, który później użyjemy w skrypcie. Zapiszmy go sobie tak:

  • Audience: {YOUR_AUDIENCE}

Pozostałe potrzebne nam szczegóły znajdują się w Auth0 Portal w sekcji Applications — wybierz Test Application (tworzone automatycznie wraz z API).

Tutaj zapiszemy:

  • Domena: {YOUR_DOMAIN}
  • Client Id: {YOUR_CLIENT_ID}

Przewiń do Test Application do pola tekstowego Allowed Callback URLs (dozwolone URL'e dla callback'u), w którym wskażemy URL, do którego powinno być wysyłane wywołanie po zakończeniu autoryzacji. W naszym przypadku to:

http://{EXTERNAL_IP}/callback

A dla Allowed Logout URLs (dozwolone URL'e dla wylogowania) dodamy:

http://{EXTERNAL_IP}/logout

Przejdźmy do frontendu.

Aktualizacja frontendu

Przełącz się na gałąź auth0 repozytorium [istio-mastery]. W tej gałęzi kod frontendu został zmieniony w taki sposób, aby przekierowywać użytkowników do Auth0 w celu autoryzacji i używać tokena JWT w żądaniach do innych usług. Ostatnie zostało zrealizowane w następujący sposób (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));
}

Aby przenieść frontend na korzystanie z danych tenants w Auth0, otwórz sa-frontend/src/services/Auth.js i zamień w nim wartości, które zapisaliśmy powyżej (Auth.js):

const Config = {
    clientID: '{YOUR_CLIENT_ID}',
    domain:'{YOUR_DOMAIN}',
    audience: '{YOUR_AUDIENCE}',
    ingressIP: '{EXTERNAL_IP}' // Używane do przekierowania po uwierzytelnieniu
}

Aplikacja jest gotowa. Podaj swoje Docker ID w poniższych poleceniach podczas budowania i wdrażania wprowadzonych zmian:

$ 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

Wypróbuj aplikację! Zostaniesz przekierowany na Auth0, gdzie musisz się zalogować (lub zarejestrować), a następnie zostaniesz odesłany z powrotem na stronę, z której będą już wykonywane uwierzytelnione zapytania. Jeśli spróbujesz wymienionych we wczesniejszych częściach artykułu poleceń za pomocą curl — otrzymasz kod 401 Status Code, sygnalizujący, że zapytanie nie jest autoryzowane.

Zróbmy kolejny krok — autoryzujemy zapytania.

Autoryzacja z Auth0

Uwierzytelnienie pozwala nam zrozumieć, kim jest użytkownik, ale aby dowiedzieć się, do czego ma dostęp, potrzebna jest autoryzacja. Istio oferuje również narzędzia do tego.

Na przykład stwórzmy dwie grupy użytkowników (patrz na schemat poniżej):

  • Użytkownicy (users) — z dostępem tylko do usług SA-WebApp i SA-Frontend;
  • Moderatorzy (moderators) — z dostępem do wszystkich trzech usług.

Wróć do mikroserwisów z Istio. Część 3
Koncepcja autoryzacji

Aby stworzyć te grupy, skorzystamy z rozszerzenia Auth0 Authorization i za pomocą Istio zapewnimy im różne poziomy dostępu.

Instalacja i konfiguracja Auth0 Authorization

Na portalu Auth0 przejdź do rozszerzeń (Extensions) i zainstaluj Auth0 Authorization. Po instalacji przejdź do Authorization Extension, a następnie do konfiguracji najemcy poprzez kliknięcie w prawym górnym rogu i wybór odpowiedniej opcji menu (Configuration). Aktywuj grupy (Groups) i kliknij przycisk publikacji reguły (Publish rule).

Wróć do mikroserwisów z Istio. Część 3

Tworzenie grup

W Authorization Extension przejdź do Groups i stwórz grupę Moderators. Ponieważ będziemy traktować wszystkich uwierzytelnionych użytkowników jako zwykłych, nie ma potrzeby tworzenia dla nich dodatkowej grupy.

Wybierz grupę Moderators, kliknij na Add Members, dodaj swoje główne konto. Pozostaw niektórych użytkowników bez żadnej grupy, aby upewnić się, że dostęp dla nich jest zabroniony. (Nowych użytkowników można stworzyć ręcznie przez Auth0 Portal > Users > Create User.)

Dodaj Group Claim do Access Token

Użytkownicy zostali dodani do grup, jednak ta informacja musi być również odzwierciedlona w tokenach dostępu. Aby spełnić wymagania OpenID Connect i jednocześnie zwrócić potrzebne grupy, token będzie musiał dodać swoje custom claim. Realizowane jest poprzez zasady Auth0.

Aby stworzyć zasadę, przejdź do Portalu Auth0 do Reguły, kliknij na Utwórz Regułę i wybierz pustą zasadę z szablonów.

Wróć do mikroserwisów z Istio. Część 3

Skopiuj poniższy kod i zapisz go jako nową zasadę Dodaj Klauzulę Grupy (namespacedGroup.js):

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

Uwaga: ten kod bierze pierwszą grupę użytkownika, określoną w Rozszerzeniu Autoryzacji, i dodaje ją do access-tokenu jako custom claim (pod własną przestrzenią nazw, jak wymagane przez Auth0).

Wróć do strony Reguły i upewnij się, że masz dwie zasady zapisane w następującej kolejności:

  • auth0-authorization-extension
  • Dodaj Klauzulę Grupy

Kolejność ma znaczenie, ponieważ pole grupy asynchronicznie otrzymuje zasadę auth0-authorization-extension i jest następnie dodawane jako claim drugą zasadą. W rezultacie otrzymujemy taki access-token:

{
 "https://sa.io/group": "Moderators",
 "iss": "https://sentiment-analysis.eu.auth0.com/",
 "sub": "google-oauth2|196405271625531691872"
 // [skrócone dla czytelności]
}

Teraz należy skonfigurować proxy Envoy, aby sprawdzało dostęp użytkownika, zatem grupa będzie wyciągana z claim (https://sa.io/group) w zwracanym access-tokenie. To temat na kolejny rozdział artykułu.

Konfiguracja autoryzacji w Istio

Aby autoryzacja działała, należy włączyć RBAC dla Istio. W tym celu skorzystamy z następującej konfiguracji:

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" 

Wyjaśnienia:

  • 1 — włączamy RBAC tylko dla usług i przestrzeni nazw wymienionych w polu Inclusion;
  • 2 — wymieniamy listę naszych usług.

Zastosujemy konfigurację takim poleceniem:

$ kubectl apply -f resource-manifests/istio/security/enable-rbac.yaml
rbacconfig.rbac.istio.io/default created

Teraz wszystkie usługi wymagają kontroli dostępu opartej na rolach (Role-Based Access Control). Innymi słowy, dostęp do wszystkich usług jest zabroniony i skutkuje odpowiedzią RBAC: dostęp zabroniony. Teraz zezwolimy na dostęp autoryzowanym użytkownikom.

Konfiguracja dostępu dla zwykłych użytkowników

Wszyscy użytkownicy powinni mieć dostęp do usług SA-Frontend i SA-WebApp. Może być to zrealizowane przy pomocy następujących zasobów Istio:

  • ServiceRole — określa prawa, które przysługują użytkownikowi;
  • ServiceRoleBinding — określa, do kogo odnosi się ta ServiceRole.

Dla zwykłych użytkowników umożliwimy dostęp do określonych usług (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: ["*"]

A przez regular-user-binding zastosujemy ServiceRole do wszystkich odwiedzających stronę (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"

Czy "wszyscy użytkownicy" oznacza, że także nieuwierzytelnieni użytkownicy uzyskają dostęp do SA WebApp? Nie, polityka sprawdzi ważność tokenu JWT.

Zastosujemy konfiguracje:

$ kubectl apply -f resource-manifests/istio/security/user-role.yaml
servicerole.rbac.istio.io/regular-user utworzony
servicerolebinding.rbac.istio.io/regular-user-binding utworzony

Konfiguracja dostępu dla moderatorów

Dla moderatorów chcemy umożliwić dostęp do wszystkich usług (mod-service-role.yaml):

apiVersion: "rbac.istio.io/v1alpha1"
kind: ServiceRole
metadata:
  name: mod-user
  namespace: default
spec:
  rules:
  - services: ["*"]
    paths: ["*"]
    methods: ["*"]

Ale chcemy mieć takie prawa tylko dla tych użytkowników, w których access-tokenie znajduje się claim https://sa.io/group o wartości 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" 

Zastosujemy konfiguracje:

$ kubectl apply -f resource-manifests/istio/security/mod-role.yaml
servicerole.rbac.istio.io/mod-user utworzony
servicerolebinding.rbac.istio.io/mod-user-binding utworzony

Z powodu buforowania w envoyach, aby nowe zasady autoryzacji zaczęły obowiązywać, może być potrzebne kilka minut. Po tym czasie upewnisz się, że użytkownicy i moderatorzy mają różne poziomy dostępu.

Podsumowanie tej części

No serio: czy kiedykolwiek widziałeś prostsze, bezwysiłkowe, skalowalne i bezpieczne podejście do autoryzacji i uwierzytelniania?

Tylko trzy zasoby Istio (RbacConfig, ServiceRole i ServiceRoleBinding) były potrzebne, aby uzyskać szczegółową kontrolę nad autoryzacją i uwierzytelnianiem dostępu użytkowników końcowych do usług.

Dodatkowo przenieśliśmy troskę o te problemy z naszych usług do envoy, osiągając:

  • zmniejszenie ilości typowego kodu, w którym mogą pojawić się problemy z bezpieczeństwem i błędy;
  • zmniejszenie liczby głupich sytuacji, w których jeden punkt końcowy był dostępny z zewnątrz i zapomniał o tym poinformować;
  • eliminacji potrzeby aktualizacji wszystkich usług przy każdym dodaniu nowej roli lub uprawnienia;
  • tego, że nowe usługi pozostają proste, bezpieczne i szybkie.

Wnioski

Istio pozwala zespołom skupić swoje zasoby na kluczowych zadaniach biznesowych, nie dodając obciążeń do usług, przywracając je do statusu 'mikro'.

Artykuł (w trzech częściach) dostarczył podstawową wiedzę oraz gotową praktyczną instrukcję do rozpoczęcia pracy z Istio w rzeczywistych projektach.

P.S. od tłumacza

Przeczytaj także na naszym blogu:

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster