
TL;DR: bit će opis Keycloak-a, sustava za upravljanje pristupom s otvorenim kodom, analiza unutarnje strukture, detalji konfiguracije.
Uvod i osnovne ideje
U ovom članku ćemo vidjeti osnovne ideje koje treba imati na umu prilikom postavljanja Keycloak klastera preko Kubernetesa.
Ako želite saznati više o Keycloak-u — obratite se poveznicama na kraju članka. Da biste se dublje uronili u praksu — možete proučiti s modulom koji implementira osnovne ideje ovog članka (vodič za pokretanje i dalje, u ovom članku će biti pregled strukture i postavki, nap. prevoditelja).
Keycloak je sveobuhvatan sustav napisan u Javi koji je izgrađen na aplikacijskom poslužitelju . Ukratko, to je okvir za autorizaciju koji korisnicima aplikacija omogućuje federaciju i SSO (single sign-on).
Pozivamo vas da pročitate službeni ili za detaljno razumijevanje.
Pokretanje Keycloak-a
Keycloak zahtijeva dva stalno pohranjena izvor podataka za pokretanje:
- Baza podataka koja se koristi za pohranu ustaljenih podataka, poput informacija o korisnicima
- Datagrid cache, koji se koristi za keširanje podataka iz baze, kao i za pohranu nekih kratkotrajnih i često promjenjivih metapodataka, poput korisničkih sesija. Implementira , koji je obično znatno brži od baze podataka. No u svakom slučaju, podaci pohranjeni u Infinispanu su efemerni — i ne treba ih čuvati prilikom ponovnog pokretanja klastera.
Keycloak radi u četiri različita načina:
- Obični — jedan i jedini proces, konfigurira se putem datoteke standalone.xml
- Običan klaster (visoko dostupna opcija) — svi procesi trebaju koristiti istu konfiguraciju koja se mora ručno sinkronizirati. Postavke su pohranjene u datoteci standalone-ha.xml, također, potrebno je omogućiti zajednički pristup bazi podataka i ravnotežu opterećenja.
- Domen klaster — pokretanje klastera u običnom režimu brzo postaje rutinski i dosadan zadatak kako klaster raste, jer svaki put kada se konfiguracija promijeni, sve promjene treba unijeti na svakom čvoru klastera. Domena način rada rješava ovaj problem postavljanjem zajedničkog skladišta i objavljivanjem konfiguracije. Ove postavke su pohranjene u datoteci domain.xml
- Replikacija između podatkovnih centara — u slučaju da želite pokrenuti Keycloak u klasteru koji se sastoji od više podatkovnih centara, često geografski udaljenih. U ovom načinu rada svaki podatkovni centar će imati svoj vlastiti klaster Keycloak poslužitelja.
U ovom članku detaljno ćemo razmotriti drugi način, odnosno običan klaster, a također ćemo malo dotaknuti temu replikacije među podatkovnim centrima, jer ova dva načina imaju smisla pokrenuti u Kubernetesu. Na sreću, u Kubernetesu nema problema sa sinkronizacijom postavki više podova (čvorova Keycloak), tako da domen klaster neće biti posebno teško napraviti.
Također, molimo vas da obratite pažnju na to da će riječ klaster do kraja članka biti korištena isključivo u vezi s grupom čvorova Keycloak koji rade zajedno, bez potrebe za referenciranjem na klaster Kubernetes.
Običan klaster Keycloak
Za pokretanje Keycloak u ovom režimu potrebno je:
- konfigurirati vanjsku zajedničku bazu podataka
- postaviti ravnotežnik opterećenja
- imati unutarnju mrežu koja podržava ip multicast
Konfiguraciju vanjske baze nećemo razmatrati, jer to nije cilj ovog članka. Smatrajmo da negdje postoji radna baza podataka — i da imamo točku povezivanja. Jednostavno ćemo dodati ove podatke u varijable okruženja.
Za bolje razumijevanje kako Keycloak radi u visoko dostupnom (HA) klasteru, važno je znati koliko to sve ovisi o mogućnostima Wildflya za klasterizaciju.
Wildfly primjenjuje nekoliko podsustava, od kojih se neki koriste kao ravnotežnici opterećenja, dok su drugi za visoku dostupnost. Ravnotežnik opterećenja osigurava dostupnost aplikacije čak i kada je čvor klastera preopterećen, dok visoka dostupnost jamči dostupnost aplikacije i u slučaju kvara dijela čvorova klastera. Neki od tih podsustava su:
mod_cluster: radi zajedno s Apacheom kao ravnotežnik HTTP-a, ovisan o TCP multicastu za pronalaženje čvorova po defaultu. Može se zamijeniti vanjskim ravnotežnikom opterećenja.infinispan: distribuirana predmemorija koja koristi kanale JGroups kao transportni nivo. Također može primijeniti protokol HotRod za komunikaciju s vanjskim klasterom Infinispan za sinkronizaciju sadržaja predmemorije.jgroups: pruža podršku za grupni komunikaciju za visoko dostupne usluge temeljene na kanalima JGroups. Imenovani kanali omogućuju instancama aplikacije u klasteru da se povežu u grupe, tako da komunikacija ima svojstva poput pouzdanosti, redoslijeda i otpornosti na greške.
Ravnoteža opterećenja
Pri instalaciji ravnoteže opterećenja kao ingress kontrolera u Kubernetes klasteru važno je imati na umu sljedeće stvari:
Rad Keycloak-a podrazumijeva da je udaljena adresa klijenta koja se povezuje putem HTTP-a na autentifikacijski server stvarna IP adresa klijentskog računala. Postavke ravnoteže opterećenja i ingress trebaju ispravno postaviti HTTP zaglavlja X-Forwarded-For i X-Forwarded-Proto, kao i zadržati izvorno zaglavlje HOST. Posljednja verzija ingress-nginx (> 0.22.0)
Aktiviranje zastavice proxy-address-forwarding postavljanjem varijable okruženja PROXY_ADDRESS_FORWARDING u true omogućuje Keycloak-u da shvati da radi iza proxyja.
Također treba uključiti sticky sessions u ingress. Keycloak koristi distribuirani cache Infinispan za pohranu podataka povezanih s trenutnom autentifikacijskom sesijom i korisničkom sesijom. Cachevi rade s jednim vlasnikom prema zadanim postavkama, drugim riječima, ova posebna sesija se pohranjuje na određenom čvoru klastera, a ostali čvorovi trebaju je zahtijevati udaljeno ako im zatreba pristup toj sesiji.
Konkretno, kod nas suprotno dokumentaciji nije radila povezanost s sesijom imenom cookie
AUTH_SESSION_ID. Keycloak je zablokirao preusmjeravanje, zato preporučujemo odabir drugog imena cookie za sticky sesiju.
Također, Keycloak povezuje ime čvora koji je prvi odgovorio na AUTH_SESSION_ID, a budući da svaki čvor u visoko dostupnoj varijanti koristi istu bazu podataka, svaki od njih odvojen i jedinstven identifikator čvora za upravljanje transakcijama. Preporučuje se postaviti JAVA_OPTS parametre jboss.node.name i jboss.tx.node.id jedinstvenima za svaki čvor — možete, na primjer, koristiti ime poda. Ako ćete postaviti ime poda — ne zaboravite na ograničenje od 23 znaka za varijable jboss, pa je bolje koristiti StatefulSet umjesto Deployment-a.
Još jedan problem — ako se pod ukloni ili ponovno pokrene, njegov cache se gubi. Uzimajući u obzir to, preporučuje se postaviti broj vlasnika cache-a za sve cache-ove na najmanje dva, tako da će ostati kopija cache-a. Rješenje je pokrenuti prilikom pokretanja poda, stavite ga u direktorij /opt/jboss/startup-scripts u kontejneru:
Sadržaj skripte
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
echo * Postavljanje CACHE_OWNERS na "${env.CACHE_OWNERS}" u svim cache-containerima
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=owners, value=${env.CACHE_OWNERS:1})
run-batch
stop-embedded-servernakon čega postavite vrijednost varijable okruženja CACHE_OWNERS na potrebnu.
Privatna mreža s podrškom za IP multicast
Ako koristite Weavenet kao CNI, multicast će raditi odmah — i vaši Keycloak čvorovi će se vidjeti jedni s drugima čim se pokrenu.
Ako nemate podršku za IP multicast u Kubernetes klasteru, možete konfigurirati JGroups da radi s drugim protokolima za pronalaženje čvorova.
Prva opcija je upotreba KUBE_DNS, koji koristi headless service za pronalaženje čvorova Keycloak, jednostavno proslijedite JGroups ime usluge koja će se koristiti za pronalaženje čvorova.
Još jedna opcija je primjena metode KUBE_PING, koja radi s API-jem za pronalaženje čvorova (morate postaviti serviceAccount s pravima list i preuzmi, nakon čega postavite podele za rad s ovom serviceAccount).
Način pronalaženja čvorova za JGroups konfigurira se postavljanjem varijabli okruženja JGROUPS_DISCOVERY_PROTOCOL i JGROUPS_DISCOVERY_PROPERTIES. Za KUBE_PING morate odabrati podele postavljanjem namespace i labels.
️ Ako koristite multicast i pokrećete dva ili više klastera Keycloak unutar jednog Kubernetes klastera (recimo, jedan u namespace
production, drugi —staging) — čvorovi jednog klastera Keycloak mogu se pridružiti drugom klasteru. Obavezno koristite jedinstvenu multicast adresu za svaki klaster postavljanjem varijablijboss.default.multicast.addressijboss.modcluster.multicast.addressuJAVA_OPTS.
Replikacija između podatkovnih centara

Povezivost
Keycloak koristi više zasebnih klastera Infinispan cache-a za svaki podatkovni centar u kojem se nalaze klasteri Keycloak, sastavljeni od čvorova Keycloak. Međutim, nema razlike između čvorova Keycloak u različitim podatkovnim centrima.
Keycloak čvorovi koriste vanjsku Java Data Grid (Infinispan servere) za povezivanje između data centara. Veza koristi protokol .
Infinispan predmemorije moraju biti konfigurirane s atributom remoteStore, tako da se podaci mogu pohranjivati u udaljenim (na drugom data centru, nap. prevoditelja) predmemorijama. Postoje odvojeni klasteri Infinispan među JDG serverima, tako da će se podaci pohranjeni na JDG1 na lokaciji site1 replicirati na JDG2 na lokaciji site2.
I konačno, primajući server JDG obavještava Keycloak servere svog klastera putem klijent konekcija, što je posebnost HotRod protokola. Keycloak čvorovi na site2 osvježavaju svoje Infinispan predmemorije, a konkretna korisnička sesija postaje također dostupna na Keycloak čvorovima na site2.
Za neke predmemorije također je moguće ne raditi sigurnosne kopije i potpuno se odrekli zapisivanja podataka putem Infinispan servera. Za to treba ukloniti postavku remote-store od određene Infinispan predmemorije (u datoteci standalone-ha.xml), nakon čega određena replicated-cache također neće biti potrebna na strani Infinispan servera.
Postavke predmemorija
Postoje dvije vrste predmemorija u Keycloku:
Lokalna. Nalazi se blizu baze, služi za smanjenje opterećenja na bazi podataka, kao i za smanjenje kašnjenja odgovora. Ovaj tip predmemorije pohranjuje realm, klijente, uloge i korisničke metapodatke. Ova vrsta predmemorije se ne replicira, čak i ako je ova predmemorija dio klastera Keycloak. Ako se izmijeni neka stavka u predmemoriji — ostalim serverima u klasteru šalje se obavijest o promjeni, nakon čega se stavka uklanja iz predmemorije. Vidi opis
workdalje za detaljniji opis procedure.Replicirana. Obraduje korisničke sesije, offline tokene, kao i prati greške prijave za određivanje pokušaja krađe lozinki i drugih napada. Pohranjeni podaci u ovim predmemorijama su privremeni, pohranjuju se samo u radnoj memoriji, ali se mogu replicirati po klasteru.
Infinispan predmemorije
Sesije — koncept u Keycloku, odvojene predmemorije koje se nazivaju authenticationSessions, koriste se za pohranu podataka konkretnih korisnika. Zahtjevi s ovih predmemorija obično trebaju preglednik i Keycloak servere, a ne aplikacije. Ovdje se manifestira ovisnost o sticky sesijama, a takve predmemorije nije potrebno replicirati, čak ni u slučaju Active-Active režima.
Tokeni akcije. Drugačija koncepcija, koja se obično primenjuje za različite scenarije, kada korisnik na primer treba nešto uraditi asinkrono putem e-pošte. Na primer, tokom procedure zaboravljena lozinka keš actionTokens se koristi za praćenje metapodataka povezanih tokena — na primer, token je već iskorišćen i ne može se ponovo aktivirati. Ova vrsta keša obično se mora replicirati između data centara.
Keširanje i zastare sačuvanih podataka radi kako bi smanjila opterećenje na bazi podataka. Ovo keširanje poboljšava performanse, ali dodaje očigledan problem. Ako jedan Keycloak server ažurira podatke, ostali serveri moraju biti obavešteni o tome kako bi mogli ažurirati podatke u svojim keševima. Keycloak koristi lokalne keše realms, users i autorizacija za keširanje podataka iz baze.
Takođe postoji odvojeni keš work, koji se replicira među svim data centrima. On sam ne čuva nikakve podatke iz baze, već služi za slanje poruka o zastari podataka čvorovima klastera između data centara. Drugim rečima, čim se podaci ažuriraju, Keycloak čvor šalje poruku drugim čvorovima u svom data centru, kao i čvorovima drugih data centara. Nakon primanja takve poruke, svaki čvor vrši čišćenje odgovarajućih podataka u svojim lokalnim keševima.
Korisničke sesije. Keševi sa imenima sessions, clientSessions, offlineSessions i offlineClientSessions, obično se repliciraju između data centara i služe za čuvanje podataka o korisničkim sesijama koje su aktivne tokom aktivnosti korisnika u pretraživaču. Ovi keševi rade sa aplikacijom koja obrađuje HTTP zahteve od krajnjih korisnika, tako da su povezani sa sticky sessions i moraju se replicirati između data centara.
Zaštita od napada brute force. Keš loginFailures služi za praćenje podataka o greškama pri prijavi, na primer koliko puta je korisnik uneo pogrešnu lozinku. Replikacija ovog keša je stvar administratora. Ali za tačno brojanje trebalo bi aktivirati replikaciju između data centara. S druge strane, ako se ovi podaci ne repliciraju, performanse se mogu poboljšati, a ako se postavi ovo pitanje — replikaciju se može i ne aktivirati.
Prilikom postavljanja Infinispan klastera potrebno je dodati definicije keševa u datoteku sa podešavanjima:
Potrebno je konfigurirati i pokrenuti Infinispan klaster prije nego što pokrenete Keycloak klaster
Zatim treba konfigurirati remoteStore keširanja za Keycloak. To se može postići skriptom koji se radi na isti način kao i prethodni, koji se koristio za konfiguraciju varijable CACHE_OWNERS, treba ga spremiti u datoteku i staviti u direktorij /opt/jboss/startup-scripts:
Sadržaj skripte
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
echo *** Ažurirajte infinispan podsustav ***
/subsystem=infinispan/cache-container=keycloak:write-attribute(name=module, value=org.keycloak.keycloak-model-infinispan)
echo ** Dodajte udaljeno socket vezanje za infinispan server **
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-cache:add(host=${remote.cache.host:localhost}, port=${remote.cache.port:11222})
echo ** Ažurirajte work element repliciranog cache-a **
/subsystem=infinispan/cache-container=keycloak/replicated-cache=work/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=work,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/replicated-cache=work:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte element sesija distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=sessions,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=sessions:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte offlineSessions element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=offlineSessions,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineSessions:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte clientSessions element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=clientSessions,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=clientSessions:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte offlineClientSessions element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=offlineClientSessions,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=offlineClientSessions:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte loginFailures element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
remote-servers=["remote-cache"],
cache=loginFailures,
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=loginFailures:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte actionTokens element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens/store=remote:add(
passivation=false,
fetch-state=false,
purge=false,
preload=false,
shared=true,
cache=actionTokens,
remote-servers=["remote-cache"],
properties={
rawValues=true,
marshaller=org.keycloak.cluster.infinispan.KeycloakHotRodMarshallerFactory,
protocolVersion=${keycloak.connectionsInfinispan.hotrodProtocolVersion}
}
)
/subsystem=infinispan/cache-container=keycloak/distributed-cache=actionTokens:write-attribute(name=statistics-enabled,value=true)
echo ** Ažurirajte authenticationSessions element distribucijskog cache-a **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=statistics-enabled,value=true)
echo *** Ažurirajte undertow podsustav ***
/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding,value=true)
run-batch
stop-embedded-serverNe zaboravite postaviti JAVA_OPTS za Keycloak čvorove za rad HotRod: remote.cache.host, remote.cache.port i ime usluge jboss.site.name.
Linkovi i dodatna dokumentacija
Članak je preveden i pripremljen za Habr od strane zaposlenika — intenzivni tečajevi, video tečajevi i korporativna obuka od iskusnih stručnjaka (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)
Izvor: habr.com
