
TL; DR: der sil in beskriuwing wêze fan Keycloak, in iepen boarne tagongskontrôlesysteem, analyze fan 'e ynterne struktuer, konfiguraasjedetails.
Ynlieding en Key Ideas
Yn dit artikel sille wy de basisideeën sjen om yn gedachten te hâlden by it ynsetten fan in Keycloak-kluster boppe op Kubernetes.
As jo mear witte wolle oer Keycloak, ferwize dan nei de keppelings oan 'e ein fan it artikel. Om mear ûnderdompele te wurden yn 'e praktyk, kinne jo studearje mei in module dy't de haadideeën fan dit artikel ymplementearret (de lanseargids is d'r, dit artikel sil in oersjoch jaan fan it apparaat en ynstellingen, ca. oersetter).
Keycloak is in wiidweidich systeem skreaun yn Java en boud boppe op in applikaasjetsjinner . Koartsein, it is in ramt foar autorisaasje dy't federaasje- en SSO-mooglikheden (single sign-on) foar applikaasjes brûkers jout.
Wy noegje jo út om de offisjele te lêzen of foar detaillearre begryp.
Keycloak opstarten
Keycloak fereasket twa persistente gegevensboarnen om te rinnen:
- In databank dy't brûkt wurdt om fêststelde gegevens op te slaan, lykas brûkersynformaasje
- Datagrid-cache, dy't brûkt wurdt om gegevens út 'e databank te cache, en ek om wat koart libbene en faak feroarjende metadata te bewarjen, lykas brûkerssesjes. Implementearre , dy't normaal signifikant flugger is as de databank. Mar yn alle gefallen binne de gegevens opslein yn Infinispan ephemeral - en it hoecht net oeral te wurde bewarre as it kluster opnij wurdt starte.
Keycloak wurket yn fjouwer ferskillende modi:
- Wenstich - ien en mar ien proses, konfigurearre fia in bestân standalone.xml
- Reguliere kluster (opsje mei hege beskikberens) - alle prosessen moatte deselde konfiguraasje brûke, dy't manuell moatte wurde syngronisearre. Ynstellings wurde opslein yn in bestân standalone-ha.xml, Dêrneist moatte jo meitsje dielde tagong ta de databank en in load balancer.
- Domein kluster - it begjinnen fan in kluster yn normale modus wurdt gau in routine en saaie taak as it kluster groeit, om't elke kear as de konfiguraasje feroaret, alle wizigingen moatte wurde makke op elke klusterknoop. Domeinmodus fan operaasje lost dit probleem op troch wat dielde opslachlokaasje yn te stellen en de konfiguraasje te publisearjen. Dizze ynstellings wurde opslein yn it bestân domain.xml
- Replikaasje tusken datasintra - as jo Keycloak wolle útfiere yn in kluster fan ferskate datasintra, meast op ferskate geografyske lokaasjes. Yn dizze opsje sil elk datasintrum in eigen kluster fan Keycloak-tsjinners hawwe.
Yn dit artikel sille wy de twadde opsje yn detail beskôgje, dat is reguliere kluster, En wy sille ek in bytsje oanreitsje op it ûnderwerp fan replikaasje tusken datasintra, om't it sin makket om dizze twa opsjes yn Kubernetes út te fieren. Gelokkich is d'r yn Kubernetes gjin probleem mei it syngronisearjen fan de ynstellingen fan ferskate pods (Keycloak-knooppunten), dus domein kluster It sil net heul lestich wêze om te dwaan.
Tink derom ek dat it wurd kluster foar de rest fan it artikel sil jilde allinnich foar in groep Keycloak knopen wurkje gear, der is gjin needsaak om te ferwizen nei in Kubernetes kluster.
Reguliere Keycloak kluster
Om Keycloak yn dizze modus út te fieren hawwe jo nedich:
- konfigurearje eksterne dielde databank
- ynstallearje load balancer
- hawwe in ynterne netwurk mei IP multicast stipe
Wy sille it opsetten fan in eksterne databank net besprekke, om't it net it doel fan dit artikel is. Lit ús oannimme dat d'r earne in wurkjende databank is - en wy hawwe der in ferbiningspunt mei. Wy sille dizze gegevens gewoan tafoegje oan 'e omjouwingsfariabelen.
Om better te begripen hoe't Keycloak wurket yn in failover (HA) kluster, is it wichtich om te witten hoefolle it allegear hinget ôf fan Wildfly's klustermooglikheden.
Wildfly brûkt ferskate subsystemen, guon fan harren wurde brûkt as load balancer, guon foar skuld tolerânsje. De load balancer soarget foar beskikberens fan applikaasjes as in klusterknooppunt wurdt oerladen, en fouttolerânsje soarget foar beskikberens fan applikaasjes sels as guon klusterknooppunten mislearje. Guon fan dizze subsystemen:
-
mod_cluster: Wurket yn gearhing mei Apache as HTTP-load balancer, hinget ôf fan TCP multicast om standert hosts te finen. Kin wurde ferfongen troch in eksterne balancer. -
infinispan: In ferspraat cache mei help fan JGroups kanalen as ferfier laach. Derneist kin it it HotRod-protokol brûke om te kommunisearjen mei in eksterne Infinispan-kluster om cache-ynhâld te syngronisearjen. -
jgroups: Biedt stipe foar groepkommunikaasje foar heul beskikbere tsjinsten basearre op JGroups-kanalen. Neamde pipes kinne applikaasje-ynstânsjes yn in kluster wurde ferbûn yn groepen, sadat de kommunikaasje eigenskippen hat lykas betrouberens, oarderlikens en gefoelichheid foar mislearrings.
Load Balancer
By it ynstallearjen fan in balancer as in yngongskontrôler yn in Kubernetes-kluster, is it wichtich om de folgjende dingen yn gedachten te hâlden:
Keycloak giet derfan út dat it ôfstânadres fan 'e kliïnt dy't fia HTTP ferbine mei de autentikaasjetsjinner it echte IP-adres fan' e kliïntkomputer is. Balancer- en yngongsynstellingen moatte HTTP-headers korrekt ynstelle X-Forwarded-For и X-Forwarded-Proto, en bewarje ek de orizjinele titel HOST. Lêtste fersje ingress-nginx (> 0.22.0)
It aktivearjen fan de flagge proxy-address-forwarding troch it ynstellen fan in omjouwingsfariabele PROXY_ADDRESS_FORWARDING в true jout Keycloak it begryp dat it wurket efter in proxy.
Jo moatte ek ynskeakelje kleverige sesjes yn yngong. Keycloak brûkt in ferspraat Infinispan-cache om gegevens te bewarjen dy't ferbûn binne mei de aktuele autentikaasje-sesje en brûkerssesje. Caches wurkje standert mei ien eigener, mei oare wurden, dy bepaalde sesje wurdt opslein op ien knooppunt yn it kluster, en oare knooppunten moatte it op ôfstân freegje as se tagong hawwe ta dy sesje.
Spesifyk, yn tsjinstelling ta de dokumintaasje, it heakjen fan in sesje mei de namme koekje wurke net foar ús
AUTH_SESSION_ID. Keycloak hat in trochferwizing-loop, dus wy riede oan om in oare koekje-namme te kiezen foar de kleverige sesje.
Keycloak hechtet ek de namme oan fan it knooppunt dêr't earst op reagearre AUTH_SESSION_ID, en om't elke knooppunt yn 'e heul beskikbere ferzje deselde databank brûkt, elk fan har in aparte en unike knooppuntidentifikator foar it behearen fan transaksjes. It is oan te rieden om yn te setten JAVA_OPTS parameters jboss.node.name и jboss.tx.node.id unyk foar elke knooppunt - jo kinne bygelyks de namme fan 'e pod pleatse. As jo in podnamme pleatse, ferjit dan net de limyt fan 23 karakters foar jboss-fariabelen, dus it is better om in StatefulSet te brûken ynstee fan in Deployment.
In oare rake - as de pod wurdt wiske of opnij starte, is syn cache ferlern. Mei dit yn 'e rekken is it de muoite wurdich om it oantal cache-eigners foar alle cache op syn minst twa yn te stellen, sadat der in kopy fan' e cache bliuwt. De oplossing is om te rinnen by it starten fan de pod, it pleatsen yn 'e map /opt/jboss/startup-scripts yn 'e kontener:
Skriptynhâld
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
echo * Setting CACHE_OWNERS to "${env.CACHE_OWNERS}" in all cache-containers
/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-server
set dan de wearde fan 'e omjouwingsfariabele yn CACHE_OWNERS oan de fereaske.
Privee netwurk mei IP multicast-stipe
As jo Weavenet brûke as CNI, sil multicast fuortendaliks wurkje - en jo Keycloak-knooppunten sille inoar sjen sa gau as se wurde lansearre.
As jo gjin ip multicast-stipe hawwe yn jo Kubernetes-kluster, kinne jo JGroups ynstelle om te wurkjen mei oare protokollen om knopen te finen.
De earste opsje is te brûken KUBE_DNSdy't brûkt headless service om Keycloak-knooppunten te finen, passe jo JGroups gewoan de namme fan 'e tsjinst troch dy't brûkt wurde sil om de knopen te finen.
In oare opsje is om de metoade te brûken KUBE_PING, dy't wurket mei de API om te sykjen nei knooppunten (jo moatte konfigurearje serviceAccount mei rjochten list и get, en konfigurearje dan de pods om hjirmei te wurkjen serviceAccount).
De manier wêrop JGroups knooppunten fine wurdt konfigureare troch omjouwingsfariabelen yn te stellen JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. foar KUBE_PING jo moatte pods selektearje troch te freegjen namespace и labels.
️ As jo multycast brûke en twa of mear Keycloak-klusters útfiere yn ien Kubernetes-kluster (litte wy sizze ien yn nammeromte
production, de twadde -staging) - knopen fan ien Keycloak-kluster kinne meidwaan oan in oare kluster. Wês wis dat jo in unyk multicast-adres brûke foar elk kluster troch fariabelen yn te stellenjboss.default.multicast.addressиjboss.modcluster.multicast.addressвJAVA_OPTS.
Replikaasje tusken datasintra

Konnektiviteit
Keycloak brûkt meardere aparte Infinispan-cache-klusters foar elk datasintrum wêr't Keycloack-klusters opboud binne út Keycloak-knooppunten. Mar d'r is gjin ferskil tusken Keycloak-knooppunten yn ferskate datasintra.
Keycloak-knooppunten brûke in eksterne Java Data Grid (Infinispan-tsjinners) foar kommunikaasje tusken datasintra. Kommunikaasje wurket neffens it protokol .
Infinispan-caches moatte wurde konfigureare mei it attribút remoteStore, sadat de gegevens op ôfstân kinne wurde opslein (yn in oar datasintrum, ca. oersetter) caches. D'r binne aparte infinispan-klusters ûnder de JDG-tsjinners, sadat de gegevens op JDG1 op 'e side opslein binne site1 sil wurde replikeare nei JDG2 op side site2.
En as lêste, de ûntfangende JDG-tsjinner ynformearret de Keycloak-tsjinners fan har kluster fia kliïntferbiningen, wat in eigenskip is fan it HotRod-protokol. Keycloak knopen op site2 update harren Infinispan caches en de spesifike brûker sesje wurdt ek beskikber op de Keycloak knopen op site2.
Foar guon caches is it ek mooglik om gjin backups te meitsjen en it skriuwen fan gegevens fia de Infinispan-tsjinner folslein te foarkommen. Om dit te dwaan moatte jo de ynstelling fuortsmite remote-store spesifike Infinispan-cache (yn it bestân standalone-ha.xml), wêrnei't guon spesifike replicated-cache sil ek net mear nedich wêze oan de Infinispan tsjinner kant.
It opsetten fan caches
D'r binne twa soarten caches yn Keycloak:
-
Pleatslik. It leit njonken de databank en tsjinnet om de lading op 'e databank te ferminderjen, en ek om antwurdlatens te ferminderjen. Dit soarte fan cache bewarret ryk, kliïnten, rollen en brûkersmetadata. Dit soarte fan cache wurdt net replicated, sels as de cache is diel fan in Keycloak kluster. As in yngong yn 'e cache feroaret, wurdt in berjocht oer de feroaring stjoerd nei de oerbleaune tsjinners yn it kluster, wêrnei't de yngong út' e cache útsletten wurdt. Sjoch beskriuwing
workSjoch hjirûnder foar in mear detaillearre beskriuwing fan de proseduere. -
Replikearre. Ferwurket brûkerssesjes, offline tokens, en kontrolearret ek oanmeldingsfouten om besykjen fan phishing mei wachtwurd en oare oanfallen te detektearjen. De gegevens opslein yn dizze caches is tydlik, opslein allinnich yn RAM, mar kin wurde replicated oer de kluster.
Infinispan caches
Sesjes - in konsept yn Keycloak, aparte caches neamd authenticationSessions, wurde brûkt om gegevens fan spesifike brûkers op te slaan. Fersiken fan dizze caches binne normaal nedich troch de browser en Keycloak-tsjinners, net troch applikaasjes. Dit is wêr't de ôfhinklikens fan kleverige sesjes yn 't spul komt, en sokke caches sels hoege net te replikearre, sels yn it gefal fan Active-Active modus.
Aksje Tokens. In oar konsept, meastentiids brûkt foar ferskate senario's as de brûker bygelyks wat asynchronysk moat dwaan mei de post. Bygelyks, tidens de proseduere forget password ûnthâld actionTokens brûkt om metadata fan assosjearre tokens te folgjen - bygelyks in token is al brûkt en kin net wer aktivearre wurde. Dit type cache moat typysk wurde replikearre tusken datasintra.
Caching en ferâldering fan bewarre gegevens wurket om de lêst op 'e databank te ûntlêsten. Dit soarte fan caching ferbettert prestaasjes, mar foeget in dúdlik probleem ta. As ien Keycloak-tsjinner gegevens bywurket, moatte de oare tsjinners op 'e hichte brocht wurde, sadat se de gegevens yn har caches bywurkje kinne. Keycloak brûkt lokale caches realms, users и authorization foar caching gegevens út de databank.
Der is ek in aparte cache work, dat wurdt replikearre oer alle datasintra. It sels bewarret gjin gegevens fan 'e databank, mar tsjinnet om berjochten oer gegevensferâldering te stjoeren nei klusterknooppunten tusken datasintra. Mei oare wurden, sa gau as de gegevens bywurke binne, stjoert de Keycloak-knooppunt in berjocht nei oare knooppunten yn har datacenter, lykas knooppunten yn oare datasintra. Nei ûntfangst fan sa'n berjocht wisket elke knoop de oerienkommende gegevens yn syn lokale caches.
Meidogger sesjes. Caches mei nammen sessions, clientSessions, offlineSessions и offlineClientSessions. Dizze caches wurkje mei de applikaasje dy't HTTP-oanfragen fan ein brûkers ferwurket, sadat se ferbûn binne mei kleverige sesjes en moatte wurde replikearre tusken datasintra.
Brute krêft beskerming. Cache loginFailures Wurdt brûkt om oanmeldingsflatergegevens te folgjen, lykas hoefolle kearen in brûker in ferkeard wachtwurd ynfierde. Replikaasje fan dizze cache is de ferantwurdlikens fan de behearder. Mar foar in krekte berekkening is it wurdich om replikaasje tusken datasintra te aktivearjen. Mar oan 'e oare kant, as jo dizze gegevens net replikearje, sille jo prestaasjes ferbetterje, en as dit probleem ûntstiet, kin replikaasje net aktivearre wurde.
As jo in Infinispan-kluster útrolje, moatte jo cache-definysjes tafoegje oan it ynstellingsbestân:
<replicated-cache-configuration name="keycloak-sessions" mode="ASYNC" start="EAGER" batching="false">
</replicated-cache-configuration>
<replicated-cache name="work" configuration="keycloak-sessions" />
<replicated-cache name="sessions" configuration="keycloak-sessions" />
<replicated-cache name="offlineSessions" configuration="keycloak-sessions" />
<replicated-cache name="actionTokens" configuration="keycloak-sessions" />
<replicated-cache name="loginFailures" configuration="keycloak-sessions" />
<replicated-cache name="clientSessions" configuration="keycloak-sessions" />
<replicated-cache name="offlineClientSessions" configuration="keycloak-sessions" />
Jo moatte it Infinispan-kluster konfigurearje en begjinne foardat jo it Keycloak-kluster begjinne
Dan moatte jo konfigurearje remoteStore foar Keycloak-caches. Om dit te dwaan is in skript genôch, dat wurdt dien lykas de foarige, dy't brûkt wurdt om de fariabele yn te stellen CACHE_OWNERS, Jo moatte it opslaan yn in triem en set it yn in map /opt/jboss/startup-scripts:
Skriptynhâld
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
echo *** Update infinispan subsystem ***
/subsystem=infinispan/cache-container=keycloak:write-attribute(name=module, value=org.keycloak.keycloak-model-infinispan)
echo ** Add remote socket binding to 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 ** Update replicated-cache work element **
/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 ** Update distributed-cache sessions element **
/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 ** Update distributed-cache offlineSessions element **
/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 ** Update distributed-cache clientSessions element **
/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 ** Update distributed-cache offlineClientSessions element **
/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 ** Update distributed-cache loginFailures element **
/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 ** Update distributed-cache actionTokens element **
/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 ** Update distributed-cache authenticationSessions element **
/subsystem=infinispan/cache-container=keycloak/distributed-cache=authenticationSessions:write-attribute(name=statistics-enabled,value=true)
echo *** Update undertow subsystem ***
/subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=proxy-address-forwarding,value=true)
run-batch
stop-embedded-server
Ferjit net te ynstallearjen JAVA_OPTS foar Keycloak-knooppunten om HotRod út te fieren: remote.cache.host, remote.cache.port en tsjinst namme jboss.site.name.
Links en oanfoljende dokumintaasje
It artikel waard oerset en taret foar Habr troch meiwurkers - yntinsive kursussen, fideokursussen en bedriuwsoplieding fan praktisearjende spesjalisten (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)
Boarne: www.habr.com
