Pokrenite Keycloak u HA modu na Kubernetesu

Pokrenite Keycloak u HA modu na Kubernetesu

TL; DR: bit će opisan Keycloak, sistem kontrole pristupa otvorenog koda, analiza interne strukture, detalji konfiguracije.

Uvod i ključne ideje

U ovom članku ćemo vidjeti osnovne ideje koje treba imati na umu kada postavljate Keycloak klaster na Kubernetes.

Ako želite saznati više o Keycloaku, pogledajte veze na kraju članka. Da biste se više uživjeli u praksu, možete učiti naše skladište sa modulom koji implementira glavne ideje ovog članka (vodič za pokretanje je tu, ovaj članak će pružiti pregled uređaja i postavki, cca. prevodilac).

Keycloak je sveobuhvatan sistem napisan na Javi i izgrađen na vrhu aplikacijskog servera divlja muva. Ukratko, to je okvir za autorizaciju koji korisnicima aplikacija daje mogućnosti federacije i SSO (single sign-on).

Pozivamo vas da pročitate službenu web stranica ili Wikipedia za detaljno razumevanje.

Pokretanje Keycloak-a

Keycloak zahtijeva dva trajna izvora podataka za pokretanje:

  • Baza podataka koja se koristi za pohranjivanje uspostavljenih podataka, kao što su korisničke informacije
  • Datagrid keš, koji se koristi za keširanje podataka iz baze podataka, kao i za pohranjivanje nekih kratkotrajnih metapodataka koji se često mijenjaju, kao što su korisničke sesije. Implementirano Infinispan, što je obično znatno brže od baze podataka. Ali u svakom slučaju, podaci sačuvani u Infinispanu su prolazni - i ne moraju se nigde spremati kada se klaster ponovo pokrene.

Keycloak radi u četiri različita načina:

  • običan - jedan i jedini proces, konfigurisan preko datoteke standalone.xml
  • Regularni klaster (opcija visoke dostupnosti) - svi procesi moraju koristiti istu konfiguraciju, koja se mora ručno sinkronizirati. Postavke su pohranjene u datoteci standalone-ha.xml, pored toga morate napraviti zajednički pristup bazi podataka i balansiranje opterećenja.
  • Klaster domena — pokretanje klastera u normalnom režimu brzo postaje rutinski i dosadan zadatak kako klaster raste, jer svaki put kada se konfiguracija promeni, sve promene se moraju izvršiti na svakom čvoru klastera. Način rada domene rješava ovaj problem postavljanjem neke dijeljene lokacije za pohranu i objavljivanjem konfiguracije. Ove postavke su pohranjene u datoteci domena.xml
  • Replikacija između data centara — ako želite da pokrenete Keycloak u grupi od nekoliko data centara, najčešće na različitim geografskim lokacijama. U ovoj opciji, svaki centar podataka će imati svoj vlastiti klaster Keycloak servera.

U ovom članku ćemo detaljno razmotriti drugu opciju, tj regularni klaster, a malo ćemo se dotaknuti i teme replikacije između podatkovnih centara, jer ima smisla pokrenuti ove dvije opcije u Kubernetesu. Srećom, u Kubernetesu nema problema sa sinhronizacijom postavki nekoliko podova (Keycloak čvorova), tako da klaster domena Neće biti teško za napraviti.

Također imajte na umu da riječ cluster budući da će se ostatak članka odnositi isključivo na grupu Keycloak čvorova koji rade zajedno, nema potrebe da se pozivate na Kubernetes klaster.

Redovni Keycloak klaster

Da pokrenete Keycloak u ovom načinu rada trebate:

  • konfigurirati eksternu dijeljenu bazu podataka
  • instalirajte balansator opterećenja
  • imaju internu mrežu s podrškom za IP multicast

Nećemo raspravljati o postavljanju vanjske baze podataka, jer to nije svrha ovog članka. Pretpostavimo da negdje postoji radna baza podataka - i da imamo tačku veze s njom. Jednostavno ćemo dodati ove podatke varijablama okruženja.

Da biste bolje razumjeli kako Keycloak funkcionira u klasteru za nadilaženje greške (HA), važno je znati koliko sve ovisi o Wildfly-jevim mogućnostima klasteriranja.

Wildfly koristi nekoliko podsistema, neki od njih se koriste kao balansator opterećenja, neki za toleranciju grešaka. Balansator opterećenja osigurava dostupnost aplikacije kada je čvor klastera preopterećen, a tolerancija grešaka osigurava dostupnost aplikacije čak i ako neki čvorovi klastera pokvare. Neki od ovih podsistema:

  • mod_cluster: Radi zajedno sa Apache-om kao HTTP balansator opterećenja, zavisi od TCP multicast-a za pronalaženje hostova po defaultu. Može se zamijeniti vanjskim balanserom.

  • infinispan: Distribuirani keš koji koristi JGroups kanale kao transportni sloj. Dodatno, može koristiti HotRod protokol za komunikaciju sa eksternim Infinispan klasterom za sinhronizaciju sadržaja keša.

  • jgroups: Pruža podršku grupne komunikacije za visoko dostupne usluge zasnovane na kanalima JGroups. Imenovane cijevi omogućavaju da se instance aplikacije u klasteru povežu u grupe tako da komunikacija ima svojstva kao što su pouzdanost, urednost i osjetljivost na kvarove.

Load Balancer

Kada instalirate balanser kao ulazni kontroler u Kubernetes klaster, važno je imati na umu sljedeće:

Keycloak pretpostavlja da je udaljena adresa klijenta koji se povezuje preko HTTP-a sa serverom za autentifikaciju prava IP adresa klijentskog računara. Postavke balansera i ulaza trebale bi ispravno postaviti HTTP zaglavlja X-Forwarded-For и X-Forwarded-Proto, a također sačuvajte originalni naslov HOST. Najnoviju verziju ingress-nginx (>0.22.0) deaktivira ovo prema zadanim postavkama

Aktiviranje zastave proxy-address-forwarding postavljanjem varijable okruženja PROXY_ADDRESS_FORWARDING в true daje Keycloaku razumijevanje da radi iza proxyja.

Također morate omogućiti lepljive sesije u ulazu. Keycloak koristi distribuiranu Infinispan keš memoriju za pohranu podataka povezanih s trenutnom sesijom provjere autentičnosti i sesijom korisnika. Predmemorije rade sa jednim vlasnikom prema zadanim postavkama, drugim riječima, ta određena sesija je pohranjena na nekom čvoru u klasteru, a drugi čvorovi ga moraju daljinski upitati ako im je potreban pristup toj sesiji.

Konkretno, suprotno dokumentaciji, prilaganje sesije sa imenom kolačić nije radilo za nas AUTH_SESSION_ID. Keycloak ima petlju za preusmjeravanje, pa preporučujemo da odaberete drugo ime kolačića za ljepljivu sesiju.

Keycloak također prilaže ime čvora koji je prvi odgovorio AUTH_SESSION_ID, a budući da svaki čvor u visoko dostupnoj verziji koristi istu bazu podataka, svaki od njih mora imati poseban i jedinstven identifikator čvora za upravljanje transakcijama. Preporučljivo je staviti JAVA_OPTS parametri jboss.node.name и jboss.tx.node.id jedinstveno za svaki čvor - možete, na primjer, staviti ime pod. Ako stavite naziv pod, ne zaboravite na ograničenje od 23 znaka za jboss varijable, tako da je bolje koristiti StatefulSet umjesto Deployment.

Još jedan rake - ako se pod obriše ili ponovo pokrene, njegov keš se gubi. Uzimajući ovo u obzir, vrijedi podesiti broj vlasnika keša za sve predmemorije na najmanje dva, kako bi kopija keša ostala. Rješenje je trčati scenario za Wildfly kada pokrećete pod, stavljajući ga u direktorij /opt/jboss/startup-scripts u kontejneru:

Sadržaj skripte

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

zatim postavite vrijednost varijable okruženja CACHE_OWNERS do potrebnog.

Privatna mreža sa podrškom za IP multicast

Ako koristite Weavenet kao CNI, multicast će raditi odmah - i vaši Keycloak čvorovi će se vidjeti čim budu pokrenuti.

Ako nemate podršku za ip multicast u svom Kubernetes klasteru, možete konfigurirati JGroups da rade s drugim protokolima za pronalaženje čvorova.

Prva opcija je korištenje KUBE_DNSkoji koristi headless service da biste pronašli Keycloak čvorove, jednostavno prosledite JGroups ime servisa koji će se koristiti za pronalaženje čvorova.

Druga opcija je korištenje metode KUBE_PING, koji radi s API-jem za traženje čvorova (morate konfigurirati serviceAccount sa pravima list и get, a zatim konfigurirajte podove da rade s ovim serviceAccount).

Način na koji JGroups pronalaze čvorove je konfigurisan postavljanjem varijabli okruženja JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. za KUBE_PING morate odabrati mahune tako što ćete pitati namespace и labels.

️ Ako koristite multicast i pokrenete dva ili više Keycloak klastera u jednom Kubernetes klasteru (recimo jedan u imenskom prostoru production, drugi - staging) - čvorovi jednog Keycloak klastera mogu se pridružiti drugom klasteru. Budite sigurni da koristite jedinstvenu multicast adresu za svaki klaster postavljanjem varijablijboss.default.multicast.address и jboss.modcluster.multicast.address в JAVA_OPTS.

Replikacija između data centara

Pokrenite Keycloak u HA modu na Kubernetesu

Povezivanje

Keycloak koristi više zasebnih Infinispan keš klastera za svaki centar podataka u kojem se nalaze Keycloack klasteri sastavljeni od Keycloak čvorova. Ali nema razlike između Keycloak čvorova u različitim podatkovnim centrima.

Keycloak čvorovi koriste eksternu Java Data Grid (Infinispan serveri) za komunikaciju između centara podataka. Komunikacija radi po protokolu Infinispan HotRod.

Infinispan kešovi moraju biti konfigurirani s atributom remoteStore, tako da se podaci mogu pohraniti na daljinu (u drugom podatkovnom centru, cca. prevodilac) keš memorije. Među JDG serverima postoje odvojeni infinispan klasteri, tako da podaci pohranjeni na JDG1 na lokaciji site1 će se replicirati na JDG2 na licu mjesta site2.

I na kraju, JDG server koji prima obaveštava Keycloak servere o svom klasteru preko klijentskih veza, što je karakteristika HotRod protokola. Keycloak čvorovi uključeni site2 ažuriraju svoje Infinispan keš memorije i određena korisnička sesija postaje dostupna i na Keycloak čvorovima uključenim site2.

Za neke keš memorije, također je moguće ne praviti sigurnosne kopije i u potpunosti izbjeći pisanje podataka preko Infinispan servera. Da biste to učinili, morate ukloniti postavku remote-store određeni Infinispan keš (u datoteci standalone-ha.xml), nakon čega neke specifične replicated-cache također više neće biti potreban na strani Infinispan servera.

Postavljanje keša

Postoje dvije vrste predmemorije u Keycloaku:

  • Lokalno. Nalazi se pored baze podataka i služi za smanjenje opterećenja baze podataka, kao i za smanjenje latencije odgovora. Ova vrsta keša pohranjuje područje, klijente, uloge i korisničke metapodatke. Ova vrsta keš memorije se ne replicira, čak i ako je keš dio klastera Keycloak. Ako se unos u keš memoriji promijeni, poruka o promjeni se šalje preostalim serverima u klasteru, nakon čega se unos isključuje iz keša. Vidi opis work Pogledajte ispod za detaljniji opis postupka.

  • Replicirano. Obrađuje korisničke sesije, offline tokene, a također prati greške prilikom prijavljivanja kako bi otkrio pokušaje krađe identiteta i druge napade. Podaci pohranjeni u ovim keš memorijama su privremeni, pohranjeni samo u RAM-u, ali se mogu replicirati u cijelom klasteru.

Infinispan kešovi

Sesije - koncept u Keycloaku, odvojeni kešovi tzv authenticationSessions, koriste se za pohranjivanje podataka određenih korisnika. Zahtjevi iz ovih keša obično su potrebni pretraživaču i Keycloak serverima, a ne aplikacijama. Ovdje dolazi do izražaja ovisnost o ljepljivim sesijama, a takve keš memorije nije potrebno replicirati, čak ni u slučaju aktivnog-aktivnog načina rada.

Akcioni tokeni. Drugi koncept, koji se obično koristi za različite scenarije kada, na primjer, korisnik mora učiniti nešto asinhrono putem pošte. Na primjer, tokom postupka forget password keš memorije actionTokens koristi se za praćenje metapodataka povezanih tokena - na primjer, token je već korišten i ne može se ponovo aktivirati. Ova vrsta keša obično se mora replicirati između centara podataka.

Keširanje i starenje pohranjenih podataka radi na rasterećenju baze podataka. Ova vrsta keširanja poboljšava performanse, ali dodaje očigledan problem. Ako jedan Keycloak server ažurira podatke, ostali serveri moraju biti obaviješteni kako bi mogli ažurirati podatke u svojim kešovima. Keycloak koristi lokalne keš memorije realms, users и authorization za keširanje podataka iz baze podataka.

Tu je i zasebna keš memorija work, koji se replicira u svim podatkovnim centrima. On sam ne pohranjuje nikakve podatke iz baze podataka, već služi za slanje poruka o starenju podataka do čvorova klastera između centara podataka. Drugim riječima, čim se podaci ažuriraju, Keycloak čvor šalje poruku drugim čvorovima u svom podatkovnom centru, kao i čvorovima u drugim podatkovnim centrima. Nakon primanja takve poruke, svaki čvor briše odgovarajuće podatke u svojoj lokalnoj keš memoriji.

Korisničke sesije. Keš memorije sa imenima sessions, clientSessions, offlineSessions и offlineClientSessions, obično se repliciraju između centara podataka i služe za pohranjivanje podataka o korisničkim sesijama koje su aktivne dok je korisnik aktivan u pretraživaču. Ove keš memorije rade s aplikacijom koja obrađuje HTTP zahtjeve krajnjih korisnika, tako da su povezani s ljepljivim sesijama i moraju se replicirati između centara podataka.

Zaštita od grube sile. Skladiste loginFailures Koristi se za praćenje podataka o grešci prilikom prijave, kao što je koliko je puta korisnik unio pogrešnu lozinku. Replikacija ove keš memorije je odgovornost administratora. Ali za tačan izračun vrijedi aktivirati replikaciju između podatkovnih centara. Ali s druge strane, ako ne replicirate ove podatke, poboljšat ćete performanse, a ako se ovaj problem pojavi, replikacija se možda neće aktivirati.

Kada pokrećete Infinispan klaster, morate dodati definicije keša u datoteku postavki:

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

Morate konfigurirati i pokrenuti Infinispan klaster prije pokretanja Keycloak klastera

Zatim morate konfigurisati remoteStore za Keycloak kešove. Za to je dovoljna skripta, koja se radi slično prethodnoj, koja se koristi za postavljanje varijable CACHE_OWNERS, morate ga spremiti u datoteku i staviti u direktorij /opt/jboss/startup-scripts:

Sadržaj skripte

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 zaboravite instalirati JAVA_OPTS za Keycloak čvorove za pokretanje HotRod-a: remote.cache.host, remote.cache.port i naziv usluge jboss.site.name.

Linkovi i dodatna dokumentacija

Članak su preveli i pripremili za Habr zaposlenici Centar za obuku u slurmu — intenzivni kursevi, video kursevi i korporativna obuka od praktičara (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

izvor: www.habr.com

Dodajte komentar