Spustenie Keycloak v režime HA na Kubernetes

Spustenie Keycloak v režime HA na Kubernetes

TL; DR: bude tu popis Keycloak, open source systému kontroly prístupu, analýza vnútornej štruktúry, detaily konfigurácie.

Úvod a kľúčové myšlienky

V tomto článku uvidíme základné myšlienky, ktoré treba mať na pamäti pri nasadzovaní klastra Keycloak na vrchole Kubernetes.

Ak sa chcete dozvedieť viac o Keycloak, pozrite si odkazy na konci článku. Aby ste sa viac ponorili do praxe, môžete študovať naše úložisko s modulom, ktorý implementuje hlavné myšlienky tohto článku (je tu návod na spustenie, tento článok poskytne prehľad o zariadení a nastaveniach, približne. prekladateľ).

Keycloak je komplexný systém napísaný v jazyku Java a postavený na aplikačnom serveri Divoká mucha. Stručne povedané, je to rámec pre autorizáciu, ktorý používateľom aplikácií poskytuje možnosti federácie a SSO (jednotné prihlásenie).

Pozývame vás, aby ste si prečítali oficiálne webové stránky alebo Wikipedia pre podrobné pochopenie.

Spustenie Keycloak

Keycloak vyžaduje na spustenie dva trvalé zdroje údajov:

  • Databáza používaná na ukladanie zavedených údajov, ako sú informácie o používateľovi
  • Datagrid cache, ktorá sa používa na ukladanie údajov z databázy do vyrovnávacej pamäte, ako aj na ukladanie niektorých krátkodobých a často sa meniacich metadát, ako sú napríklad relácie používateľa. Implementovaná Infinispan, ktorá je zvyčajne výrazne rýchlejšia ako databáza. V každom prípade sú však údaje uložené v Infinispane pominuteľné – a pri reštartovaní klastra ich nie je potrebné nikam ukladať.

Keycloak funguje v štyroch rôznych režimoch:

  • Normálne - jeden a len jeden proces, nakonfigurovaný prostredníctvom súboru samostatný.xml
  • Pravidelný zhluk (možnosť vysokej dostupnosti) – všetky procesy musia používať rovnakú konfiguráciu, ktorá sa musí synchronizovať manuálne. Nastavenia sú uložené v súbore standalone-ha.xml, okrem toho musíte urobiť zdieľaný prístup k databáze a nástroj na vyrovnávanie záťaže.
  • Klaster domén — spustenie klastra v normálnom režime sa s rastom klastra rýchlo stáva rutinnou a nudnou úlohou, pretože pri každej zmene konfigurácie je potrebné vykonať všetky zmeny v každom uzle klastra. Prevádzkový režim domény rieši tento problém nastavením zdieľaného úložiska a zverejnením konfigurácie. Tieto nastavenia sú uložené v súbore doména.xml
  • Replikácia medzi dátovými centrami — ak chcete spustiť Keycloak v klastri niekoľkých dátových centier, najčastejšie v rôznych geografických lokalitách. Pri tejto možnosti bude mať každé dátové centrum svoj vlastný klaster serverov Keycloak.

V tomto článku podrobne zvážime druhú možnosť, tj pravidelný zhluka tiež sa trochu dotkneme témy replikácie medzi dátovými centrami, pretože má zmysel spúšťať tieto dve možnosti v Kubernetes. Našťastie v Kubernetes nie je problém so synchronizáciou nastavení viacerých podov (Keycloak nodes), takže doménový klaster Nebude to veľmi ťažké urobiť.

Upozorňujeme tiež, že slovo zhluk pre zvyšok článku sa bude vzťahovať výlučne na skupinu uzlov Keycloak spolupracujúcich, nie je potrebné odkazovať na klaster Kubernetes.

Bežný klaster Keycloak

Na spustenie Keycloak v tomto režime potrebujete:

  • konfigurovať externú zdieľanú databázu
  • nainštalujte vyrovnávač zaťaženia
  • majú internú sieť s podporou IP multicast

Nebudeme rozoberať nastavenie externej databázy, pretože to nie je účelom tohto článku. Predpokladajme, že niekde existuje funkčná databáza – a máme k nej bod pripojenia. Tieto údaje jednoducho pridáme do premenných prostredia.

Aby ste lepšie pochopili, ako Keycloak funguje v klastri s prepnutím pri zlyhaní (HA), je dôležité vedieť, do akej miery to všetko závisí od klastrovacích schopností Wildfly.

Wildfly používa niekoľko podsystémov, niektoré z nich sa používajú ako vyrovnávač záťaže, niektoré na odolnosť voči chybám. Nástroj na vyrovnávanie záťaže zaisťuje dostupnosť aplikácií, keď je uzol klastra preťažený, a odolnosť voči chybám zaisťuje dostupnosť aplikácie aj v prípade, že niektoré uzly klastra zlyhajú. Niektoré z týchto subsystémov:

  • mod_cluster: Funguje v spojení s Apache ako vyrovnávač záťaže HTTP, v predvolenom nastavení závisí od viacsmerového vysielania TCP. Možno nahradiť externým balancerom.

  • infinispan: Distribuovaná vyrovnávacia pamäť využívajúca kanály JGroups ako transportnú vrstvu. Okrem toho môže používať protokol HotRod na komunikáciu s externým klastrom Infinispan na synchronizáciu obsahu vyrovnávacej pamäte.

  • jgroups: Poskytuje podporu skupinovej komunikácie pre vysoko dostupné služby založené na kanáloch JGroups. Pomenované kanály umožňujú inštancie aplikácií v klastri spájať do skupín, takže komunikácia má vlastnosti ako spoľahlivosť, usporiadanosť a citlivosť na zlyhania.

Load Balancer

Pri inštalácii balancéra ako kontroléra vstupu do klastra Kubernetes je dôležité mať na pamäti nasledujúce veci:

Keycloak predpokladá, že vzdialená adresa klienta pripájajúceho sa cez HTTP k autentifikačnému serveru je skutočná IP adresa klientskeho počítača. Nastavenia balancéra a vstupu by mali správne nastaviť hlavičky HTTP X-Forwarded-For и X-Forwarded-Protoa tiež uložiť pôvodný názov HOST. Najnovšia verzia ingress-nginx (>0.22.0) predvolene to zakáže

Aktivácia vlajky proxy-address-forwarding nastavením premennej prostredia PROXY_ADDRESS_FORWARDING в true dáva Keycloak pochopenie, že funguje za proxy.

Musíte tiež povoliť lepkavé relácie vo vstupe. Keycloak používa distribuovanú vyrovnávaciu pamäť Infinispan na ukladanie údajov spojených s aktuálnou reláciou autentifikácie a reláciou používateľa. Cache štandardne fungujú s jedným vlastníkom, inými slovami, táto konkrétna relácia je uložená v niektorom uzle v klastri a ostatné uzly sa na ňu musia pýtať na diaľku, ak potrebujú prístup k tejto relácii.

Konkrétne, na rozdiel od dokumentácie, pripojenie relácie s názvom cookie nám nefungovalo AUTH_SESSION_ID. Keycloak má slučku presmerovania, preto vám odporúčame zvoliť iný názov súboru cookie pre reláciu s pevným umiestnením.

Keycloak tiež pripája názov uzla, ktorý odpovedal ako prvý AUTH_SESSION_IDa keďže každý uzol vo vysoko dostupnej verzii používa rovnakú databázu, každý z nich musieť mať samostatný a jedinečný identifikátor uzla na riadenie transakcií. Odporúča sa vložiť JAVA_OPTS parametre jboss.node.name и jboss.tx.node.id jedinečný pre každý uzol - môžete napríklad zadať názov pod. Ak zadáte názov pod, nezabudnite na limit 23 znakov pre premenné jboss, takže je lepšie použiť StatefulSet namiesto Deployment.

Ďalší rake - ak sa pod vymaže alebo reštartuje, jeho vyrovnávacia pamäť sa stratí. S prihliadnutím na to stojí za to nastaviť počet vlastníkov kešiek pre všetky kešky aspoň na dvoch, aby zostala kópia kešky. Riešením je beh scenár pre Wildfly pri spustení modulu ho umiestnite do adresára /opt/jboss/startup-scripts v kontajneri:

Obsah skriptu

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

potom nastavte hodnotu premennej prostredia CACHE_OWNERS k požadovanému.

Privátna sieť s podporou IP multicast

Ak používate Weavenet ako CNI, multicast bude fungovať okamžite – a vaše uzly Keycloak sa navzájom uvidia hneď po ich spustení.

Ak vo svojom klastri Kubernetes nemáte podporu viacsmerového vysielania IP, môžete nakonfigurovať JGroups na prácu s inými protokolmi pri hľadaní uzlov.

Prvou možnosťou je použiť KUBE_DNSktorý používa headless service ak chcete nájsť uzly Keycloak, jednoducho odovzdajte JGroups názov služby, ktorá sa použije na nájdenie uzlov.

Ďalšou možnosťou je použiť metódu KUBE_PING, ktorý spolupracuje s API na vyhľadávanie uzlov (treba nakonfigurovať serviceAccount s právami list и geta potom nakonfigurujte moduly tak, aby s tým pracovali serviceAccount).

Spôsob, akým JGroups nachádzajú uzly, je nakonfigurovaný nastavením premenných prostredia JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Pre KUBE_PING musíte vybrať pods otázkou namespace и labels.

️ Ak používate multicast a spúšťate dva alebo viac klastrov Keycloak v jednom klastri Kubernetes (povedzme jeden v mennom priestore production, druhy - staging) - uzly jedného klastra Keycloak sa môžu pripojiť k inému klastru. Uistite sa, že používate jedinečnú adresu multicast pre každý klaster nastavením premennýchjboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replikácia medzi dátovými centrami

Spustenie Keycloak v režime HA na Kubernetes

Odkaz

Keycloak používa viacero samostatných klastrov vyrovnávacej pamäte Infinispan pre každé dátové centrum, kde sa nachádzajú klastre Keycloack zložené z uzlov Keycloak. Medzi uzlami Keycloak v rôznych dátových centrách však nie je žiadny rozdiel.

Uzly Keycloak používajú externý Java Data Grid (servery Infinispan) na komunikáciu medzi dátovými centrami. Komunikácia funguje podľa protokolu Infinispan HotRod.

Vyrovnávacie pamäte Infinispan musia byť nakonfigurované pomocou atribútu remoteStore, aby bolo možné údaje ukladať na diaľku (v inom dátovom centre, približne. prekladateľ) vyrovnávacie pamäte. Medzi servermi JDG existujú samostatné klastre infinispan, takže údaje uložené na serveri JDG1 na mieste site1 budú replikované do JDG2 na mieste site2.

A nakoniec, prijímajúci server JDG informuje servery Keycloak o svojom klastri prostredníctvom klientskych pripojení, čo je vlastnosť protokolu HotRod. Uzly maskovania sú zapnuté site2 aktualizujte svoje vyrovnávacie pamäte Infinispan a konkrétna používateľská relácia bude dostupná aj na uzloch Keycloak na site2.

Pre niektoré vyrovnávacie pamäte je tiež možné nevytvárať zálohy a úplne sa vyhnúť zapisovaniu údajov cez server Infinispan. Ak to chcete urobiť, musíte odstrániť nastavenie remote-store špecifická vyrovnávacia pamäť Infinispan (v súbore standalone-ha.xml), po ktorom niektoré konkrétne replicated-cache už nebude potrebný ani na strane servera Infinispan.

Nastavenie vyrovnávacej pamäte

V Keycloak sú dva typy skrýš:

  • Miestne. Nachádza sa vedľa databázy a slúži na zníženie zaťaženia databázy, ako aj na zníženie latencie odozvy. Tento typ vyrovnávacej pamäte ukladá sféru, klientov, roly a používateľské metadáta. Tento typ vyrovnávacej pamäte sa nereplikuje, aj keď je vyrovnávacia pamäť súčasťou klastra Keycloak. Ak sa zmení záznam vo vyrovnávacej pamäti, správa o zmene sa odošle na zostávajúce servery v klastri, po čom sa záznam vylúči z vyrovnávacej pamäte. Pozri popis work Podrobnejší popis postupu nájdete nižšie.

  • Replikované. Spracováva používateľské relácie, offline tokeny a tiež monitoruje chyby prihlásenia, aby zistil pokusy o neoprávnené získavanie hesiel a iné útoky. Dáta uložené v týchto cache sú dočasné, uložené iba v RAM, ale môžu byť replikované v rámci klastra.

Vyrovnávacie pamäte Infinispan

Relácie - koncept v Keycloak, samostatné kešky tzv authenticationSessions, slúžia na ukladanie údajov konkrétnych používateľov. Požiadavky z týchto vyrovnávacích pamätí zvyčajne potrebuje prehliadač a servery Keycloak, nie aplikácie. Tu prichádza na rad závislosť na sticky sessions a takéto cache samotné nie je potrebné replikovať ani v prípade Active-Active módu.

Akčné žetóny. Ďalší koncept, ktorý sa zvyčajne používa pre rôzne scenáre, keď napríklad používateľ musí urobiť niečo asynchrónne poštou. Napríklad počas procedúry forget password cache actionTokens slúži na sledovanie metadát súvisiacich tokenov – napríklad token už bol použitý a nedá sa znova aktivovať. Tento typ vyrovnávacej pamäte je zvyčajne potrebné replikovať medzi dátovými centrami.

Ukladanie do vyrovnávacej pamäte a starnutie uložených údajov pracuje na odľahčení databázy. Tento druh ukladania do vyrovnávacej pamäte zlepšuje výkon, ale prináša zjavný problém. Ak jeden server Keycloak aktualizuje údaje, ostatné servery musia byť upozornené, aby mohli aktualizovať údaje vo svojich vyrovnávacích pamätiach. Keycloak používa lokálne vyrovnávacie pamäte realms, users и authorization na ukladanie údajov z databázy do vyrovnávacej pamäte.

Nachádza sa tu aj samostatná keška work, ktorý je replikovaný vo všetkých dátových centrách. Tá sama o sebe neukladá žiadne dáta z databázy, ale slúži na odosielanie správ o starnutí dát do uzlov klastra medzi dátovými centrami. Inými slovami, akonáhle sú údaje aktualizované, uzol Keycloak odošle správu ostatným uzlom vo svojom dátovom centre, ako aj uzlom v iných dátových centrách. Po prijatí takejto správy každý uzol vymaže príslušné údaje vo svojich lokálnych vyrovnávacích pamätiach.

Používateľské relácie. Kešky s menami sessions, clientSessions, offlineSessions и offlineClientSessions, sa zvyčajne replikujú medzi dátovými centrami a slúžia na ukladanie údajov o používateľských reláciách, ktoré sú aktívne, kým je používateľ aktívny v prehliadači. Tieto vyrovnávacie pamäte pracujú s aplikáciou spracúvajúcou požiadavky HTTP od koncových používateľov, takže sú spojené s pevnými reláciami a musia sa replikovať medzi dátovými centrami.

Ochrana hrubou silou. Cache loginFailures Používa sa na sledovanie údajov o chybách pri prihlásení, napríklad koľkokrát používateľ zadal nesprávne heslo. Za replikáciu tejto vyrovnávacej pamäte je zodpovedný administrátor. Ale pre presný výpočet sa oplatí aktivovať replikáciu medzi dátovými centrami. Ale na druhej strane, ak tieto údaje nereplikujete, zlepšíte výkon a ak sa vyskytne tento problém, replikácia nemusí byť aktivovaná.

Pri zavádzaní klastra Infinispan musíte do súboru s nastaveniami pridať definície vyrovnávacej pamäte:

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

Pred spustením klastra Keycloak musíte nakonfigurovať a spustiť klaster Infinispan

Potom musíte nakonfigurovať remoteStore pre vyrovnávacie pamäte Keycloak. K tomu stačí skript, ktorý sa robí podobne ako predchádzajúci, ktorý slúži na nastavenie premennej CACHE_OWNERS, musíte ho uložiť do súboru a vložiť do adresára /opt/jboss/startup-scripts:

Obsah skriptu

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

Nezabudnite nainštalovať JAVA_OPTS pre uzly Keycloak na spustenie HotRod: remote.cache.host, remote.cache.port a názov služby jboss.site.name.

Odkazy a dodatočná dokumentácia

Článok preložili a pre Habra pripravili zamestnanci Tréningové centrum slurmu — intenzívne kurzy, video kurzy a firemné školenia od praktických špecialistov (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Zdroj: hab.com

Pridať komentár