Keycloak uitvoeren in HA-modus op Kubernetes

Keycloak uitvoeren in HA-modus op Kubernetes

TL; DR: er zal een beschrijving zijn van Keycloak, een open source toegangscontrolesysteem, analyse van de interne structuur, configuratiedetails.

Inleiding en belangrijkste ideeën

In dit artikel zullen we de basisideeën zien waarmee u rekening moet houden bij het implementeren van een Keycloak-cluster bovenop Kubernetes.

Als je meer wilt weten over Keycloak, raadpleeg dan de links aan het einde van het artikel. Om meer ondergedompeld te raken in de praktijk, kun je studeren onze opslagplaats met een module die de belangrijkste ideeën van dit artikel implementeert (de startgids is aanwezig, dit artikel geeft een overzicht van het apparaat en de instellingen, ca. vertaler).

Keycloak is een uitgebreid systeem geschreven in Java en gebouwd bovenop een applicatieserver Wilde vlieg. Kortom, het is een raamwerk voor autorisatie dat federatie- en SSO-mogelijkheden (single sign-on) voor applicatiegebruikers biedt.

Wij nodigen u uit om de ambtenaar te lezen сайт of Wikipedia voor gedetailleerd begrip.

Keycloak lanceren

Keycloak vereist twee persistente gegevensbronnen om te kunnen werken:

  • Een database die wordt gebruikt om gevestigde gegevens op te slaan, zoals gebruikersinformatie
  • Datagrid-cache, die wordt gebruikt om gegevens uit de database in de cache op te slaan, en om enkele kortstondige en vaak veranderende metadata op te slaan, zoals gebruikerssessies. Geïmplementeerd Oneindige span, wat meestal aanzienlijk sneller is dan de database. Maar hoe dan ook, de gegevens die in Infinispan zijn opgeslagen, zijn van tijdelijke aard en hoeven nergens te worden opgeslagen wanneer het cluster opnieuw wordt opgestart.

Keycloak werkt in vier verschillende modi:

  • Normaal - één en slechts één proces, geconfigureerd via een bestand zelfstandig.xml
  • Reguliere cluster (optie voor hoge beschikbaarheid) - alle processen moeten dezelfde configuratie gebruiken, die handmatig moet worden gesynchroniseerd. Instellingen worden opgeslagen in een bestand standalone-ha.xml, daarnaast moet u gedeelde toegang tot de database en een load balancer maken.
  • Domeincluster — het starten van een cluster in de normale modus wordt snel een routineuze en saaie taak naarmate het cluster groeit, aangezien elke keer dat de configuratie verandert, alle wijzigingen op elk clusterknooppunt moeten worden aangebracht. De domeinmodus lost dit probleem op door een gedeelde opslaglocatie in te stellen en de configuratie te publiceren. Deze instellingen worden opgeslagen in het bestand domein.xml
  • Replicatie tussen datacenters — als u Keycloak in een cluster van meerdere datacenters wilt gebruiken, meestal op verschillende geografische locaties. Bij deze optie beschikt elk datacenter over een eigen cluster Keycloak-servers.

In dit artikel zullen we de tweede optie in detail bekijken reguliere cluster, en we zullen ook even ingaan op het onderwerp replicatie tussen datacenters, omdat het zinvol is om deze twee opties in Kubernetes uit te voeren. Gelukkig is er in Kubernetes geen probleem met het synchroniseren van de instellingen van verschillende pods (Keycloak-nodes), dus domeincluster Het zal niet heel moeilijk zijn om te doen.

Houd er ook rekening mee dat het woord TROS Omdat de rest van het artikel uitsluitend van toepassing is op een groep Keycloak-knooppunten die samenwerken, is het niet nodig om naar een Kubernetes-cluster te verwijzen.

Reguliere sleutelmantelcluster

Om Keycloak in deze modus te gebruiken, hebt u het volgende nodig:

  • externe gedeelde database configureren
  • load-balancer installeren
  • een intern netwerk hebben met IP-multicast-ondersteuning

We zullen het opzetten van een externe database niet bespreken, aangezien dit niet het doel van dit artikel is. Laten we aannemen dat er ergens een werkende database is - en dat we daar een verbindingspunt mee hebben. We voegen deze gegevens eenvoudigweg toe aan de omgevingsvariabelen.

Om beter te begrijpen hoe Keycloak werkt in een failover-cluster (HA), is het belangrijk om te weten hoeveel dit allemaal afhangt van de clustermogelijkheden van Wildfly.

Wildfly gebruikt verschillende subsystemen, sommige worden gebruikt als load balancer, andere voor fouttolerantie. De load balancer garandeert de beschikbaarheid van applicaties wanneer een clusterknooppunt overbelast is, en fouttolerantie garandeert de beschikbaarheid van applicaties, zelfs als sommige clusterknooppunten uitvallen. Enkele van deze subsystemen:

  • mod_cluster: Werkt samen met Apache als een HTTP-load balancer, is standaard afhankelijk van TCP-multicast om hosts te vinden. Kan worden vervangen door een externe balancer.

  • infinispan: Een gedistribueerde cache die JGroups-kanalen gebruikt als transportlaag. Bovendien kan het het HotRod-protocol gebruiken om te communiceren met een extern Infinispan-cluster om de cache-inhoud te synchroniseren.

  • jgroups: Biedt ondersteuning voor groepscommunicatie voor maximaal beschikbare services op basis van JGroups-kanalen. Met Named Pipes kunnen applicatie-instanties in een cluster in groepen worden verbonden, zodat de communicatie eigenschappen heeft zoals betrouwbaarheid, ordelijkheid en gevoeligheid voor fouten.

Loadbalancer

Bij het installeren van een balancer als ingress-controller in een Kubernetes-cluster is het belangrijk om de volgende zaken in gedachten te houden:

Keycloak gaat ervan uit dat het externe adres van de client die via HTTP verbinding maakt met de authenticatieserver het echte IP-adres van de clientcomputer is. Balancer- en ingangsinstellingen moeten HTTP-headers correct instellen X-Forwarded-For и X-Forwarded-Protoen sla ook de originele titel op HOST. Laatste versie ingress-nginx (> 0.22.0) schakelt dit standaard uit

Het activeren van de vlag proxy-address-forwarding door een omgevingsvariabele in te stellen PROXY_ADDRESS_FORWARDING в true geeft Keycloak het inzicht dat het achter een proxy werkt.

Je moet ook inschakelen plakkerige sessies bij binnenkomst. Keycloak gebruikt een gedistribueerde Infinispan-cache om gegevens op te slaan die verband houden met de huidige authenticatiesessie en gebruikerssessie. Caches werken standaard met één enkele eigenaar, met andere woorden: die specifieke sessie wordt opgeslagen op een bepaald knooppunt in het cluster, en andere knooppunten moeten er op afstand naar vragen als ze toegang tot die sessie nodig hebben.

Concreet werkte het koppelen van een sessie met de naamcookie, in tegenstelling tot de documentatie, niet voor ons AUTH_SESSION_ID. Keycloak heeft een omleidingslus, dus we raden aan een andere cookienaam te kiezen voor de sticky-sessie.

Keycloak voegt ook de naam toe van het knooppunt waarop het eerst heeft gereageerd AUTH_SESSION_ID, en aangezien elk knooppunt in de versie met hoge beschikbaarheid dezelfde database gebruikt, is elk knooppunt hebbeding een afzonderlijke en unieke knooppuntidentificatie voor het beheren van transacties. Het wordt aanbevolen om in te zetten JAVA_OPTS parameters jboss.node.name и jboss.tx.node.id uniek voor elk knooppunt - u kunt bijvoorbeeld de naam van de pod invoeren. Als u een podnaam invoert, vergeet dan niet de limiet van 23 tekens voor jboss-variabelen, dus het is beter om een ​​StatefulSet te gebruiken in plaats van een Deployment.

Nog een hark: als de pod wordt verwijderd of opnieuw wordt opgestart, gaat de cache verloren. Hiermee rekening houdend, is het de moeite waard om het aantal cache-eigenaren voor alle caches op minimaal twee in te stellen, zodat er een kopie van de cache overblijft. De oplossing is rennen script voor Wildfly bij het starten van de pod, plaatst u deze in de map /opt/jboss/startup-scripts in de container:

Scriptinhoud

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

Stel vervolgens de waarde van de omgevingsvariabele in CACHE_OWNERS naar het vereiste.

Privénetwerk met IP-multicast-ondersteuning

Als u Weavenet als CNI gebruikt, werkt multicast onmiddellijk - en uw Keycloak-nodes zullen elkaar zien zodra ze worden gelanceerd.

Als u geen IP-multicast-ondersteuning heeft in uw Kubernetes-cluster, kunt u JGroups configureren om met andere protocollen te werken om knooppunten te vinden.

De eerste optie is gebruiken KUBE_DNSwelke gebruikt? headless service om Keycloak-knooppunten te vinden, geeft u eenvoudigweg JGroups de naam door van de service die zal worden gebruikt om de knooppunten te vinden.

Een andere optie is om de methode te gebruiken KUBE_PING, dat werkt met de API om naar knooppunten te zoeken (u moet configureren serviceAccount met rechten list и geten configureer vervolgens de peulen om hiermee te werken serviceAccount).

De manier waarop JGroups knooppunten vindt, wordt geconfigureerd door omgevingsvariabelen in te stellen JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Voor KUBE_PING je moet pods selecteren door te vragen namespace и labels.

️ Als u multicast gebruikt en twee of meer Keycloak-clusters in één Kubernetes-cluster uitvoert (laten we zeggen één in de naamruimte production, de seconde - staging) - knooppunten van één Keycloak-cluster kunnen lid worden van een ander cluster. Zorg ervoor dat u voor elk cluster een uniek multicast-adres gebruikt door variabelen in te stellenjboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replicatie tussen datacenters

Keycloak uitvoeren in HA-modus op Kubernetes

Связь

Keycloak maakt gebruik van meerdere afzonderlijke Infinispan-cacheclusters voor elk datacenter waar Keycloack-clusters bestaande uit Keycloak-nodes zich bevinden. Maar er is geen verschil tussen Keycloak-nodes in verschillende datacenters.

Keycloak-knooppunten gebruiken een extern Java Data Grid (Infinispan-servers) voor communicatie tussen datacenters. De communicatie verloopt volgens het protocol Infinispan HotRod.

Infinispan-caches moeten worden geconfigureerd met het kenmerk remoteStore, zodat de gegevens op afstand kunnen worden opgeslagen (in een ander datacenter, ca. vertaler) caches. Er zijn aparte infinispan-clusters onder de JDG-servers, zodat de gegevens op JDG1 op locatie worden opgeslagen site1 zal ter plaatse worden gerepliceerd naar JDG2 site2.

En ten slotte informeert de ontvangende JDG-server de Keycloak-servers over zijn cluster via clientverbindingen, wat een kenmerk is van het HotRod-protocol. Keycloak-knooppunten ingeschakeld site2 update hun Infinispan-caches en de specifieke gebruikerssessie wordt ook beschikbaar op de Keycloak-knooppunten site2.

Voor sommige caches is het ook mogelijk om geen back-ups te maken en te voorkomen dat gegevens volledig via de Infinispan-server worden geschreven. Om dit te doen, moet u de instelling verwijderen remote-store specifieke Infinispan-cache (in het bestand standalone-ha.xml), waarna enkele specifieke replicated-cache zal ook niet langer nodig zijn aan de Infinispan-serverkant.

Cache's instellen

Er zijn twee soorten caches in Keycloak:

  • Lokaal. Het bevindt zich naast de database en dient om de belasting van de database te verminderen en de responslatentie te verminderen. Dit type cache slaat realm, clients, rollen en gebruikersmetagegevens op. Dit type cache wordt niet gerepliceerd, zelfs als de cache deel uitmaakt van een Keycloak-cluster. Als een vermelding in de cache verandert, wordt er een bericht over de wijziging verzonden naar de overige servers in het cluster, waarna de vermelding wordt uitgesloten van de cache. zie beschrijving work Zie hieronder voor een meer gedetailleerde beschrijving van de procedure.

  • Gerepliceerd. Verwerkt gebruikerssessies, offline tokens en bewaakt ook inlogfouten om wachtwoordphishing-pogingen en andere aanvallen te detecteren. De gegevens die in deze caches zijn opgeslagen, zijn tijdelijk en worden alleen in het RAM opgeslagen, maar kunnen door het hele cluster worden gerepliceerd.

Infinispan-caches

Sessies - een concept in Keycloak, aparte caches genoemd authenticationSessions, worden gebruikt om gegevens van specifieke gebruikers op te slaan. Verzoeken uit deze caches zijn meestal nodig voor de browser en Keycloak-servers, niet voor applicaties. Dit is waar de afhankelijkheid van sticky-sessies een rol speelt, en dergelijke caches hoeven zelf niet te worden gerepliceerd, zelfs niet in het geval van de Active-Active-modus.

Actiefiches. Een ander concept dat meestal wordt gebruikt voor verschillende scenario's waarin de gebruiker bijvoorbeeld asynchroon iets per mail moet doen. Bijvoorbeeld tijdens de procedure forget password cache actionTokens gebruikt om metadata van gekoppelde tokens bij te houden - een token is bijvoorbeeld al gebruikt en kan niet opnieuw worden geactiveerd. Dit type cache moet doorgaans tussen datacenters worden gerepliceerd.

Caching en veroudering van opgeslagen gegevens werkt om de belasting van de database te verlichten. Dit soort caching verbetert de prestaties, maar voegt een duidelijk probleem toe. Als één Keycloak-server gegevens bijwerkt, moeten de andere servers hiervan op de hoogte worden gesteld, zodat zij de gegevens in hun caches kunnen bijwerken. Keycloak maakt gebruik van lokale caches realms, users и authorization voor het cachen van gegevens uit de database.

Er is ook een aparte cache work, dat wordt gerepliceerd in alle datacenters. Het slaat zelf geen gegevens uit de database op, maar dient om berichten over dataveroudering naar clusterknooppunten tussen datacenters te sturen. Met andere woorden: zodra de gegevens zijn bijgewerkt, stuurt het Keycloak-knooppunt een bericht naar andere knooppunten in zijn datacenter, evenals naar knooppunten in andere datacenters. Na ontvangst van een dergelijk bericht wist elk knooppunt de overeenkomstige gegevens in zijn lokale caches.

Gebruikerssessies. Caches met namen sessions, clientSessions, offlineSessions и offlineClientSessions, worden meestal gerepliceerd tussen datacenters en dienen om gegevens op te slaan over gebruikerssessies die actief zijn terwijl de gebruiker actief is in de browser. Deze caches werken samen met de applicatie die HTTP-verzoeken van eindgebruikers verwerkt, dus ze zijn gekoppeld aan sticky-sessies en moeten worden gerepliceerd tussen datacenters.

Bescherming tegen brute kracht. Cache loginFailures Wordt gebruikt om gegevens over inlogfouten bij te houden, zoals hoe vaak een gebruiker een onjuist wachtwoord heeft ingevoerd. Replicatie van deze cache is de verantwoordelijkheid van de beheerder. Maar voor een nauwkeurige berekening is het de moeite waard om replicatie tussen datacenters te activeren. Maar aan de andere kant, als u deze gegevens niet repliceert, verbetert u de prestaties en als dit probleem zich voordoet, wordt de replicatie mogelijk niet geactiveerd.

Wanneer u een Infinispan-cluster uitrolt, moet u cachedefinities toevoegen aan het instellingenbestand:

<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" />

U moet het Infinispan-cluster configureren en starten voordat u het Keycloak-cluster start

Dan moet je configureren remoteStore voor Keycloak-caches. Om dit te doen is een script voldoende, dat op dezelfde manier wordt gedaan als het vorige, dat werd gebruikt om de variabele in te stellen CACHE_OWNERS, moet u het in een bestand opslaan en in een map plaatsen /opt/jboss/startup-scripts:

Scriptinhoud

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

Vergeet niet te installeren JAVA_OPTS voor Keycloak-knooppunten om HotRod uit te voeren: remote.cache.host, remote.cache.port en dienstnaam jboss.site.name.

Links en aanvullende documentatie

Het artikel is door medewerkers vertaald en voorbereid voor Habr Opleidingscentrum Slurm — intensieve cursussen, videocursussen en bedrijfstrainingen van praktijkspecialisten (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Bron: www.habr.com

Voeg een reactie