Eseguite Keycloak in modalità HA in Kubernetes

Eseguite Keycloak in modalità HA in Kubernetes

TL; DR: ci sarà una discrizzione di Keycloak, un sistema di cuntrollu d'accessu open source, una analisi di u dispusitivu internu, dettagli di cunfigurazione.

Introduzione è idee principali

In questu articulu, vedemu l'idee principali per mantene in mente quandu implementate un cluster Keycloak in cima à Kubernetes.

Se vulete sapè più nantu à Keycloak, fate un riferimentu à i ligami à a fine di l'articulu. Per immerse più profondamente in a pratica, pudete studià u nostru repository cù un modulu chì implementa l'idee principali di questu articulu (a guida di lanciamentu hè quì, in questu articulu ci sarà una panoramica di u dispusitivu è i paràmetri, ca. traduttore).

Keycloak hè un sistema cumplessu scrittu in Java è custruitu nantu à un servitore di applicazioni. mosca salvatica. In breve, hè un framework d'autorizazione chì dà a capacità di federazione di l'utilizatori di l'applicazione è SSO (single sign-on).

Vi invitemu à leghje u ufficiale situ web o Wikipedia per una comprensione dettagliata.

Principià Keycloak

Keycloak hà bisognu di duie fonti di dati persistenti per eseguisce:

  • Una basa di dati utilizata per almacenà dati persistenti, cum'è l'infurmazioni nantu à l'utilizatori
  • Datagrid cache, chì hè utilizatu per cache di dati da a basa di dati, è ancu per almacenà alcune metadati di corta durata è spessu cambiati, cum'è e sessioni d'utilizatori. Liberatu Infinispan, chì generalmente hè significativamente più veloce di a basa di dati. Ma in ogni casu, i dati salvati in Infinispan sò effimeri - è ùn deve micca esse salvatu in un locu quandu u cluster hè riavviatu.

Keycloak funziona in quattru modi diffirenti:

  • strasurdinariu - un solu prucessu, cunfiguratu attraversu un schedariu standalone.xml
  • cluster regular (opzione assai dispunibule) - Tutti i prucessi devenu aduprà a listessa cunfigurazione, chì deve esse sincronizzata manualmente. I paràmetri sò guardati in un schedariu standalone-ha.xml, in più, avete bisognu di fà un accessu cumunu à a basa di dati è un balancer di carica.
  • cluster di duminiu - L'iniziu di u cluster in modu normale diventa rapidamente un compitu di rutina è noioso mentre u cluster cresce, postu chì ogni volta chì cambiate a cunfigurazione, avete bisognu di fà tutti i cambiamenti in ogni nodu di u cluster. U modu di funziunamentu di u duminiu risolve stu prublema creendu qualchì almacenamentu cumunu è publicendu a cunfigurazione. Questi paràmetri sò guardati in un schedariu duminiu.xml
  • Replicazione trà i centri di dati - in casu chì vulete eseguisce Keycloak in un cluster di parechji centri di dati, più spessu in diverse lochi geografichi. In questa opzione, ogni centru di dati avarà u so propiu cluster di servitori Keycloak.

In questu articulu, avemu da piglià un ochju più vicinu à a seconda opzione, i.e. cluster normale, è ancu un pocu toccu nantu à u tema di a replicazione trà i centri di dati, postu chì hè sensu per eseguisce queste duie opzioni in Kubernetes. Per furtuna, Kubernetes ùn hà micca un prublema cù a sincronizazione di i paràmetri di parechje pods (nodi Keycloak), cusì cluster di duminiu ùn sarà micca troppu difficiule à fà.

Per piacè nutate ancu chì a parolla cluster finu à a fine di l'articulu s'applicà solu à un gruppu di nodi Keycloak chì travaglianu inseme, ùn ci hè bisognu di riferite à un cluster Kubernetes.

Cluster di Keycloak Regular

Per eseguisce Keycloak in questu modu, avete bisognu:

  • crià una basa di dati cumuna esterna
  • installà l'equilibriu di carica
  • avè una rete interna cù supportu multicast ip

Ùn avemu micca analizà a cunfigurazione di a basa di dati esterna, postu chì ùn hè micca u scopu di stu articulu. Assumimu chì in qualchì locu ci hè una basa di dati di travagliu - è avemu un puntu di cunnessione à questu. Simpliceremu aghjunghje sti dati à e variabili di l'ambiente.

Per capisce megliu cumu funziona Keycloak in un cluster failover (HA), hè impurtante sapè quantu tuttu dipende di l'abilità di clustering di Wildfly.

Wildfly usa parechji sottosistemi, alcuni sò usati cum'è balancer di carica, alcuni sò usati per fallu. U bilanciatore di carica assicura a dispunibilità di l'applicazione quandu u node di cluster hè sopracargatu, è u failover assicura a dispunibilità di l'applicazione ancu se alcuni di i nodi di cluster fallenu. Certi di sti sottosistemi sò:

  • mod_cluster: travaglia in cungiunzione cù Apache cum'è un equilibratore di carica HTTP, dipende da u multicast TCP per a scuperta di l'ospiti predeterminatu. Pò esse rimpiazzatu da un equilibratore esternu.

  • infinispan: cache distribuitu utilizendu canali JGroups cum'è strata di trasportu. Opcionalmente, pò utilizà u protocolu HotRod per cumunicà cù un cluster Infinispan esternu per sincronizà u cuntenutu di u cache.

  • jgroups: Fornisce supportu per l'associazione di u gruppu per i servizii altamente dispunibili basati nantu à i canali JGroups. I tubi chjamati permettenu l'istanze di l'applicazione in un cluster per esse cunnessi in gruppi in modu chì a cunnessione hà proprietà cum'è affidabilità, ordine, è sensibilità di fallimentu.

equilibratore di carica

Quandu si stallanu un equilibratore cum'è un controller di ingressu in un cluster Kubernetes, hè impurtante di mantene in mente e cose seguenti:

U travagliu di Keycloak implica chì l'indirizzu remotu di u cliente chì cunnette via HTTP à u servitore d'autentificazione hè l'indirizzu IP veru di l'urdinatore cliente. I paràmetri di l'equilibriu è di l'ingressu duveranu stabilisce currettamente l'intestazione HTTP X-Forwarded-For и X-Forwarded-Proto, è mantene u titulu originale HOST. l'ultima versione ingress-nginx (> 0.22.0) disattiva per automaticamente

Attivazione di bandiera proxy-address-forwarding stabilendu una variabile d'ambiente PROXY_ADDRESS_FORWARDING в true dà Keycloak a cunniscenza chì hè in esecuzione daretu à un proxy.

Avete ancu bisognu di attivà sessioni appiccicose in entrata. Keycloak usa a cache distribuita di Infinispan per almacenà e dati assuciati cù a sessione di autentificazione attuale è a sessione d'utilizatore. I caches sò unicu pruprietariu per difettu, in altre parolle chì a sessione particulare hè almacenata in un nodu di cluster è altri nodi devenu dumandà à distanza s'ellu anu bisognu di accessu à quella sessione.

In particulare, contru à a documentazione, l'attachete una sessione cù u nome di cookie ùn hà micca travagliatu per noi AUTH_SESSION_ID. Keycloak hà ricivutu u redirect, cusì ricumandemu di sceglie un nome di cookie differenti per a sessione appiccicosa.

Keycloak attache ancu u nome di l'ospite chì hà rispostu prima AUTH_SESSION_ID, è postu chì ogni node in a versione assai dispunibile usa a stessa basa di dati, ognunu di elli deve avè un ID di nodu separatu è unicu per a gestione di e transazzione. Hè cunsigliatu di mette in JAVA_OPTS login jboss.node.name и jboss.tx.node.id unicu per ogni node - per esempiu, pudete stabilisce u nome di u pod. Se mette u nome di u pod - ùn vi scurdate di u limitu di 23 caratteri per e variabili jboss, cusì hè megliu aduprà StatefulSet, micca Deployment.

Un altru rake - se un pod hè sguassatu o riavviatu, u so cache hè persu. Cù questu in mente, vale a pena stabilisce u numeru di pruprietarii di cache per tutti i cache à almenu dui, cusì ci sarà una copia di u cache. A suluzione hè di curriri script per Wildfly quandu principia u pod, mette in u cartulare /opt/jboss/startup-scripts in cuntainer :

U cuntenutu di u script

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

poi stabilisce u valore di a variabile di l'ambiente CACHE_OWNERS à u necessariu.

Rete privata cù supportu IP multicast

Sè vo aduprate Weavenet cum'è u vostru CNI, u multicast hà da travaglià subitu - è i vostri nodi Keycloak si vederanu quandu sò in funzione.

Se ùn avete micca supportu multicast ip in u vostru cluster Kubernetes, pudete cunfigurà JGroups per travaglià cù altri protokolli per truvà nodi.

A prima opzione hè di utilizà KUBE_DNSchì usa headless service per truvà i nodi Keycloak, basta à passà JGroups u nome di u serviziu chì serà utilizatu per truvà i nodi.

Una altra opzione hè di utilizà u metudu KUBE_PING, chì travaglia cù l'API per truvà nodi (avete bisognu di cunfigurà serviceAccount cù diritti list и get, è dopu cunfigurà i pods per travaglià cun questu serviceAccount).

Cumu i nodi sò cercati per JGroups hè cunfiguratu da l'ambienti variabili JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. di KUBE_PING avete bisognu di sceglie pods dumandendu namespace и labels.

️ Se utilizate multicast è eseguite dui o più clusters Keycloak in u stessu cluster Kubernetes (dicemu unu in u spaziu di nomi production, u sicondu - staging) - i nodi da un cluster Keycloak ponu unisce à un altru cluster. Assicuratevi d'utilizà un indirizzu multicast unicu per ogni cluster per stabilisce variàbilijboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replicazione trà i centri di dati

Eseguite Keycloak in modalità HA in Kubernetes

Cunnessibilità

Keycloak usa più Clusters Infinispan Cache separati per ogni centru di dati chì ospita clusters Keycloack composti da nodi Keycloak. Ma à u listessu tempu, ùn ci hè nisuna differenza trà i nodi Keycloak in diversi centri di dati.

I nodi Keycloak utilizanu una griglia di dati Java esterna (servitori Infinispan) per cumunicà trà i centri di dati. A cumunicazione travaglia secondu u protocolu Infinispan HotRod.

I caches Infinispan devenu esse cunfigurati cù l'attributu remoteStore, cusì chì i dati ponu esse guardati in remoto (in un altru centru di dati, ca. traduttore) cache. Ci sò clusters infinispan separati trà i servitori JDG, cusì dati almacenati in JDG1 in situ site1 serà replicatu à JDG2 in situ site2.

Infine, u servitore JDG di ricezione notifica à i servitori Keycloak di u so cluster via cunnessione di u cliente, chì hè una caratteristica di u protocolu HotRod. Keycloak nodi attivati site2 aghjurnà i so cache Infinispan è a sessione d'utilizatore particulare diventa dispunibule nantu à i nodi Keycloak site2.

Hè ancu pussibule per certi cache ùn esse micca salvatu è ricusate cumplettamente di scrive dati attraversu u servitore Infinispan. Per fà questu, avete bisognu di sguassà u paràmetru remote-store cache Infinispan specificu (in u schedariu standalone-ha.xml), dopu à quale alcuni specifichi replicated-cache ùn serà ancu più necessariu da u latu di u servitore Infinispan.

Configurazione di cache

Ci sò dui tipi di cache in Keycloak:

  • Locale. Hè situatu vicinu à a basa, serve per riduce a carica nantu à a basa di dati, è ancu per riduce a latenza di risposta. Stu tipu di cache guarda u regnu, i clienti, i roli è i metadati di l'utilizatori. Stu tipu di cache ùn hè micca replicatu ancu se sta cache hè parti di un cluster Keycloak. Se qualchì entrata in u cache cambia, un missaghju di cambiamentu hè mandatu à u restu di i servitori in u cluster, dopu chì l'entrata hè escluduta da u cache. vede a descrizzione work sottu per una descrizzione più dettagliata di a prucedura.

  • Replicable. Prucessa sessioni d'utilizatori, tokens offline, è monitoreghja i fallimenti di login per detectà i tentativi di phishing di password è altri attacchi. I dati guardati in questi cache sò tempurane, almacenati solu in RAM, ma ponu esse replicati in u cluster.

Caches Infinispan

Sessioni - un cuncettu in Keycloak, cache separati, chì sò chjamati authenticationSessions, sò usati per almacenà e dati di utilizatori specifichi. E dumande da queste cache sò generalmente necessarie da u navigatore è i servitori Keycloak, micca l'applicazioni. Hè quì chì a dependenza di e sessioni appiccicose si manifesta, è tali cache ùn anu micca bisognu di esse replicate, ancu in u casu di u modu Active-Active.

Tokens d'azzione. Un altru cuncettu, di solitu usatu per diversi scenarii, quandu, per esempiu, l'utilizatore hà bisognu di fà qualcosa in modu asincronu per mail. Per esempiu, durante a prucedura forget password cache actionTokens utilizatu per seguità a metadata di tokens rilativi - per esempiu, u token hè digià statu utilizatu è ùn pò micca esse riattivatu. Stu tipu di cache deve esse riplicatu trà i datacenters.

Caching è scadenza di dati almacenati travaglia per caccià a carica da a basa di dati. Questa cache migliora u rendiment, ma aghjunghje un prublema evidenti. Se un servitore Keycloak aghjurnà i dati, u restu di i servitori deve esse notificatu per pudè aghjurnà i so cache. Keycloak usa cache lucali realms, users и authorization per cache dati da a basa di dati.

Ci hè ancu una cache separata work, chì hè replicatu in tutti i centri di dati. Hè stessu ùn guarda micca dati da a basa di dati, ma serve per mandà messagi di data invechje à i nodi di cluster trà i centri di dati. In altri palori, quandu i dati sò aghjurnati, u node Keycloak manda un missaghju à altri nodi in u so centru di dati, è ancu nodi in altri centri di dati. Dopu avè ricivutu un tali missaghju, ogni nodu purga i dati currispondenti in i so caches lucali.

Sessioni d'utilizatori. Caches cù nomi sessions, clientSessions, offlineSessions и offlineClientSessions, sò generalmente replicati trà i centri di dati è serve per almacenà e dati nantu à e sessioni d'utilizatori chì sò attivi mentre l'utilizatore hè attivu in u navigatore. Questi cache funzionanu cù l'applicazione chì gestisce e dumande HTTP da l'utilizatori finali, per quessa, sò assuciati cù sessioni sticky è devenu esse replicati trà i datacenters.

prutezzione di forza bruta. Cache loginFailures utilizatu per seguità i dati di errore di login, cum'è u numeru di volte chì un utilizatore hà inseritu una password incorrecta. A replicazione di sta cache hè à l'amministratore. Ma per un calculu precisu, vale a pena attivà a replicazione trà i centri di dati. Ma da l'altra banda, sè ùn avete micca riplicate sta dati, puderete migliurà u rendiment, è se sta quistione si pone, a replicazione ùn pò esse attivata.

Quandu stende un cluster Infinispan, avete bisognu di aghjunghje definizioni di cache à u schedariu di paràmetri:

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

Duvete cunfigurà è inizià u cluster Infinispan prima di eseguisce u cluster Keycloak

Allora avete bisognu di stabilisce remoteStore per i cache Keycloak. Per questu, un script hè abbastanza, chì hè fattu in modu simili à u precedente, chì hè utilizatu per stabilisce a variàbile CACHE_OWNERS, avete bisognu di salvà in un schedariu è mette in un cartulare /opt/jboss/startup-scripts:

U cuntenutu di u 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-server

Ùn vi scurdate di stallà JAVA_OPTS per i nodi Keycloak per travaglià HotRod: remote.cache.host, remote.cache.port è u nome di serviziu jboss.site.name.

Ligami è documentazioni supplementari

L'articulu hè statu traduttu è preparatu per Habr da l'impiegati Centru di furmazione Slurm - corsi intensivi, video corsi è furmazione corporativa da i praticanti (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Source: www.habr.com

Add a comment