
TL;DR: er zal een beschrijving zijn van Keycloak, een open source toegangsbeheersysteem, een analyse van de interne structuur, en details over de configuratie.
Inleiding en basisideeƫn
In dit artikel bekijken we de belangrijkste ideeƫn om rekening mee te houden bij het uitrollen van een Keycloak-cluster bovenop Kubernetes.
Als je meer gedetailleerde informatie over Keycloak wilt, raadpleeg dan de links aan het einde van het artikel. Voor een diepere praktijkervaring kun je onze bekijken met de module die de belangrijkste ideeƫn van dit artikel implementeert (de handleiding voor het opstarten is daar ook, in dit artikel vind je een overzicht van de structuur en instellingen, opmerking van de vertaler).
Keycloak is een uitgebreid systeem, geschreven in Java en gebouwd op de applicatieserver Kort gezegd, het is een framework voor autorisatie dat gebruikers van applicaties federatie en de mogelijkheid tot SSO (single sign-on) biedt.
We nodigen je uit om de officiƫle of te lezen voor een gedetailleerd begrip.
Keycloak opstarten
Voor Keycloak zijn er twee persistente gegevensbronnen nodig om op te starten:
- Een database voor het opslaan van vaste gegevens, zoals informatie over gebruikers.
- Datagrid-cache, dat wordt gebruikt voor het cachen van gegevens uit de database en voor het opslaan van enkele kortlevende en vaak veranderende metadata, zoals gebruikerssessies. Dit wordt geĆÆmplementeerd met dat over het algemeen veel sneller is dan de database. Maar in elk geval zijn de in Infinispan opgeslagen gegevens efemeer ā en hoeven ze niet ergens te worden opgeslagen bij een herstart van het cluster.
Keycloak werkt in vier verschillende modi:
- Normaal ā ƩƩn enkele proces, geconfigureerd via het bestand standalone.xml.
- Normaal cluster (high-availability optie) ā alle processen moeten dezelfde configuratie gebruiken, die handmatig moet worden gesynchroniseerd. Instellingen worden opgeslagen in het bestand standalone-ha.xml,en daarnaast moet er gedeelde toegang tot de database en een load balancer worden ingesteld.
- Domeincluster ā het draaien van een cluster in normale modus wordt snel routine en saai bij het groeien van het cluster, omdat elke wijziging in de configuratie op elke knoop van het cluster moet worden doorgevoerd. De domein werkwijze lost dit probleem op door een gemeenschappelijke opslagruimte en publicatie van de configuratie in te stellen. Deze instellingen zijn opgeslagen in het bestand domain.xml.
- Replicatie tussen datacenters ā als u Keycloak wilt draaien in een cluster van meerdere datacenters, meestal op geografisch verschillende locaties. In deze werkwijze heeft elk datacenter zijn eigen Keycloak-cluster servers.
In dit artikel bekijken we gedetailleerd de tweede optie, namelijk een gewoon cluster, en we zullen ook kort het onderwerp behandelen van replicatie tussen datacenters, aangezien deze twee opties logisch zijn om in Kubernetes te draaien. Gelukkig is er in Kubernetes geen probleem met het synchroniseren van de instellingen van meerdere pods (Keycloak knooppunten), zodat domeincluster niet al te moeilijk te realiseren zal zijn.
Houd er ook rekening mee dat het woord cluster tot het einde van het artikel uitsluitend zal worden gebruikt met betrekking tot een groep Keycloak knooppunten die samenwerken, er is geen noodzaak om naar het Kubernetes-cluster te verwijzen.
Gewoon Keycloak-cluster
Om Keycloak in deze modus te starten, moet u:
- een externe gedeelde database instellen
- een load balancer installeren
- een intern netwerk hebben met ondersteuning voor ip multicast
We zullen de configuratie van de externe database niet bespreken, omdat dit niet het doel van dit artikel is. Laten we aannemen dat er ergens een werkende database is ā en dat we toegangspunt hebben. We voegen deze gegevens gewoon toe aan de omgevingsvariabelen.
Om beter te begrijpen hoe Keycloak werkt in een high availability (HA) cluster, is het belangrijk te weten hoezeer dit alles afhankelijk is van de clustering mogelijkheden van Wildfly.
Wildfly past verschillende subsystems toe, sommige worden gebruikt als load balancer, andere voor failover. De load balancer zorgt voor de beschikbaarheid van de applicatie bij overbelasting van een cluster knooppunt, terwijl de failover garandeert dat de applicatie beschikbaar blijft, zelfs als een deel van de cluster knooppunten uitvalt. Enkele van deze subsystems zijn:
mod_cluster: werkt samen met Apache als een HTTP load balancer, afhankelijk van TCP multicast voor het vinden van knooppunten per default. Kan worden vervangen door een externe load balancer.infinispan: een gedistribueerde cache die JGroups kanalen als transportlaag gebruikt. Kan aanvullend het HotRod protocol toepassen voor communicatie met een externe Infinispan cluster voor het synchroniseren van de cache-inhoud.jgroups: biedt groepscommunicatiesupport voor hoogbeschikbare services op basis van JGroups-kanalen. Genaamd kanalen stellen applicatie-instanties in het cluster in staat om verbinding te maken in groepen, waardoor de communicatie eigenschappen heeft zoals betrouwbaarheid, volgordelijkheid en storingsgevoeligheid.
Load Balancer
Bij het installeren van de load balancer als ingress-controller in het Kubernetes-cluster zijn er een aantal dingen om in gedachten te houden:
Keycloak werkt zo dat het externe IP-adres van de cliƫnt die via HTTP verbinding maakt met de authenticatieserver het echte IP-adres van de klantcomputer is. De instellingen van de load balancer en ingress moeten correct de HTTP-headers instellen. X-Forwarded-For en X-Forwarded-Proto, evenals de oorspronkelijke header behouden HOST. De laatste versie ingress-nginx (> 0.22.0)
Het inschakelen van de vlag proxy-address-forwarding door een omgevingsvariabele in te stellen PROXY_ADDRESS_FORWARDING in true geeft Keycloak informatie dat het achter een proxy werkt.
Daarnaast moet sticky sessions in de ingress worden ingeschakeld. Keycloak past een gedistribueerde Infinispan-cache toe om gegevens met betrekking tot de huidige authenticatiesessie en gebruikerssessie op te slaan. Caches werken standaard met ƩƩn eigenaar, met andere woorden, deze specifieke sessie wordt op een bepaalde knoop in het cluster opgeslagen, en andere knopen moeten deze op afstand opvragen als ze toegang tot deze sessie nodig hebben.
Specifiek bij ons werkte de cookie met de naam 'AUTH_SESSION_ID' niet, in tegenstelling tot de documentatie.
Keycloak leidde om, daarom raden we aan een andere cokenaam voor de sticky session te kiezen.Bovendien hecht Keycloak de naam van de eerste knoop die antwoordt aan
, en aangezien elke knoop in de hoogbeschikbare variant dezelfde database gebruikt, moet elke knoop Keycloak leidde om, daarom raden we aan een andere cokenaam voor de sticky session te kiezen.over een JAVA_OPTS parameters jboss.node.name jboss.tx.node.id en uniek voor elke knoop in te stellen - je kunt bijvoorbeeld de naam van de pod gebruiken. Als je de naam van de pod gebruikt, vergeet dan niet de beperking van 23 tekens voor jboss-variabelen, dus het is beter om StatefulSet en niet Deployment te gebruiken. uniek voor elk knooppunt - je kunt bijvoorbeeld de naam van de pod instellen. Als je de naam van de pod gaat instellen, vergeet dan de beperking van 23 tekens voor jboss-variabelen niet, dus het is beter om StatefulSet te gebruiken in plaats van Deployment.
Een andere valkuil is dat als de pod wordt verwijderd of opnieuw opgestart, de cache verloren gaat. Houd hier rekening mee door het aantal cache-eigenaren voor alle caches op ten minste twee in te stellen, zodat er een kopie van de cache blijft. Oplossing: voer het uit bij het opstarten van de pod, door het in de map /opt/jboss/startup-scripts in de container:
De inhoud van het script
embed-server --server-config=standalone-ha.xml --std-out=echo
batch
echo * CACHE_OWNERS wordt ingesteld op "${env.CACHE_OWNERS}" in alle 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-serververvolgens de omgevingsvariabele CACHE_OWNERS instellen op de vereiste waarde.
Privaat netwerk met ondersteuning voor ip multicast
Als je Weavenet gebruikt als CNI, werkt multicast onmiddellijk ā en je Keycloak-nodes kunnen elkaar zien zodra ze zijn opgestart.
Als je geen ondersteuning voor ip multicast in het Kubernetes-cluster hebt, kun je JGroups configureren om met andere protocollen te werken voor het vinden van nodes.
De eerste optie is het gebruik van KUBE_DNS, dat gebruikt maakt van headless service voor het vinden van Keycloak-nodes, je geeft gewoon JGroups de servicenaam door die zal worden gebruikt voor het vinden van nodes.
Een andere optie is het toepassen van de methode KUBE_PING, die werkt met de API voor het vinden van nodes (je moet serviceAccount met rechten lijst en krijgen, waarna je de pods configureert om met deze serviceAccount).
De methode voor het vinden van nodes voor JGroups wordt ingesteld door de omgevingsvariabelen JGROUPS_DISCOVERY_PROTOCOL en JGROUPS_DISCOVERY_PROPERTIESin te stellen. Voor KUBE_PING moet je de pods selecteren door namespace en labels.
ļø Als je multicast gebruikt en twee of meer Keycloak-clusters in hetzelfde Kubernetes-cluster draait (bijvoorbeeld ƩƩn in de namespace
production, de andere āstaging) ā nodes van het ene Keycloak-cluster kunnen zich bij het andere cluster aansluiten. Zorg ervoor dat je een uniek multicastadres voor elk cluster gebruikt door de variabelen in te stellenjboss.default.multicast.addressenjboss.modcluster.multicast.addressinparameters.
Replicatie tussen datacenters

Connectiviteit
Keycloak maakt gebruik van meerdere afzonderlijke Infinispan-cacheclusters voor elk datacenter waar de Keycloak-clusters zijn samengesteld uit Keycloak-knooppunten. Er is echter geen verschil tussen de Keycloak-knooppunten in verschillende datacenters.
Keycloak-knooppunten gebruiken een externe Java Data Grid (Infinispan-servers) om te communiceren tussen datacenters. De verbinding werkt via het protocol .
Infinispan-caches moeten worden geconfigureerd met de attribuut remoteStore, zodat gegevens kunnen worden opgeslagen in externe (in een ander datacenter, opmerking van de vertaler) caches. Er zijn afzonderlijke Infinispan-clusters onder JDG-servers, zodat gegevens die zijn opgeslagen op JDG1 op de locatie site1 worden gerepliceerd naar JDG2 op de locatie site2.
Tenslotte meldt de ontvangende JDG-server de Keycloak-servers van zijn cluster via clientverbindingen, wat een kenmerk is van het HotRod-protocol. Keycloak-knooppunten op site2 werken hun Infinispan-caches bij, waardoor een specifieke gebruikerssessie ook toegankelijk wordt op de Keycloak-knooppunten op site2.
Voor sommige caches is het ook mogelijk om geen back-ups te maken en volledig af te zien van het opslaan van gegevens via de Infinispan-server. Hiervoor moet de instelling remote-store voor de specifieke Infinispan-cache (in het bestand standalone-ha.xml,), waarna een bepaalde replicated-cache ook niet meer nodig zal zijn aan de kant van de Infinispan-server.
Cache-instellingen
Er zijn twee soorten caches in Keycloak:
Lokaal. Deze bevindt zich dicht bij de database en dient om de belasting op de database te verminderen en de responstijd te verlagen. In dit type cache worden realm, clients, rollen en gebruikersmetadata opgeslagen. Dit type cache wordt niet gerepliceerd, zelfs niet als deze cache deel uitmaakt van een cluster van Keycloak. Als er een wijziging plaatsvindt in een bepaalde record in de cache, wordt er een bericht met de wijziging naar de andere servers in het cluster verzonden, waarna de record uit de cache wordt geƫxcludeerd. Zie de beschrijving
workvoor een meer gedetailleerde beschrijving van de procedure.Repliceerbaar. Behandelt gebruikerssessies, offline tokens en houdt toezicht op inlogfouten om pogingen tot phishing van wachtwoorden en andere aanvallen te identificeren. Gegevens die in deze caches worden opgeslagen, zijn tijdelijk, worden alleen in het geheugen opgeslagen, maar kunnen door het cluster worden gerepliceerd.
Infinispan-caches
Sessies ā een concept in Keycloak, afzonderlijke caches die worden genoemd authenticationSessions, worden gebruikt voor het opslaan van gegevens van specifieke gebruikers. Verzoeken van deze caches zijn doorgaans nodig voor browsers en Keycloak-servers, niet voor applicaties. Hier komt de afhankelijkheid van sticky sessions naar voren, en dergelijke caches hoeven niet te worden gerepliceerd, zelfs niet in een Active-Active-configuratie.
Actietokens. Een concept dat gewoonlijk wordt toegepast in verschillende scenario's, bijvoorbeeld wanneer een gebruiker iets asynchroon via e-mail moet doen. Bijvoorbeeld tijdens de procedure wachtwoord vergeten cache actionTokens wordt gebruikt voor het volgen van metadata van gerelateerde tokens - bijvoorbeeld een token dat al is gebruikt en niet opnieuw kan worden geactiveerd. Dit type cache moet doorgaans worden gerepliceerd tussen datacenters.
Caching en veroudering van opgeslagen gegevens werkt om de belasting van de database te verminderen. Dergelijke caching verbetert de prestaties, maar voegt een duidelijke uitdaging toe. Als een Keycloak-server gegevens bijwerkt, moeten de andere servers hierover worden geĆÆnformeerd, zodat zij hun gegevens in hun caches kunnen actualiseren. Keycloak maakt gebruik van lokale caches realms, gebruikers en autorisatie voor het cachen van gegevens uit de database.
Er is ook een aparte cache work, die wordt gerepliceerd over alle datacenters. Deze zelf bevat geen gegevens uit de database, maar dient voor het verzenden van berichten over het verouderen van gegevens aan clusterknopen tussen datacenters. Met andere woorden, zodra de gegevens worden bijgewerkt, stuurt de Keycloak-knoop een bericht naar andere knopen in zijn datacenter en naar knopen in andere datacenters. Na ontvangst van een dergelijk bericht voert elke knoop een opruiming uit van de relevante gegevens in zijn lokale caches.
Gebruikerssessies. Caches met de namen sessions, clientSessions, offlineSessions en offlineClientSessions, worden doorgaans gerepliceerd tussen datacenters en worden gebruikt voor het opslaan van gegevens over gebruikerssessies die actief zijn tijdens de activiteit van de gebruiker in de browser. Deze caches werken samen met de applicatie die HTTP-verzoeken van eindgebruikers verwerkt, zodat ze verband houden met sticky sessions en moeten worden gerepliceerd tussen datacenters.
Beveiliging tegen brute force aanvallen. Cache loginFailures wordt gebruikt om inlogfoutgegevens bij te houden, zoals het aantal keren dat een gebruiker een onjuist wachtwoord heeft ingevoerd. Het repliceren van deze cache is de verantwoordelijkheid van de beheerder. Voor een nauwkeurige telling is het echter raadzaam replicatie tussen datacenters te activeren. Aan de andere kant, als deze gegevens niet worden gerepliceerd, kan de prestaties verbeteren en als deze vraag opkomt, kan replicatie ook worden overgeslagen.
Bij het uitrollen van het Infinispan-cluster moeten cache-definities aan het configuratiebestand worden toegevoegd:
Het is noodzakelijk om het Infinispan-cluster in te stellen en te starten voordat het Keycloak-cluster wordt opgestart.
Vervolgens moet configureren voor Keycloak caches. Hiervoor is een script voldoende, dat op dezelfde manier wordt gemaakt als het vorige dat werd gebruikt om de variabele in te stellen. remoteStore , moet het worden opgeslagen in een bestand en in de map worden geplaatst. CACHE_OWNERS, je moet het in een bestand opslaan en in de map plaatsen /opt/jboss/startup-scripts:
De inhoud van het script
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-serverVergeet niet in te stellen parameters voor de Keycloak knooppunten voor HotRod: remote.cache.host, remote.cache.port en de servicenaam jboss.site.name.
Links en aanvullende documentatie
Dit artikel is vertaald en voorbereid voor Habr door medewerkers van ā intensieve cursussen, videocursussen en corporate training van werkende professionals (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)
Bron: habr.com
