Begin Keycloak in HA-modus op Kubernetes

Begin Keycloak in HA-modus op Kubernetes

TL; DR: daar sal 'n beskrywing wees van Keycloak, 'n oopbron toegangsbeheerstelsel, 'n ontleding van die interne toestel, konfigurasiebesonderhede.

Inleiding en hoofgedagtes

In hierdie artikel sal ons die hoofidees sien om in gedagte te hou wanneer 'n Keycloak-kluster bo-op Kubernetes ontplooi word.

As jy meer wil weet oor Keycloak, verwys asseblief na die skakels aan die einde van die artikel. Om jouself dieper in die praktyk te verdiep, kan jy studeer ons bewaarplek met 'n module wat die hoofgedagtes van hierdie artikel implementeer (die bekendstellingsgids is daar, in hierdie artikel sal daar 'n oorsig van die toestel en instellings wees, ongeveer. vertaler).

Keycloak is 'n komplekse stelsel wat in Java geskryf is en bo-op 'n toepassingsbediener gebou is. Veldvlieg. Kortom, dit is 'n magtigingsraamwerk wat toepassingsgebruikers federasie en SSO (enkelaanteken) vermoë gee.

Ons nooi jou uit om die amptelike te lees webwerf of Wikipedia vir gedetailleerde begrip.

Begin Keycloak

Keycloak benodig twee aanhoudende databronne om te loop:

  • 'n Databasis wat gebruik word om standhoudende data, soos inligting oor gebruikers, te stoor
  • Datagrid-kas, wat gebruik word om data vanaf die databasis te kas, sowel as om 'n paar kortstondige en gereeld veranderde metadata te stoor, soos gebruikersessies. Vrygestel Infinispan, wat gewoonlik aansienlik vinniger is as die databasis. Maar in elk geval, die data wat in Infinispan gestoor is, is kortstondig – en dit hoef nie iewers gestoor te word wanneer die groep herbegin word nie.

Keycloak werk in vier verskillende modusse:

  • Normale - een en slegs een proses, gekonfigureer deur 'n lêer selfstandig.xml
  • gereelde cluster (hoogs beskikbare opsie) - Alle prosesse moet dieselfde konfigurasie gebruik, wat met die hand gesinchroniseer moet word. Instellings word in 'n lêer gestoor selfstandige-ha.xml, Daarbenewens moet jy 'n gedeelde toegang tot die databasis en 'n lasbalanseerder maak.
  • Domeingroepering - om die groep in normale modus te begin, word vinnig 'n roetine en vervelige taak soos die groep groei, aangesien elke keer as jy die konfigurasie verander, moet jy al die veranderinge op elke nodus van die groep maak. Die domein werkingsmodus los hierdie probleem op deur 'n paar gedeelde berging op te stel en die konfigurasie te publiseer. Hierdie instellings word in 'n lêer gestoor domein.xml
  • Replikasie tussen datasentrums - as u Keycloak in 'n groep van verskeie datasentrums wil laat loop, meestal op verskillende geografiese liggings. In hierdie opsie sal elke datasentrum sy eie groep Keycloak-bedieners hê.

In hierdie artikel gaan ons die tweede opsie van nader bekyk, d.w.s. normale cluster, sowel as 'n bietjie aanraking oor die onderwerp van replikasie tussen datasentrums, aangesien dit sin maak om hierdie twee opsies in Kubernetes uit te voer. Gelukkig het Kubernetes nie 'n probleem om die instellings van veelvuldige peule (Keycloak-nodes) te sinkroniseer nie, so domeingroepering dit sal nie te moeilik wees om te doen nie.

Neem ook asseblief kennis dat die woord cluster tot aan die einde van die artikel sal slegs van toepassing wees op 'n groep Keycloak-nodusse wat saamwerk, dit is nie nodig om na 'n Kubernetes-kluster te verwys nie.

Gereelde Keycloak Cluster

Om Keycloak in hierdie modus te laat loop, benodig jy:

  • stel 'n eksterne gedeelde databasis op
  • installeer load balancer
  • het 'n interne netwerk met ip multicast-ondersteuning

Ons sal nie die konfigurasie van die eksterne databasis ontleed nie, aangesien dit nie die doel van hierdie artikel is nie. Kom ons neem aan dat daar iewers 'n werkende databasis is - en ons het 'n verbindingspunt daarmee. Ons sal eenvoudig hierdie data by die omgewingsveranderlikes voeg.

Om beter te verstaan ​​hoe Keycloak in 'n failover (HA) groep werk, is dit belangrik om te weet hoeveel dit alles afhang van Wildfly se groeperingsvermoëns.

Wildfly gebruik verskeie substelsels, sommige van hulle word gebruik as 'n lasbalanseerder, sommige word gebruik vir failover. Die lasbalanseerder verseker die beskikbaarheid van die toepassing wanneer die trosknoop oorlaai is, en failover verseker die beskikbaarheid van die toepassing selfs al misluk sommige van die groepnodusse. Sommige van hierdie substelsels is:

  • mod_cluster: werk saam met Apache as 'n HTTP-lasbalanseerder, hang af van TCP multicast vir verstekgasheerontdekking. Kan deur 'n eksterne balanseerder vervang word.

  • infinispan: verspreide kas met behulp van JGroups-kanale as vervoerlaag. Opsioneel kan dit die HotRod-protokol gebruik om met 'n eksterne Infinispan-kluster te kommunikeer om die inhoud van die kas te sinchroniseer.

  • jgroups: Bied ondersteuning vir groepvereniging vir hoogs beskikbare dienste gebaseer op JGroups-kanale. Benoemde pype laat toepassinggevalle in 'n groepering toe om in groepe gekoppel te word sodat die verbinding eienskappe het soos betroubaarheid, ordelikheid en mislukkingsensitiwiteit.

lasbalanseerder

Wanneer 'n balanseerder as 'n ingangsbeheerder in 'n Kubernetes-kluster geïnstalleer word, is dit belangrik om die volgende dinge in gedagte te hou:

Keycloak se werk impliseer dat die afgeleë adres van die kliënt wat via HTTP aan die verifikasiebediener koppel, die regte IP-adres van die kliëntrekenaar is. Balanser- en ingangsinstellings moet HTTP-opskrifte korrek stel X-Forwarded-For и X-Forwarded-Proto, en hou die oorspronklike titel HOST. nuutste weergawe ingress-nginx (> 0.22.0) deaktiveer dit by verstek

Vlag aktivering proxy-address-forwarding deur 'n omgewingsveranderlike in te stel PROXY_ADDRESS_FORWARDING в true gee Keycloak die begrip dat dit agter 'n instaanbediener loop.

Jy moet ook aktiveer taai sessies intree. Keycloak gebruik Infinispan se verspreide kas om data te stoor wat verband hou met die huidige verifikasiesessie en gebruikersessie. Kasgeheue is by verstek enkeleienaar, met ander woorde daardie spesifieke sessie word op een of ander groepknoop gestoor en ander nodusse moet dit op afstand versoek as hulle toegang tot daardie sessie benodig.

Spesifiek, in teenstelling met die dokumentasie, het die aanheg van 'n sessie met die koekienaam nie vir ons gewerk nie AUTH_SESSION_ID. Keycloak het die herleiding herlei, so ons beveel aan om 'n ander koekienaam vir die taai sessie te kies.

Keycloak heg ook die naam van die gasheer aan wat eerste geantwoord het AUTH_SESSION_ID, en aangesien elke nodus in die hoogs beskikbare weergawe dieselfde databasis gebruik, elkeen van hulle moet he 'n aparte en unieke nodus-ID vir die bestuur van transaksies. Dit word aanbeveel om in te sit JAVA_OPTS opsies jboss.node.name и jboss.tx.node.id uniek vir elke nodus - jy kan byvoorbeeld die naam van die peul stel. As jy die naam van die pod plaas - moenie vergeet van die 23 karakterlimiet vir jboss-veranderlikes nie, so dit is beter om StatefulSet te gebruik, nie Deployment nie.

Nog een hark - as 'n peul uitgevee of herbegin word, is sy kas verlore. Met dit in gedagte is dit die moeite werd om die aantal kas-eienaars vir alle kas op ten minste twee te stel, so daar sal 'n kopie van die kas wees. Die oplossing is om te hardloop draaiboek vir Wildfly wanneer jy die peul begin, plaas dit in die gids /opt/jboss/startup-scripts in houer:

Skrip inhoud

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 dan die waarde van die omgewingsveranderlike in CACHE_OWNERS na die vereiste.

Privaat netwerk met ip multicast ondersteuning

As jy Weavenet as jou CNI gebruik, sal multicast dadelik werk – en jou Keycloak-nodusse sal mekaar sien sodra hulle aan die gang is.

As jy nie ip multicast-ondersteuning in jou Kubernetes-kluster het nie, kan jy JGroups instel om met ander protokolle te werk om nodusse te vind.

Die eerste opsie is om te gebruik KUBE_DNSwat gebruik headless service om Keycloak-nodusse te vind, gee jy eenvoudig aan JGroups die naam van die diens wat gebruik sal word om die nodusse te vind.

Nog 'n opsie is om die metode te gebruik KUBE_PING, wat met die API werk om nodusse te vind (jy moet konfigureer serviceAccount met regte list и get, en stel dan die peule in om hiermee te werk serviceAccount).

Hoe nodusse vir JGroups gesoek word, word gekonfigureer deur omgewingsveranderlikes in te stel JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Vir KUBE_PING jy moet peule kies deur te vra namespace и labels.

️ As jy multicast gebruik en twee of meer Keycloak-klusters in dieselfde Kubernetes-kluster laat loop (kom ons sê een in naamruimte production, die tweede - staging) - nodusse van een Keycloak-kluster kan by 'n ander groep aansluit. Maak seker dat jy 'n unieke multicast-adres vir elke groepering gebruik deur veranderlikes in te steljboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replikasie tussen datasentrums

Begin Keycloak in HA-modus op Kubernetes

Связь

Keycloak gebruik verskeie afsonderlike Infinispan Cache Clusters vir elke datasentrum wat Keycloack-klusters huisves wat uit Keycloak-nodusse bestaan. Maar terselfdertyd is daar geen verskil tussen Keycloak-nodusse in verskillende datasentrums nie.

Keycloak-nodusse gebruik 'n eksterne Java Data Grid (Infinispan-bedieners) om tussen datasentrums te kommunikeer. Kommunikasie werk volgens die protokol Infinispan HotRod.

Infinispan-kas moet met die kenmerk gekonfigureer word remoteStore, sodat die data op afstand gestoor kan word (in 'n ander datasentrum, ongeveer. vertaler) caches. Daar is aparte infinispan-klusters onder die JDG-bedieners, dus data wat op JDG1 op die werf gestoor word site1 sal op die terrein na JDG2 gerepliseer word site2.

Laastens stel die JDG-bediener wat ontvang, die Keycloak-bedieners van sy groepering in kennis via kliëntverbindings, wat 'n kenmerk van die HotRod-protokol is. Keycloak nodusse aan site2 dateer hul Infinispan-kas op en die spesifieke gebruikersessie word op die Keycloak-nodusse beskikbaar site2.

Dit is ook moontlik dat sommige kas nie gerugsteun word nie en heeltemal weier om data deur die Infinispan-bediener te skryf. Om dit te doen, moet jy die instelling verwyder remote-store spesifieke Infinispan-kas (in lêer selfstandige-ha.xml), waarna 'n paar spesifieke replicated-cache sal ook nie meer aan die kant van die Infinispan-bediener benodig word nie.

Opstel van caches

Daar is twee tipes kas in Keycloak:

  • Plaaslik. Dit is langs die basis geleë, dien om die las op die databasis te verminder, sowel as om reaksievertraging te verminder. Hierdie tipe kas stoor die ryk, kliënte, rolle en gebruikersmetadata. Hierdie tipe kas word nie gerepliseer nie, selfs al is hierdie kas deel van 'n Keycloak-kluster. As een of ander inskrywing in die kas verander, word 'n veranderingsboodskap na die res van die bedieners in die groep gestuur, waarna die inskrywing uit die kas uitgesluit word. sien beskrywing work hieronder vir 'n meer gedetailleerde beskrywing van die prosedure.

  • Herhaalbaar. Verwerk gebruikersessies, vanlyn tekens en monitor aanmeldmislukkings om wagwoord-uitvissingpogings en ander aanvalle op te spoor. Die data wat in hierdie kas gestoor word, is tydelik, net in RAM gestoor, maar kan oor die groep herhaal word.

Infinispan Caches

Sessies - 'n konsep in Keycloak, aparte caches, wat genoem word authenticationSessions, word gebruik om die data van spesifieke gebruikers te stoor. Versoeke vanaf hierdie kas word gewoonlik deur die blaaier en Keycloak-bedieners benodig, nie toepassings nie. Dit is hier waar die afhanklikheid van taai sessies hom manifesteer, en sulke caches self hoef nie gerepliseer te word nie, selfs in die geval van Active-Active-modus.

Aksietekens. 'n Ander konsep, wat gewoonlik vir verskeie scenario's gebruik word, wanneer die gebruiker byvoorbeeld iets asinchronies per pos moet doen. Byvoorbeeld, tydens die prosedure forget password kas actionTokens gebruik om die metadata van verwante tekens na te spoor - byvoorbeeld, die teken is reeds gebruik en kan nie heraktiveer word nie. Hierdie tipe kas moet tipies tussen datasentrums gerepliseer word.

Kas en verstryking van gestoorde data werk om die las van die databasis af te haal. Hierdie caching verbeter werkverrigting, maar voeg 'n ooglopende probleem by. As een Keycloak-bediener die data opdateer, moet die res van die bedieners in kennis gestel word sodat hulle hul kas kan opdateer. Keycloak gebruik plaaslike kas realms, users и authorization vir die kas van data vanaf die databasis.

Daar is ook 'n aparte kas work, wat oor alle datasentrums gerepliseer word. Dit self stoor geen data vanaf die databasis nie, maar dien om dataverouderingsboodskappe na groepnodusse tussen datasentrums te stuur. Met ander woorde, sodra die data opgedateer is, stuur die Keycloak-nodus 'n boodskap na ander nodusse in sy datasentrum, sowel as nodusse in ander datasentrums. By ontvangs van so 'n boodskap suiwer elke nodus die ooreenstemmende data in sy plaaslike kas uit.

Gebruiker sessies. Caches met name sessions, clientSessions, offlineSessions и offlineClientSessions, word gewoonlik tussen datasentrums gerepliseer en dien om data te stoor oor gebruikersessies wat aktief is terwyl die gebruiker aktief is in die blaaier. Hierdie kas werk met die toepassing wat HTTP-versoeke van eindgebruikers hanteer, dus word dit geassosieer met taai sessies en moet tussen datasentrums herhaal word.

brute krag beskerming. Kas loginFailures gebruik om aanmeldfoutdata op te spoor, soos die aantal kere wat 'n gebruiker 'n verkeerde wagwoord ingevoer het. Replikasie van hierdie kas is aan die administrateur. Maar vir 'n akkurate berekening is dit die moeite werd om replikasie tussen datasentrums te aktiveer. Maar aan die ander kant, as jy nie hierdie data herhaal nie, sal jy prestasie kan verbeter, en as hierdie vraag ontstaan, kan replikasie nie geaktiveer word nie.

Wanneer u 'n Infinispan-kluster uitrol, moet u kasdefinisies by die instellingslêer voeg:

<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 die Infinispan-kluster opstel en begin voordat u die Keycloak-kluster laat loop

Dan moet jy stel remoteStore vir Keycloak-caches. Hiervoor is 'n skrip genoeg, wat soortgelyk aan die vorige een gedoen word, wat gebruik word om die veranderlike te stel CACHE_OWNERS, moet jy dit in 'n lêer stoor en in 'n gids plaas /opt/jboss/startup-scripts:

Skrip inhoud

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

Moenie vergeet om te installeer nie JAVA_OPTS vir Keycloak-nodusse om HotRod te werk: remote.cache.host, remote.cache.port en diens naam jboss.site.name.

Skakels en bykomende dokumentasie

Die artikel is deur werknemers vertaal en vir Habr voorberei Slurm opleidingsentrum — intensiewe, videokursusse en korporatiewe opleiding van praktisyns (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Bron: will.com

Voeg 'n opmerking