Izgradnja API platforme na otvorenom kôdu

Svrha ovog dokumenta je objasniti kako osigurati cjelovitu i robusnu platformu za upravljanje API1-jem zahvaljujući Open-Source2 rješenjima. Napraviće se brzi uvod u nekoliko alata. Zatim će se objasniti kako povezati te alate zajedno kako bi se izgradila kompletna platforma za upravljanje API-jem.

Upravljanje API-jem

Glavne karakteristike rješenja za upravljanje API-jem su:

  • API gateway3: Ulazna, početna tačka za dolazne zahtjeve, i posljednja tačka kod odgovora ka klijentu. Gateway usmjerava zahtjev u pozadinski API4 zahvaljujući konfigurisanim pravilima. Nadalje, Gateway može primijeniti neke filtere na zahtjev prije nego što dođe do ciljanog pozadinskog okruženja. Filteri mogu biti oko kvota, sigurnosti (AuthN i AuthZ), računovodstva, transformacije itd. Ovo je najvažnija komponenta za upravljanje API platformom, obzirom da obrađuje svaki zahtjev i odgovor koji kroz njega prolazi.
  • Upravljanje životnim ciklusom API-ja: Tipični životni ciklus API-ja napreduje od dizajna do razvoja, zatim testiranja, raspoređivanja, zastarjelosti i, na kraju, penzionisanja.
  • Upravljanje API polisama: Administrativna komponenta koja kontroliše životni ciklus polisa koje se koriste za definisanje i upravljanje API-jima.
  • Analitika API: Prikuplja mjerne podatke koji dolaze s Gateway-a i pruža ih kontrolnoj ploči5 kako bi se otkrili ti mjerni podaci. Glavna svrha ove platforme je pružanje informacija o potrošnji API-ja (na primjer: radi stvaranja naplate).
  • Razvojni portal: Izlaže API dokumentaciju i upravlja pristupima programera: pribavljanju API pristupnih podataka, pristup sandbox6 i produkcijskom7 okruženju itd. Također se koristi od strane razvojnih programera za testiranje i procjenu dostupnosti bez upotrebe trećih alata.
  • API fasade: Omogućuje nam izlaganje API-ja visokog nivoa. Pravi orkestraciju ili transformaciju (na primjer, SOAP do REST) ​​zahtjeva. Neki od jakih pružatelja usluga u oblacima (AWS, Azure, …) pokrivaju ovu uslugu s fenomenalnim grafičkim okruženjem, API fasade obično su posvećene razvoju jer su previše specifičane.

Kong

Posao koji obavlja Gateway nije uopšte jednostavan, stoga kod izgradnje platforme za upravljanje API-jima, neophodno je odabrati pravi alat.

Kong je API Gateway izrađen na principima otvorenog kôda i platfoma koja djeluje kao middleware između klijentskih i API-orijentisanih aplikacija. Kong platforma se veoma lahko proširuje upotrebom dodataka (extensions). Ne znamo da li je Kong najpopularniji alat, ali besplatni, open-source jeste sigurno.

Kong mora imati uspostavljene dvije komponente da bi radio ispravno:

  • Kong server HTTP server baziran na NGINX-u i izvodi obrnuti proxy za isporuku zahtjeva kllijenata upstream uslugama.
  • Kong datastore – Čuvanje konfiguracije. Za to se uglavnom koriste Apache Cassandra i PostgreSQL.

Kada upravljanje API-jima koristi Kong, tada svaki zahtjev upućen na neki specifični API na pozadinskom servisu, mora proći kroz Kong. Zahtjev koji prođe kroz Kong izvršava i određene, konfigurisane dodatke. Tako da se Kong može smatrati ulaznom tačkom za API zahtjeve klijenata.

Neki od dodataka (extensions, plugins) za Kong su:

  • Autentikacija: Zaštita servisa sa slojem za autentikaciju
    • Osnovno: Uz pomoć korisničkog imena i šifre.
    • Key: Poznato još i kao API ključ ili API ključ za razvojne inžinjere.
    • OAuth2: Sloj OAuth 2.0 sa Authorization Code Grant, Client Credentials, Implicit Grant ili Resource Owner Password Credentials Grant tokovima.
    • JWT: Provjera zahtjeva da li sadržavaju HS256 ili RS256 JSON Web Token.
    • HMAC: HMAC Autorizacioni potpis za API uz pomoć kog se identifikuje korisnik.
    • LDAP: Moguće se zakačiti na treći servis koji podržava protokol LDAP za pribavljanje pristupnih podataka.
  • Sigurnost: Zaštita servisa s dodatnim slojem
    • ACLAccess Control List: Uspostava ograničenja s crnim i bijelim listama korisnika i njihovo grupisanje.
    • CORS – Cross-origin resource sharing: Dozvola slanja i primanja zahtjeva samo s jednog izvora.
    • Ograničenja po IP adresi: Ogrničen pristup API resursima preko bijele i crne liste IP adresa.
    • Detekcija botova: Otkrivanje i blokiranje neželjenih botova i/ili neželjenih klijenata.
    • SSL: SSL certifikat kao sloj zaštite.
  • Kontrola prometa: Upravljanje, gašenje i ograničavanje API prometa
    • Ograničavanje broja zahtjeva: Ograničenje koliko zahtjeva se može uputiti API-ju.
    • Ograničenje broja odgovora: Ograničenje koliko odgovora se može dobiti nakon upita ka API-ju.
    • Ograničenje po veličini: Blok zahtjev s tijelom ne smije prelaziti određenu veličinu.
  • Analitika i nadgledanje: Vizualiziranje, inspekcija i nadgledanje API, kao i prometa mikroservisa
    • Datadog, Galileo, Runscope: Neki od vanjskih servisa uz pomoć kojih se može vršiti metrika.
  • Transformacije: Pretvaranje zahtjeva i odgovora u letu
    • Pretvaranje zahtjeva: Modifikovanje zahtjeva prije nego dospiju do upstream servera.
    • Pretvaranje odgovora: Modifikovanje upstream odgovora prije nego dođu do klijenta.
    • Korelacioni ID: Jedinstveni ID uz pomoć kog je moguće pratiti cijelu nit od slanja zahtjeva s klijenta, proslijeđivanje pozadinskom serveru do dobijanja odgovora na klijentu.
  • Dnevnici: Vođenje dnevnika o zahtjevima i odgovorima koristeći različita transportna rješenja
    • TCP, UDP, HTTP, File, SysLog, StatsD, Loggy.

Kao što se može vidjeti iz gornje liste, mogućnosti Kong-a su doista ogromne, to mu daje prednost u odnosu na ostale platforme i alate, i uz to je platformski agnostik (moguće ga je instalirati na bilo kom operativnom sistemu) i veoma brz. Dodatne mogućnosti koje Kong pruža su:

  • Horizontalno skalabilan (moguće je imati više paralelno pokrenutih instanci u tzv. Heartbeat modu).
  • Lahko se integriše u bilo koji postojeći sistem.
  • Baziran je na veoma provjerenim i pouzdanim tehnologijama kao što su NGINX, Apache Cassandra ili pak PostgreSQL.
  • Proširiv je već postojećim dodacima, ali i lahko je napraviti vlastiti dodatak baziran na osnovnom dodatku.
  • Može da se instalira kako lokalno, tako i na popularnim platformama u oblacima.

Instalacija

Uz pomoć zvanične slike za Docker instalacija Kong-a je veoma jednostavna. Sve što treba uraditi je slijedeće:

$ git clone https://github.com/Mashape/docker-kong.git
$ cd docker-kong/compose
$ docker-compose up -d

Napomena:
Zvanična slika, odnosno docker-compose.yml datoteka ne sadržava jednu neophodnu stvar za ovaj primjer, a to je dodatak OIDC. Stoga se preporučuje zamjena docker-compose.yml datoteke u prethodnom primjeru s onom koja se nalazi na ovom linku.

Ovo će uspostaviti:

  • NGINX balanser opterećenja.
  • Jednu PostgreSQL pazu podataka za čuvanje konfiguracije.
  • Instancu Konga grafičog okruženja za upravljanje Kong-om.
  • I naravno, jednu instancu Kong-a.

Ako se koristi Docker klaster, moguće je skalirati broj instanci koristeći sastavljač (composer). Svakako, moguće je podesiti odgovarajuće parametre ručno, kao što je promjena s PostrgreSQL na Cassandra bazu podataka.

Jednom pokrenuti svi servisi dozvoliće pristup na slijedećim portovima:

  • 8000: API ulazna tačka (entry point) na Gateway.
  • 8443: API ulazna tačka, ali SSL-ovana.
  • 8001: API za konfiguraciju Gateway-a.
  • 1337: Konga veb okruženje

Podešavanje

Besplatna verzija Kong-a, i to ona potpuno Open-Source se podešava preko API-ja, uglavnom koristeći komandnu liniju. Postoje svakako i određeni alati koji omogućavaju podešavanje preko grafičkih okruženja, ali nisu zvanično podržani od Kong-a, kao što je npr Konga.

$ # Kako napraviti API pristupnu tačku
$ curl -i -X POST \
  --url http://localhost:8001/apis/ \
  --data 'name=mockbin' \
  --data 'upstream_url=http://mockbin.com/' \
  --data 'request_host=mockbin.com'

 $ # Proslijediti zahtjev kroz Kong na drugi host
 $ curl -i -X GET \
  --url http://localhost:8000/ \
  --header 'Host: mockbin.com'
Kong ispod haube

Da ne dođe do zabune

Kong jeste moćan alat kojeg koriste stotine hiljada razvojnih inžinjera širom svijeta u malim, srednjim i velikim preduzećima. Ima ogromnu zajednicu korisnika, ljudi koji ga unaprjeđuju, ali još uvijek je samo Gateway i to ne se ne smije izgubiti iz vida. Prije svega se misli na sisteme za autentifikaciju, gdje sve firme, male ili velike koriste uglavnom IAM – Identity and Access Management, odnosno Rješenje za upravljanje identitetima i pristupom, a to nas dovodi do slijedećeg poglavlja.

Keycloak

Keycloak je projekt otvorenog kôda kojeg razvija i održava RedHat zajednica. Upravljanje autentifikacijom i autorizacijom ključan je zadatak svake dobro osmišljene veb aplikacije ili usluge. Keycloak to čini vrlo jednostavnim i praktičnim, omogućavajući da se fokusira na poslovnu logiku aplikacija, a ne na implementaciju sigurnosnih funkcija, prije svega sigurnosti korisnika.

Keycloak je rješenje za upravljanje identitetom i pristupom otvorenog kôda namijenjeno modernim aplikacijama i uslugama. Omogućava jednostavno zaštitu aplikacija i usluga s malo i potpuno bez kôda.

Nudi širok skup karakteristika kao što su:

  • Adminisgtratorska konzola: Podešavanje Keycloak servera i stvaranje područja (Realm), uloga, korisnika i klijenata.
  • Prijava jednim potpisom (SSO – Single Sign-On): Pomoću protokola provjere autentičnosti OpenID Connect (OIDC) na OAuth 2.0.
  • Standardni protokoli: OpenID Connect, OAuth 2.0 i SAML 2.0.
  • Klijentski adapteri: Integrisanje aplikacija i servisa.
  • LDAP i Active Directory: Povezivanje s postojećim bazama korisnika.
  • Centralizovano upravljanje: Za administratore i korisnike.
  • Prijava uz pomoć društvenih mreža: Jednostavno povezivanje s svim modernim društvenim mrežama.
  • Snažne performanse: Lagan, brz i skalabilan.
  • Klaster: Skalabilnost i dostupnost.
  • Teme: Prilagođavanje izgleda.
  • Proširenja: Proširenja ne samo putem već dostupnih dodataka, nego i uz pomoć kôda.
  • Police za šifre: Moguće je definisati razne vrste polica osiguranja i kreiranja korisničkih šifri.
  • Admin REST API: Upravljanje korisnicima.

Instalacija

Isto kao što se imalo kod instalacije Kong-a, i ovdje se koristi Docker slika i to na slijedeći način:

$ KEYCLOAK_USER=admin
$ KEYCLOAK_PASSWORD=admin
$ docker run -d --name keycloak \
  -p 8080:8080 \
  -e KEYCLOAK_USER=$KEYCLOAK_USER \
  -e KEYCLOAK_PASSWORD=$KEYCLOAK_PASSWORD \
  jboss/keycloak:latest
$ docker logs -f keycloak

Ovo će uspostaviti Keycloak instancu koja koristi ugrađenu bazu podataka (H2). Jednom pokrenut servis omogućava pristup Administratorskoj konzoli na URL: http://localhost:8080/auth/admin/ (Početni pristupni podaci su admin/admin, kao što se vidi iznad).


Udruživanje Kong-a i Keycloak-a

Udružiti Kong i Keycloak je moguće na mnogo načina u zavisnosti od toga šta je svrha udruživanja, mada se uvijek sve svede na isto/slično, stoga će se u nastavku dati jedan primjer (Use case):

Autentifikacija se delegira na Keycloak. To znači, da svaki korisnik prilikom prijave na klijentsku aplikaciju će ustvari koristiti sve osobine Keycloak-a kako bi dobio pristup zaštićeom dijelu aplikacije. Taj pristup će se odobriti uz pomoć JWT žetona (Čita se Džot žetona), koji sadržava mnogo parametara o identitetu korisnika, kao i to koliko je vijek trajanja žetona, ko ga izdaje i slično, a kojim se povezuje klijentska aplikacija s Keycloak-om. Ovo je već standardni način povezivanja aplikacija na internetu, i iako je opisan svuda, još uvijek stvara konfuziju u glavi kod mnogih koji se prvi put susreću s JWT žetonom, bez obzira da li bili iskusan programer ili tek početnik.

No, u nastavku se daje slikovit opis životnog ciklusa jednog žetona u udruženom poduhvatu Kong-a i Keycloak-a:

  1. Korisnik pristupa kllijentskoj aplikaciji na odgovarajućem URL koji se nalazi na Kong. Obzirom, da korisnik još uvijek nema uspostavljenu sjednicu (session), to on bude automatski preusmjeren na prijavnu formu koja se nalazi na Keyloack.
  2. U zavisnosti kako je Keycloak podešen korisnik može da se prijavi na više načina. Koristeći korisničko ime (e-mail) i šifru ili izborom neke od platformi koje nude uslugu preuzimanja identiteta, kao što je npr. prijava uz pomoć Google, Facebook, …
    Jednom prijavljen korisnik dobija pristupni žeton i žeton za osvježenje pristupnog žetona od Keycloak-a.
  3. Oba žetona klijentska aplikacija čuva na svojoj strani za narednu upotrebu.
  4. Sada klijentska aplikacija može da doda JWT žeton u zaglavlje HTTP-a, npr: Authentication: Bearer <jwt_pristupni_žeton>.
    U slučaju da je pristupni žeton istekao, server će vratiti klijentu upozorenje o isteku, a on će u pozadini automatski poslati žeton za osvježenje na osnovu kog će dobiti novi pristupni žeton i novi žeton za osvježenje. S tim novim pristupnim žetonom, izmijeniće se HTTP zaglavlje i upit ponovo poslati.
  5. Kong provjerava pristupni žeton. Ovjerava potpis, izdavača i vrijeme isteka žetona.
  6. Ako je sve uradu, Kong prosljeđuje zahtjev pozadinskom servisu radi prikupljanja potrebnih podataka. Napomena: Kong dodaje ID klijentske aplikacije u HTTP zaglavlje što može biti veoma korisno za pozadinske servise i identifikaciju odakle je korisnik, odnosno upit došao.
  7. Odgovor pozadinskog servisa prolazi kroz sve slojeve do kranje klijentske aplikacije.

Dodatak o JWT

Prije svega valja odrediti na koji način će se potpisati JWT. JWT može biti potpisan uglavnom na dva različita načina s višestrukim nivoom enkripcije.

  • Jedan način je koristeći RS – par privatni/javni ključ, poput onog kako se kreira SSL certifikat.
  • Drugi način je koristeći HS – gdje se JWT potpisuje “običnom” šifrom.

Koji god način da se izabere, važno je omogućiti Kong-u da može provjeriti i potvrditi potpis.

Napomena:
JWT nije enkriptovan! Bilo ko može kopirati JWT niz u https://jwt.io i vidjeti sadržaj payload-a, međutim, niko ga ne može izmijeniti, niti potvrditi osim onoga ko ima ispravan ključ potpisa.

Podešavanje Kong-a za pristup zaštišćenoj kranjoj tački (endpoint)

Ovdje će se koristiti https://httpbin.org kao simulacija nekog pozadinskog servisa, kako se to inače koristi u dokumentaciji širom interneta kada je u pitanju podešavanje Kong+Keycloak.

Ako se pokuša pristupiti direktno na ovaj pozadinski servis koristeći CURL ili neki drugi alat, rezultat bi bio nalik:

$ curl http://httpbin.org/anything
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.54.0",
    "X-Amzn-Trace-Id": "Root=1-5ec37b3c-f89a99445ba887d80ad5ef1a"
  },
  "json": null,
  "method": "GET",
  "origin": "77.2.45.175",
  "url": "http://httpbin.org/anything"
}

Sada će se ovaj pozadinski servis staviti iza Kong-a. Tako da ako se želi pristupiti ovom pozadinskom servisu, mora se ustvari pristupiti nekom posebno definisanom URL-u za tu potrebu. Za tu potrebu potrebno je proći slijedeću proceduru

1. Dodavanje servisa

$ curl -i -X POST \
--url http://localhost:8001/services/ \
--data 'name=httpbin' \
--data 'url=http://httpbin.org/anything'

Ako je sve prošlo kako treba, odgovor na prethodu naredbu bi trebao biti nalik na:

HTTP/1.1 201 Created
Date: Tue, 19 May 2020 06:22:37 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/2.0.0
Content-Length: 296
X-Kong-Admin-Latency: 302

{
   "host":"httpbin.org",
   "created_at":1589869357,
   "connect_timeout":60000,
   "id":"055a5702-1dae-400e-9413-f343ccda8f9d",
   "protocol":"http",
   "name":"httpbin",
   "read_timeout":60000,
   "port":80,
   "path":"\/anything",
   "updated_at":1589869357,
   "retries":5,
   "write_timeout":60000,
   "tags":null,
   "client_certificate":null
}

2. Provjera je li servis dodan

$ curl --location --request GET 'http://localhost:8001/services'

Odgovor na prethodnu naredbu bi trebao biti oblika:

{
   "next":null,
   "data":[
      {
         "host":"httpbin.org",
         "created_at":1589869357,
         "connect_timeout":60000,
         "id":"055a5702-1dae-400e-9413-f343ccda8f9d",
         "protocol":"http",
         "name":"httpbin",
         "read_timeout":60000,
         "port":80,
         "path":"\/anything",
         "updated_at":1589869357,
         "retries":5,
         "write_timeout":60000,
         "tags":null,
         "client_certificate":null
      }
   ]
}

3. Dodavanje rute za dodani servis

Da bi servis bio dostupan neophodno je dodati rutu (Route) na njega. Uz pomoć ruta se određuje kako se (ako se) zahtjevi šalju prema pozadinskom servisu jednom kada im se pokuša pristupiti kroz Kong. Jedan pozadinski servis može imati mnogo ruta.

U prvom koraku servisu je dato ime httpbin, pa će se prilikom dodavanja rute pozvati na to ime.

$ curl -i -X POST \
--url http://localhost:8001/services/httpbin/routes \
--data 'paths[]=/moj-servis'

Ako je sve prošlo uredno, izlaz prethodne naredbe bi trebao biti oblika:

HTTP/1.1 201 Created
Date: Tue, 19 May 2020 06:32:17 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Access-Control-Allow-Origin: *
Server: kong/2.0.0
Content-Length: 429
X-Kong-Admin-Latency: 79

{
   "id":"f9467b3e-95d5-4163-b61c-a129993dd915",
   "path_handling":"v0",
   "paths":[
      "\/moj-servis"
   ],
   "destinations":null,
   "headers":null,
   "protocols":[
      "http",
      "https"
   ],
   "methods":null,
   "snis":null,
   "service":{
      "id":"055a5702-1dae-400e-9413-f343ccda8f9d"
   },
   "name":null,
   "strip_path":true,
   "preserve_host":false,
   "regex_priority":0,
   "updated_at":1589882965,
   "sources":null,
   "hosts":null,
   "https_redirect_status_code":426,
   "tags":null,
   "created_at":1589882965
}

4. Provjera je li ruta dodana

$ curl --location --request GET 'http://localhost:8001/routes'

Ako je ruta uredno dodana izlaz bi trebao da bude veoma sličan izlazu koji se dobio kada se ruta dodavala, tj:

{
   "next":null,
   "data":[
      {
         "id":"f9467b3e-95d5-4163-b61c-a129993dd915",
         "path_handling":"v0",
         "paths":[
            "\/moj-servis"
         ],
         "destinations":null,
         "headers":null,
         "protocols":[
            "http",
            "https"
         ],
         "methods":null,
         "snis":null,
         "service":{
            "id":"055a5702-1dae-400e-9413-f343ccda8f9d"
         },
         "name":null,
         "strip_path":true,
         "preserve_host":false,
         "regex_priority":0,
         "updated_at":1589882965,
         "sources":null,
         "hosts":null,
         "https_redirect_status_code":426,
         "tags":null,
         "created_at":1589882965
      }
   ]
}

5. Provjera da li je pozadinski servis dostupan kroz Kong

Ako je sve prošlo uredno, nema razloga da se ne može pristupiti pozadinskom servisu kroz Kong preko rute koja je upravo dodana. To će se veoma lahko provjeriti na slijedeći način:

$ curl -i -X GET \
--url http://localhost:8000/moj-servis

Ako je sve dobro podešeno, izlaz prethodne naredbe bi trebao da bude isti kao i izlaz kada se direktno pristupa pozadinskom servisu, što je već provjereno prije prvog koraka (vidjeti iznad), tj:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 392
Connection: keep-alive
Date: Tue, 19 May 2020 06:44:14 GMT
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
X-Kong-Upstream-Latency: 310
X-Kong-Proxy-Latency: 165
Via: kong/2.0.0

{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.54.0",
    "X-Amzn-Trace-Id": "Root=1-5ec3803e-1c8811745651a8147857bfc8",
    "X-Forwarded-Host": "example.com"
  },
  "json": null,
  "method": "GET",
  "origin": "172.20.0.1, 77.2.45.175",
  "url": "http://example.com/anything"
}

Ovdje će se podešavanje podijeliti u dvije grane.

  • Jedna je ona u kojoj će se ruti pridružiti JWT dodatak i pokazati kako se može ruta zaštiti na taj način.
  • A drugi je tako što će se instalirati OIDC dodatak u Kong i povezati Keycloak klijent s tim.

6a. Pridruživanje dodatka (Plugin) ruti za JWT

Kao što se vidjelo u prethodnom koraku, uspjelo se dobiti odgovor od pozadinskog servisa bez korištenja bilo kakvih pristupnih podataka. Da bi se ruta zaštitila, prije svega se na tačno određenu rutu koja se želi zaštiti treba pridružiti odgovarajući dodatak i pravilno ga namjestiti. Za tu potrebu se može koristiti više dodataka, a dva najpopularnija su: key-auth i jwt. Ovdje će se dati primjer korištenja ovog drugog.

Valja ne zabraviti da je pozadinski servis u ovom primjeru imenovan s httpbin, preko čega će se jasno dati do znanja da se dodatak pridružuje tom servisu. Ako će se dodatak pridružiti samo posebnoj ruti, kao u ovom slučaju, nephodno je dobaviti ID rute ili pak prilikom dodavanja rute definisati ime. U ovom slučaju ID rute je (pogledati iznad):

f9467b3e-95d5-4163-b61c-a129993dd915
$ curl -X POST http://localhost:8001/routes/f9467b3e-95d5-4163-b61c-a129993dd915/plugins \
    --data "name=jwt"

Ako je dodatak uspješno pridružen, izlaz prethodne naredbe bi trebao biti nalik:

{
   "created_at":1589872922,
   "config":{
      "secret_is_base64":false,
      "run_on_preflight":true,
      "uri_param_names":[
         "jwt"
      ],
      "key_claim_name":"iss",
      "header_names":[
         "authorization"
      ],
      "maximum_expiration":0,
      "anonymous":null,
      "claims_to_verify":null,
      "cookie_names":[

      ]
   },
   "id":"c901ec60-f4ca-449d-bacb-72efd54f1ea6",
   "service":null,
   "enabled":true,
   "protocols":[
      "grpc",
      "grpcs",
      "http",
      "https"
   ],
   "name":"jwt",
   "consumer":null,
   "route":{
      "id":"f9467b3e-95d5-4163-b61c-a129993dd915"
   },
   "tags":null
}

7a. Provjera pridruženog dodatka

$ curl -i -X GET \
--url http://localhost:8000/moj-servis

Ako je sve prošlo uredno, sada bi ova ruta trebala biti javno NEdostupna, tj. izlaz prethodne naredbe bi trebao biti nalik:

HTTP/1.1 401 Unauthorized
Date: Tue, 19 May 2020 07:29:35 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 26
X-Kong-Response-Latency: 29
Server: kong/2.0.0

{"message":"Unauthorized"}

8a. Pristup zaštićenoj ruti uz pomoć JWT-a u HTTP zaglavlju

Da bi se dozvolio pristup zaštićenoj ruti uz pomoć JWT-a u HTTP zaglavlju prije svega je neophodno napraviti korisnika te rute s odgovarajućim parametrima.

Korisnik (consumer) rute se dodaje na slijedeći način:

$ curl -X POST http://localhost:8001/consumers --data "username=meho"

Ako je sve prošlo uredno, izlaz gornje naredbe ima oblik:

{
   "custom_id":null,
   "created_at":1589885612,
   "id":"c1c104a8-30db-4bcf-b2a3-bd32e1f42a80",
   "tags":null,
   "username":"meho"
}

Sada ovom korisniku se treba pridružiti JWT također, kako bi bio u mogućnosti pristupiti zaštićenoj ruti.

Valja obratiti pažnju da gornji izlaz sadržava polje ID. Taj identifikator će se koristiti kod pridruživanja JWT-a tačno određenom korisniku.

Također, zbog jednostavnijeg primjera, ovdje će se koristiti algoritam HS256 za potpisivanje JWT-a. Kako se već prije kazalo, za one koji žele koristiti RS256, neophodno je prvo napraviti SSL certifikat, njegov javni i privatni dio.

$ curl -X POST http://localhost:8001/consumers/c1c104a8-30db-4bcf-b2a3-bd32e1f42a80/jwt \
  --data "key=neki-kljuc" \
  --data "algorithm=HS256" \
  --data-urlencode "secret=MojaSifraZaPotpis"

Ako je sve prošlo uredno, izlaz gornje naredbe bi trebao imati oblik:

{
   "rsa_public_key":null,
   "created_at":1589886135,
   "consumer":{
      "id":"c1c104a8-30db-4bcf-b2a3-bd32e1f42a80"
   },
   "id":"a3a4001b-3e78-4a92-9bda-a6b4b1aa9cde",
   "tags":null,
   "key":"neki-kljuc",
   "secret":"MojaSifraZaPotpis",
   "algorithm":"HS256"
}

Da bi se bilo sigurno da je sve ispravno napravljeno, može se poslati slijedeći upit:

$ curl -X GET http://localhost:8001/consumers/c1c104a8-30db-4bcf-b2a3-bd32e1f42a80/jwt

A čiji rezultat bi trebao da bude veoma nalik gornjem rezultatu prilikom pridruživanja JWT-a korisniku:

{
   "next":null,
   "data":[
      {
         "rsa_public_key":null,
         "created_at":1589886135,
         "consumer":{
            "id":"c1c104a8-30db-4bcf-b2a3-bd32e1f42a80"
         },
         "id":"a3a4001b-3e78-4a92-9bda-a6b4b1aa9cde",
         "tags":null,
         "key":"neki-kljuc",
         "secret":"MojaSifraZaPotpis",
         "algorithm":"HS256"
      }
   ]
}

Da bi se napravio JWT najlakše je koristiti već spomenutu veb stranicu https://jwt.io/ i to tako što će se za algoritam izabrati HS256, a u Payload upisati slijedeće:

{
  "iss": "neki-kljuc",
  "sub": "1234567890",
  "name": "Meho Imamovic",
  "iat": 1516239022
}

Naravno, za potpis će se koristiti secret već prije definisan kod dodjeljivanja JWT-a klijentu:

MojaSifraZaPotpis

Tako napravljen JWT bi trebao imati oblik:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuZWtpLWtsanVjIiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJNZWhvIEltYW1vdmljIiwiaWF0IjoxNTE2MjM5MDIyfQ.o4mVlE2scfHF9duyHdxmzItDYTKwWoBe8ovXGuRyhT0

Sada je vrijeme da se provjeri, da li je moguće pristupiti zaštićenoj ruti uz pomoć ovog, potpisanog i ovjerenog JWT-a:

$ curl -X GET \
  --url http://localhost:8000/moj-servis \
  --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuZWtpLWtsanVjIiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJNZWhvIEltYW1vdmljIiwiaWF0IjoxNTE2MjM5MDIyfQ.o4mVlE2scfHF9duyHdxmzItDYTKwWoBe8ovXGuRyhT0"

Ako se sve dosljedno slijedilo, rezultat gornjeg upita bi trebao biti nalik:

{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuZWtpLWtsanVjIiwic3ViIjoiMTIzNDU2Nzg5MCIsIm5hbWUiOiJNZWhvIEltYW1vdmljIiwiaWF0IjoxNTE2MjM5MDIyfQ.o4mVlE2scfHF9duyHdxmzItDYTKwWoBe8ovXGuRyhT0",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.54.0",
    "X-Amzn-Trace-Id": "Root=1-5ec3bffe-4a8332a4aa0803a489363ff9",
    "X-Consumer-Id": "c1c104a8-30db-4bcf-b2a3-bd32e1f42a80",
    "X-Consumer-Username": "meho",
    "X-Credential-Identifier": "neki-kljuc",
    "X-Forwarded-Host": "localhost"
  },
  "json": null,
  "method": "GET",
  "origin": "172.20.0.1, 77.2.45.175",
  "url": "http://localhost/anything"
}

A što je veoma slično, tačnije isto kao i upit kojim se provjerio dostupnost http://httpbin.org/anything (provjeriti iznad), s razlikom da je HTTP zaglavlje drugačije.

6b. Povezivanje Kong-a i Keycloak-a uz pomoć OIDC dodatka

Nakon što se pokazalo kako ustvari JWT radi i kako se s njim štiti neka ruta, te kako joj se kasnije pristupa s valjanim JWT-om, sada se može pristupiti drugoj metodi, a koja će biti, nadamo se, mnogo jasnija. Odnosno, biće mnogo jasnije, šta je to što se događa u pozadini između Kong-a i Keycloak-a.

Prije svega, valja provjeriti da li je OIDC dodatak dostupan na Kong-u. Ako pak ne postoji, instalacija ovog dodatka nije previše jednostavna jer dodatak ne izdaje Kong nego Nokia. Neophodno je urediti Dockerfile ili se pak zakvačiti na Docker kontejner i osim instalacije samog dodatka, omogućiti ga kroz Environment promjenjive ili preko konfiguracionih datoteka. Ali, kako se prije naglasilo, u ovom primjeru se koristi Kong koji već u sebi sadrži ovaj dodatak, tako da ne bi trebalo biti problema.

$ curl -s http://localhost:8001 | jq .plugins.available_on_server.oidc

Ako se nema jq za parsanje JSON objekata, moguće ga je preuzet i instalirati s tranice: https://stedolan.github.io/jq/download/

Ako dodatak postoji na serveru, izlaz gornjeg upita bi trebao biti:

true

7b. Postavke klijenta unutar Keycloak

Jednom kada se prijavi u Keycloak (pogledaj iznad) dodavanje klijenta treba obaviti na slijedeći način:

  1. Kliknuti na Clients u izborniku s lijeve strane
  2. Kliknuti na Create dugme u desnom gornjem ćošku
  3. Upisati za Client ID: kong (malim slovima)
  4. Izabrati Client Protocol: openid-connect
  5. Kliknuti na Save

Sada će se otvoriti novi ekran s formom na kojoj je moguće uređivati klijenta i tu je potrebno slijedeće (ako se vrati iznad, vidjeće se da su to podaci koji dolaze s Kong-a):

  1. Na kartici Settings
    1. Za Access Type izabrati: confidental
    2. Za Root URL upisati: http://localhost:8000
    3. Za Valid Redirect URL upisati: /moj-novi-servis/* (moj-servis je već iskorišten kod prethodnog primjera)
    4. Kliknuti na Save
  2. Na kartici Credentuals
    1. Kopirati Secret i sačuvati ga na posebno mjesto, jer će se kasnije iskoristiti u Kong

8b. Dodavanje korisnika u Keycloak

Svakako, ako se želi iskoristiti potpuni proces pristupa zaštićenoj ruti, onda se to treba treba raditi tako da kada korisnik jednom dođe na zaštićenu rutu on bude proslijeđen na formu za prijavu gdje je neophodno da upiše svoje pristupne podatke.

Dodavanje korisnika u Keycloak se obavlja na slijedeći način:

  1. Kliknuti na Users na izborniku s lijeve strane
  2. Kliknuti na Add user dugme u gornjem desnom uglu
  3. Upisati Username, npr: korisnik
  4. Postaviti Email Verified na ON, tako da korisnik neće imati potrebu dodatno da se potvrđuje, jer ga ovdje na ovom mjestu administrator koji ga upravo dodaje i potvrđuje
  5. Kliknuti na Save

Sada će se otvoriti novi ekran s formom na kojoj se može uređivati korisnik te je potrebno uraditi slijedeće:

  1. Na kartici Credentials
    1. Upisati New Password i Password Confirmation
    2. Obavezno Temporary postaviti na OFF, kako se korisnik ne bi prisiljavao da postavlja novu šifru pri prvoj prijavi.
    3. Kliknuti na Reset Password

9b. Postavke u Kong-u da bi mogao raditi s Keycloak

Kako je već prije rečeno povezaće se Kong s Keycloak uz pomoć OIDC dodatka. Da bi se to uradilo, odnosno da bi se OIDC dodatak pravilo namjestio neophodno je znati:

  • Klijentski ID, to je u ovom slučaju: kong (Naziv klijenta u Keycloak).
  • Klijentski sigurnosti niz: to je ono što se naglasilo da treba sačuvati kod dodavanja klijenta u Keycloak-u.
  • Pristupna tačka: /moj-novi-servis.
  • IP adresa lokalne mašine, bilo LAN, bilo WAN u zavisnosti koja je trenutno aktivna. Obzirom da je Keycloak unutar Docker kontejnera to se ne može pisati localhost.
  • Port na kom Keycloak je dostupan. Nekada je to 8080, a nekada 8180, pa valja dobro voditi računa koja konfiguracija se koristi. Keycloak je zvanično “ponovo se vratio” na port 8180, ali svejedno, valja dobro provjeriti.

Aktivacija OIDC dodatka za klijent kong bi u tom slučaju bila putem slijedeće naredbe:

$ curl -s -X POST http://localhost:8001/plugins \
  -d name=oidc \
  -d config.client_id=kong \
  -d config.client_secret=98fa767c-0026-4d33-95ec-fdec99f66f30 \
  -d config.discovery=http://192.168.178.48:8080/auth/realms/master/.well-known/openid-configuration \
  | python -mjson.tool

Ako je sve prošlo uredno, izlaz gornje naredbe bi trebao biti:

{
    "config": {
        "bearer_only": "no",
        "client_id": "kong",
        "client_secret": "98fa767c-0026-4d33-95ec-fdec99f66f30",
        "discovery": "http://192.168.178.48:8080/auth/realms/master/.well-known/openid-configuration",
        "filters": null,
        "introspection_endpoint": null,
        "introspection_endpoint_auth_method": null,
        "logout_path": "/logout",
        "realm": "kong",
        "recovery_page_path": null,
        "redirect_after_logout_uri": "/",
        "redirect_uri_path": null,
        "response_type": "code",
        "scope": "openid",
        "session_secret": null,
        "ssl_verify": "no",
        "token_endpoint_auth_method": "client_secret_post"
    },
    "consumer": null,
    "created_at": 1589894385,
    "enabled": true,
    "id": "ee5ad7dd-86de-4396-a29a-729f67ef9fdf",
    "name": "oidc",
    "protocols": [
        "grpc",
        "grpcs",
        "http",
        "https"
    ],
    "route": null,
    "service": null,
    "tags": null
}

Korake 3., 4., i 5. ponoviti ali stim da se treba dodati nova ruta moj-novi-servis, jer je ruta moj-servis već iskorištena kod JWT primjera.

Ako se sada pokuša pristupiti lokaciji putem omiljenog internet pretraživača (Internet Browser), a ne putem CURL kao do sada:
http://localhost:8000/moj-novi-servis

Biće se automatski proslijeđeno na Keycloak prijavnu formu na IP adresi lokalne mašine (iznad unesene), npr:

http://192.168.178.48:8080/auth/realms/master/protocol/openid-connect/auth?response_type=code&client_id=kong&state=534ef65b8f8e42a1935f56bda54ec851&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fmoj-novi-servis&nonce=2cb732d469eba4fbb68ccadeb44043c1&scope=openid

Za prijavu se koristi korisnik dodan u sekciji 8b.

Nakon prijave ponovo će se biti proslijeđeno na početni URL, tj: http://localhost:8000/moj-novi-servis, ali ovaj put će se vidjeti rezultat u obliku JSON formata, čime se potvrđuje da je prijava na zaštićenu rutu uz pomoć Keycloak-a uspjela.

Par trikova

Ako se primjera radi ima pozadinski servis na kom se imaju rute s različitim nivoom sigurnosti, onda to treba reflektovati i na Kong svakako. Ovdje se može iskoristiti nekoliko načina da se to riješi.

Jedan je da se napravi vlastiti dodatak u kom će se provjeravati neophodni uslovi.

Drugi je da se umjesto dodavanja jednog servisa u Kong koji je veza s pozadinskim servisom ustvari doda više Kong servisa, gdje će svaki servis pokazivati na određenu grupu ruta pozadinskog servisa.


1API – Application Programming Interface – Aplikativni programski interfejs jest skup protokola i rutina koji računarski sistem, računarska biblioteka ili aplikacija obezbjeđuje drugim aplikacijama za obavljanje zahtjeva i usluga tim aplikacijama. Primjer je skup funkcija operativnog sistema koje programi mogu da koriste za obavljanje poslova kao što su upravljanje datotekama i prikazivanje informacija na ekranu.

2 Open-Source – Softver otvorenog kôda se odnosi na softver čiji je izvorni kod dostupan unutar „open source” licence svim korisnicima koji mogu mijenjati, prepravljati i poboljšavati njegov sadržaj. To znači da uz „open source” programe dolazi i čitav izvorni kôd u nekom programskom jeziku, pa se može i mijenjati sam program. Ovo nije slučaj sa plaćenim softverom.

3 Gateway – Glavna kapija, Pristupnik ili Prevodilac protokola, je uređaj koji se nalazi na međi između dvije računarske mreže, te vrši pretvaranje jednog prometa u drugi.

4 Backend API – Svi servisi koji nisu vidljivi izvana, odnosno nemaju direktan pristup izvana nazivaju se pozadinski ili bekend (backend) servisima.

5 Dashboard – Među ljudima u svijetu IT-a jednostavno se koristi prilagođena engleska riječ Dašbord, a koja u osnovi označava neku nadzornu i/ili kontrolnu ploču. Nešto poput komandne table u automobilu.

6 Sandbox – Doslovno kao kutija s pijeskom, gdje se djeca igraju. Preneseno, radno, razvojno okruženje za programere na kojem se mogu „igrati”.

7 Production – Produkcija ili produkcijsko okruženje. Misli se na okruženje na kome se vrte softveri koje koriste krajnji korisnici.

0

Hits: 61