Zagon Keycloaka v načinu HA v Kubernetesu

Zagon Keycloaka v načinu HA v Kubernetesu

TL; DR: opisan bo Keycloak, odprtokodni sistem za nadzor dostopa, analiza notranje strukture, podrobnosti konfiguracije.

Uvod in ključne ideje

V tem članku si bomo ogledali osnovne zamisli, ki jih je treba upoštevati pri uvajanju gruče Keycloak na vrhu Kubernetesa.

Če želite izvedeti več o Keycloaku, si oglejte povezave na koncu članka. Če se želite bolj poglobiti v prakso, se lahko učite naše skladišče z modulom, ki izvaja glavne zamisli tega članka (vodnik za zagon je tam, ta članek bo zagotovil pregled naprave in nastavitev, pribl. prevajalec).

Keycloak je celovit sistem, napisan v Javi in ​​zgrajen na vrhu aplikacijskega strežnika Divja muha. Skratka, to je ogrodje za avtorizacijo, ki daje uporabnikom aplikacij zvezo in zmogljivosti SSO (enotne prijave).

Vabimo vas k branju uradnega Spletna stran ali Wikipedia za podrobno razumevanje.

Zagon Keycloaka

Keycloak za delovanje potrebuje dva trajna vira podatkov:

  • Baza podatkov, ki se uporablja za shranjevanje uveljavljenih podatkov, kot so informacije o uporabniku
  • Datagrid predpomnilnik, ki se uporablja za predpomnilnik podatkov iz baze podatkov, kot tudi za shranjevanje nekaterih kratkotrajnih in pogosto spreminjajočih se metapodatkov, kot so uporabniške seje. Izvedeno Infinispan, ki je običajno bistveno hitrejši od baze podatkov. V vsakem primeru pa so podatki, shranjeni v Infinispanu, kratkotrajni – in jih ob ponovnem zagonu gruče ni treba nikamor shranjevati.

Keycloak deluje v štirih različnih načinih:

  • Normalno - en in edini proces, konfiguriran preko datoteke standalone.xml
  • Redni grozd (možnost visoke razpoložljivosti) - vsi procesi morajo uporabljati isto konfiguracijo, ki mora biti ročno sinhronizirana. Nastavitve so shranjene v datoteki standalone-ha.xml, poleg tega morate omogočiti skupni dostop do baze podatkov in izravnalnika obremenitve.
  • Domenski grozd — zagon gruče v običajnem načinu hitro postane rutinsko in dolgočasno opravilo, ko grozd raste, saj je treba ob vsaki spremembi konfiguracije vse spremembe opraviti na vsakem vozlišču gruče. Domenski način delovanja rešuje to težavo z nastavitvijo skupne lokacije za shranjevanje in objavo konfiguracije. Te nastavitve so shranjene v datoteki domena.xml
  • Replikacija med podatkovnimi centri — če želite zagnati Keycloak v gruči več podatkovnih centrov, največkrat na različnih geografskih lokacijah. Pri tej možnosti bo imel vsak podatkovni center svojo gručo strežnikov Keycloak.

V tem članku bomo podrobno preučili drugo možnost, tj redna gruča, malo pa se bomo dotaknili tudi teme replikacije med podatkovnimi centri, saj je smiselno, da ti dve možnosti izvajamo v Kubernetesu. Na srečo v Kubernetesu ni težav s sinhronizacijo nastavitev več podov (Keycloak nodes), tako da domenski grozd To ne bo zelo težko narediti.

Upoštevajte tudi, da beseda grozd ker se bo preostanek članka nanašal samo na skupino vozlišč Keycloak, ki delujejo skupaj, se ni treba sklicevati na gručo Kubernetes.

Običajna gruča Keycloak

Če želite zagnati Keycloak v tem načinu, potrebujete:

  • konfigurirajte zunanjo skupno bazo podatkov
  • namestite izravnalnik obremenitve
  • imeti notranje omrežje s podporo za večvrstno oddajanje IP

O vzpostavitvi zunanje baze podatkov ne bomo razpravljali, ker to ni namen tega članka. Predpostavimo, da nekje obstaja delujoča baza podatkov - in imamo povezavo z njo. Te podatke bomo preprosto dodali spremenljivkam okolja.

Da bi bolje razumeli, kako Keycloak deluje v gruči za samodejni preklop (HA), je pomembno vedeti, koliko je vse odvisno od zmožnosti gručenja Wildfly.

Wildfly uporablja več podsistemov, nekateri od njih se uporabljajo kot izenačevalnik obremenitve, nekateri za toleranco napak. Izravnalnik obremenitve zagotavlja razpoložljivost aplikacije, ko je vozlišče gruče preobremenjeno, toleranca napak pa zagotavlja razpoložljivost aplikacije, tudi če nekatera vozlišča gruče odpovejo. Nekateri od teh podsistemov:

  • mod_cluster: Deluje v povezavi z Apache kot izravnalnik obremenitve HTTP, privzeto iskanje gostiteljev je odvisno od multicasta TCP. Lahko se zamenja z zunanjim balanserjem.

  • infinispan: Porazdeljeni predpomnilnik, ki uporablja kanale JGroups kot transportno plast. Poleg tega lahko uporablja protokol HotRod za komunikacijo z zunanjo gručo Infinispan za sinhronizacijo vsebine predpomnilnika.

  • jgroups: Zagotavlja skupinsko komunikacijsko podporo za zelo razpoložljive storitve, ki temeljijo na kanalih JGroups. Poimenovane cevi omogočajo povezovanje primerkov aplikacij v gruči v skupine, tako da ima komunikacija lastnosti, kot so zanesljivost, urejenost in občutljivost na napake.

Izravnalnik obremenitve

Ko nameščate izravnalnik kot vstopni krmilnik v gručo Kubernetes, je pomembno upoštevati naslednje:

Keycloak predvideva, da je oddaljeni naslov odjemalca, ki se prek HTTP povezuje s strežnikom za preverjanje pristnosti, pravi naslov IP odjemalskega računalnika. Nastavitve izravnave in vstopa bi morale pravilno nastaviti glave HTTP X-Forwarded-For и X-Forwarded-Protoin shranite izvirni naslov HOST. Najnovejša različica ingress-nginx (>0.22.0) to privzeto onemogoči

Aktiviranje zastavice proxy-address-forwarding z nastavitvijo spremenljivke okolja PROXY_ADDRESS_FORWARDING в true daje Keycloaku vedeti, da deluje za posrednikom.

Prav tako morate omogočiti lepljive seje v vstopu. Keycloak uporablja porazdeljeni predpomnilnik Infinispan za shranjevanje podatkov, povezanih s trenutno sejo preverjanja pristnosti in uporabniško sejo. Predpomnilniki privzeto delujejo z enim samim lastnikom, z drugimi besedami, ta določena seja je shranjena na nekem vozlišču v gruči, druga vozlišča pa morajo poizvedovati na daljavo, če potrebujejo dostop do te seje.

Natančneje, v nasprotju z dokumentacijo, pripenjanje seje z imenom piškotek pri nas ni delovalo AUTH_SESSION_ID. Keycloak ima preusmeritveno zanko, zato priporočamo, da za lepljivo sejo izberete drugo ime piškotka.

Keycloak priloži tudi ime vozlišča, ki se je prvo odzvalo AUTH_SESSION_ID, in ker vsako vozlišče v zelo razpoložljivi različici uporablja isto bazo podatkov, vsako od njih moram imeti ločen in edinstven identifikator vozlišča za upravljanje transakcij. Priporočljivo je vnesti JAVA_OPTS parametri jboss.node.name и jboss.tx.node.id edinstven za vsako vozlišče - lahko na primer vnesete ime sklopa. Če vnesete ime poda, ne pozabite na omejitev 23 znakov za spremenljivke jboss, zato je bolje uporabiti StatefulSet namesto Deployment.

Še ena grablje - če se pod izbriše ali znova zažene, se njegov predpomnilnik izgubi. Ob upoštevanju tega je vredno nastaviti število lastnikov predpomnilnika za vse predpomnilnike na vsaj dva, tako da ostane kopija predpomnilnika. Rešitev je v begu scenarij za Wildfly ko zaženete pod, ga postavite v imenik /opt/jboss/startup-scripts v posodi:

Vsebina skripta

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

nato nastavite vrednost spremenljivke okolja CACHE_OWNERS na zahtevano.

Zasebno omrežje s podporo za IP multicast

Če uporabljate Weavenet kot CNI, bo multicast deloval takoj – vaša vozlišča Keycloak pa se bodo videla, takoj ko bodo zagnana.

Če v gruči Kubernetes nimate podpore za multicast ip, lahko konfigurirate JGroups za delo z drugimi protokoli za iskanje vozlišč.

Prva možnost je uporaba KUBE_DNSki uporablja headless service če želite poiskati vozlišča Keycloak, JGroups preprosto posredujete ime storitve, ki bo uporabljena za iskanje vozlišč.

Druga možnost je uporaba metode KUBE_PING, ki deluje z API-jem za iskanje vozlišč (morate konfigurirati serviceAccount s pravicami list и getin nato konfigurirajte sklope za delo s tem serviceAccount).

Način, kako JGroups najde vozlišča, je konfiguriran z nastavitvijo spremenljivk okolja JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Če želite KUBE_PING morate izbrati stroke z vprašanjem namespace и labels.

️ Če uporabljate multicast in izvajate dve ali več gruč Keycloak v eni gruči Kubernetes (recimo ena v imenskem prostoru production, drugič - staging) - vozlišča ene gruče Keycloak se lahko pridružijo drugi gruči. Prepričajte se, da za vsako gručo uporabite edinstven naslov za večvrstno oddajanje z nastavitvijo spremenljivkjboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replikacija med podatkovnimi centri

Zagon Keycloaka v načinu HA v Kubernetesu

Связь

Keycloak uporablja več ločenih gruč predpomnilnika Infinispan za vsak podatkovni center, kjer se nahajajo gruče Keycloack, sestavljene iz vozlišč Keycloak. Toda med vozlišči Keycloak v različnih podatkovnih centrih ni razlike.

Vozlišča Keycloak uporabljajo zunanji Java Data Grid (strežniki Infinispan) za komunikacijo med podatkovnimi centri. Komunikacija deluje po protokolu Infinispan HotRod.

Predpomnilniki Infinispan morajo biti konfigurirani z atributom remoteStore, tako da se lahko podatki shranijo na daljavo (v drugem podatkovnem centru, pribl. prevajalec) predpomnilniki. Med strežniki JDG so ločene gruče infinispan, tako da so podatki, shranjeni na JDG1 na mestu site1 bo podvojen v JDG2 na mestu site2.

In končno, sprejemni strežnik JDG obvesti strežnike Keycloak o svoji gruči prek odjemalskih povezav, kar je značilnost protokola HotRod. Vklopljena vozlišča Keycloak site2 posodobijo svoje predpomnilnike Infinispan in določena uporabniška seja postane na voljo tudi na vozliščih Keycloak na site2.

Pri nekaterih predpomnilnikih je možno tudi, da ne delate varnostnih kopij in se v celoti izognete zapisovanju podatkov prek strežnika Infinispan. Če želite to narediti, morate odstraniti nastavitev remote-store poseben predpomnilnik Infinispan (v datoteki standalone-ha.xml), nato pa nekaj specifičnih replicated-cache prav tako ne bo več potreben na strani strežnika Infinispan.

Nastavitev predpomnilnikov

V Keycloaku obstajata dve vrsti predpomnilnikov:

  • Lokalno. Nahaja se ob bazi podatkov in služi za zmanjšanje obremenitve baze podatkov, kakor tudi za zmanjšanje zakasnitve odziva. Ta vrsta predpomnilnika shranjuje področje, odjemalce, vloge in uporabniške metapodatke. Ta vrsta predpomnilnika ni podvojena, tudi če je predpomnilnik del gruče Keycloak. Če se vnos v predpomnilniku spremeni, se sporočilo o spremembi pošlje preostalim strežnikom v gruči, nato pa je vnos izključen iz predpomnilnika. Glej opis work Za podrobnejši opis postopka glejte spodaj.

  • Replicirano. Obdeluje uporabniške seje, žetone brez povezave in tudi spremlja napake pri prijavi, da zazna poskuse lažnega predstavljanja gesel in druge napade. Podatki, shranjeni v teh predpomnilnikih, so začasni, shranjeni samo v RAM-u, vendar jih je mogoče podvajati v gručo.

Infinispan predpomnilniki

Seje - koncept v Keycloaku, imenovani ločeni predpomnilniki authenticationSessions, se uporabljajo za shranjevanje podatkov določenih uporabnikov. Zahteve iz teh predpomnilnikov običajno potrebujeta brskalnik in strežniki Keycloak, ne pa aplikacije. Tu pride do izraza odvisnost od lepljivih sej in takšnih predpomnilnikov samih ni treba posnemati, niti v primeru načina Active-Active.

Akcijski žetoni. Drug koncept, ki se običajno uporablja za različne scenarije, ko mora na primer uporabnik narediti nekaj asinhrono po pošti. Na primer med postopkom forget password predpomnilnik actionTokens uporablja za sledenje metapodatkov povezanih žetonov - žeton je bil na primer že uporabljen in ga ni mogoče znova aktivirati. To vrsto predpomnilnika je običajno treba podvajati med podatkovnimi centri.

Predpomnjenje in staranje shranjenih podatkov deluje tako, da razbremeni bazo podatkov. Ta vrsta predpomnjenja izboljša zmogljivost, vendar doda očitno težavo. Če en strežnik Keycloak posodobi podatke, morajo biti drugi strežniki obveščeni, da lahko posodobijo podatke v svojih predpomnilnikih. Keycloak uporablja lokalne predpomnilnike realms, users и authorization za predpomnjenje podatkov iz baze podatkov.

Obstaja tudi ločen predpomnilnik work, ki se replicira v vseh podatkovnih centrih. Sama ne shranjuje nobenih podatkov iz baze podatkov, ampak služi za pošiljanje sporočil o staranju podatkov v vozlišča gruče med podatkovnimi centri. Z drugimi besedami, takoj ko so podatki posodobljeni, vozlišče Keycloak pošlje sporočilo drugim vozliščem v svojem podatkovnem centru, pa tudi vozliščem v drugih podatkovnih centrih. Po prejemu takega sporočila vsako vozlišče izbriše ustrezne podatke v svojih lokalnih predpomnilnikih.

Uporabniške seje. Skladi z imeni sessions, clientSessions, offlineSessions и offlineClientSessions, se običajno replicirajo med podatkovnimi centri in služijo za shranjevanje podatkov o uporabniških sejah, ki so aktivne, medtem ko je uporabnik aktiven v brskalniku. Ti predpomnilniki delujejo z aplikacijo, ki obdeluje zahteve HTTP končnih uporabnikov, zato so povezani z lepljivimi sejami in jih je treba podvajati med podatkovnimi centri.

Zaščita pred grobo silo. predpomnilnik loginFailures Uporablja se za sledenje podatkov o napakah pri prijavi, na primer, kolikokrat je uporabnik vnesel napačno geslo. Podvajanje tega predpomnilnika je odgovornost skrbnika. Toda za natančen izračun je vredno aktivirati replikacijo med podatkovnimi centri. Toda po drugi strani, če teh podatkov ne podvojite, boste izboljšali zmogljivost in če se pojavi ta težava, podvajanje morda ne bo aktivirano.

Ko uvajate gručo Infinispan, morate v datoteko z nastavitvami dodati definicije predpomnilnika:

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

Preden zaženete gručo Keycloak, morate konfigurirati in zagnati gručo Infinispan

Nato morate konfigurirati remoteStore za predpomnilnike Keycloak. Za to je dovolj skripta, ki se naredi podobno kot prejšnja, s katero nastavimo spremenljivko CACHE_OWNERS, ga morate shraniti v datoteko in postaviti v imenik /opt/jboss/startup-scripts:

Vsebina skripta

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

Ne pozabite namestiti JAVA_OPTS za vozlišča Keycloak za zagon HotRod: remote.cache.host, remote.cache.port in ime storitve jboss.site.name.

Povezave in dodatna dokumentacija

Članek so za Habr prevedli in pripravili zaposleni Slurm trening center — intenzivni tečaji, video tečaji in korporativno usposabljanje strokovnjakov v praksi (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Vir: www.habr.com

Dodaj komentar