Pokretanje Keycloaka u HA načinu rada na Kubernetesu

Pokretanje Keycloaka u HA načinu rada na Kubernetesu

TL; DR: opisat će se Keycloak, sustav kontrole pristupa otvorenog koda, analiza unutarnje strukture, detalji konfiguracije.

Uvod i ključne ideje

U ovom ćemo članku vidjeti osnovne ideje koje treba imati na umu prilikom postavljanja Keycloak klastera povrh Kubernetesa.

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

Keycloak je sveobuhvatan sustav napisan u Javi i izgrađen na vrhu aplikacijskog poslužitelja Divlja muha. Ukratko, to je okvir za autorizaciju koji korisnicima aplikacije daje mogućnosti federacije i SSO (single sign-on).

Pozivamo vas da pročitate službeni web stranica ili Wikipedia za detaljno razumijevanje.

Pokretanje Keycloaka

Keycloak zahtijeva dva trajna izvora podataka za rad:

  • Baza podataka koja se koristi za pohranu utvrđenih podataka, kao što su informacije o korisniku
  • Datagrid predmemorija, koja se koristi za predmemoriju podataka iz baze podataka, kao i za pohranu nekih kratkotrajnih metapodataka koji se često mijenjaju, kao što su korisničke sesije. Provedeno Infinispan, koji je obično znatno brži od baze podataka. Ali u svakom slučaju, podaci spremljeni u Infinispanu su prolazni - i ne moraju se nigdje spremati kada se klaster ponovno pokrene.

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

  • Normalan - jedan i samo jedan proces, konfiguriran putem datoteke samostalan.xml
  • Redoviti grozd (opcija visoke dostupnosti) - svi procesi moraju koristiti istu konfiguraciju, koja se mora ručno sinkronizirati. Postavke su pohranjene u datoteci samostalni-ha.xml, osim toga trebate napraviti zajednički pristup bazi podataka i balanseru opterećenja.
  • Klaster domene — pokretanje klastera u normalnom načinu rada brzo postaje rutina i dosadan zadatak kako klaster raste, jer svaki put kada se promijeni konfiguracija, sve promjene moraju biti napravljene na svakom čvoru klastera. Domenski način rada rješava ovaj problem postavljanjem neke zajedničke lokacije za pohranu i objavljivanjem konfiguracije. Te su postavke pohranjene u datoteci domena.xml
  • Replikacija između podatkovnih centara — ako želite pokrenuti Keycloak u klasteru od nekoliko podatkovnih centara, najčešće na različitim geografskim lokacijama. U ovoj će opciji svaki podatkovni centar imati vlastiti klaster Keycloak poslužitelja.

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

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

Uobičajen klaster Keycloak

Za pokretanje Keycloaka u ovom načinu rada potrebno vam je:

  • konfigurirati vanjsku dijeljenu bazu podataka
  • instalirajte balanser opterećenja
  • imati internu mrežu s IP multicast podrškom

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

Da biste bolje razumjeli kako Keycloak radi u failover (HA) klasteru, važno je znati koliko sve ovisi o Wildflyjevim mogućnostima klasteriranja.

Wildfly koristi nekoliko podsustava, neki od njih se koriste kao load balancer, neki za toleranciju grešaka. Balansiranje opterećenja osigurava dostupnost aplikacije kada je čvor klastera preopterećen, a tolerancija grešaka osigurava dostupnost aplikacije čak i ako neki čvorovi klastera zakažu. Neki od ovih podsustava:

  • mod_cluster: Radi u kombinaciji s Apacheom kao HTTP balanser opterećenja, ovisi o TCP multicastu za pronalaženje hostova prema zadanim postavkama. Može se zamijeniti vanjskim balanserom.

  • infinispan: Distribuirana predmemorija koja koristi JGroups kanale kao prijenosni sloj. Dodatno, može koristiti HotRod protokol za komunikaciju s vanjskim Infinispan klasterom za sinkronizaciju sadržaja predmemorije.

  • jgroups: Pruža grupnu komunikacijsku podršku za visoko dostupne usluge temeljene na kanalima JGroups. Imenovani vodovi omogućuju povezivanje instanci aplikacije u klasteru u grupe tako da komunikacija ima svojstva kao što su pouzdanost, urednost i osjetljivost na kvarove.

Balansiranje opterećenja

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

Keycloak pretpostavlja da je udaljena adresa klijenta koji se povezuje putem HTTP-a s autentifikacijskim poslužiteljem prava IP adresa klijentskog računala. Postavke balansera i ulaza trebale bi ispravno postaviti HTTP zaglavlja X-Forwarded-For и X-Forwarded-Proto, a također spremite izvorni naslov HOST. Najnovija verzija ingress-nginx (>0.22.0) onemogućuje ovo prema zadanim postavkama

Aktiviranje zastave proxy-address-forwarding postavljanjem varijable okoline PROXY_ADDRESS_FORWARDING в true daje Keycloaku do znanja da radi iza proxyja.

Također morate omogućiti ljepljive sesije u ulazu. Keycloak koristi distribuiranu Infinispan predmemoriju za pohranu podataka povezanih s trenutnom autentifikacijskom sesijom i korisničkom sesijom. Predmemorije prema zadanim postavkama rade s jednim vlasnikom, drugim riječima, ta određena sesija pohranjena je na nekom čvoru u klasteru, a drugi čvorovi moraju mu postavljati upite na daljinu ako trebaju pristup toj sesiji.

Konkretno, suprotno dokumentaciji, prilaganje sesije s imenom cookie nije nam uspjelo AUTH_SESSION_ID. Keycloak ima petlju za preusmjeravanje, stoga preporučujemo odabir drugog naziva kolačića za ljepljivu sesiju.

Keycloak također prilaže naziv čvora koji je prvi odgovorio AUTH_SESSION_ID, a budući da svaki čvor u vrlo dostupnoj verziji koristi istu bazu podataka, svaki od njih morati imati zasebni i jedinstveni identifikator čvora za upravljanje transakcijama. Preporuča se staviti u JAVA_OPTS parametri jboss.node.name и jboss.tx.node.id jedinstven za svaki čvor - možete, na primjer, staviti naziv mahune. Ako stavite naziv modula, 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 izbriše ili ponovno pokrene, njegova predmemorija se gubi. Uzimajući ovo u obzir, vrijedi postaviti broj vlasnika predmemorije za sve predmemorije na najmanje dva, tako da kopija predmemorije ostane. Rješenje je trčanje scenarij za Wildfly prilikom pokretanja modula, stavljajući ga u imenik /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 okoline CACHE_OWNERS na traženo.

Privatna mreža s IP multicast podrškom

Ako koristite Weavenet kao CNI, multicast će raditi odmah - a vaši Keycloak čvorovi će se međusobno vidjeti čim se pokrenu.

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

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

Druga mogućnost je korištenje metode KUBE_PING, koji radi s API-jem za traženje čvorova (morate konfigurirati serviceAccount s pravima list и get, a zatim konfigurirajte module za rad s ovim serviceAccount).

Način na koji JGroups pronalazi čvorove konfigurira se postavljanjem varijabli okoline JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Za KUBE_PING trebate odabrati mahune traženjem namespace и labels.

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

Replikacija između podatkovnih centara

Pokretanje Keycloaka u HA načinu rada na Kubernetesu

Link

Keycloak koristi više zasebnih Infinispan klastera predmemorije za svaki podatkovni centar 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 vanjski Java Data Grid (Infinispan poslužitelji) za komunikaciju između podatkovnih centara. Komunikacija radi prema protokolu Infinispan HotRod.

Infinispan predmemorije moraju biti konfigurirane s atributom remoteStore, tako da se podaci mogu pohraniti na daljinu (u drugom podatkovnom centru, cca. prevoditelj) spremišta. Među JDG poslužiteljima postoje zasebni infinispan klasteri, tako da podaci pohranjeni na JDG1 na licu mjesta site1 će se replicirati na JDG2 na licu mjesta site2.

I na kraju, primateljski JDG poslužitelj obavještava Keycloak poslužitelje o svom klasteru putem klijentskih veza, što je značajka HotRod protokola. Keycloak čvorovi uključeni site2 ažuriraju svoje Infinispan predmemorije i određena korisnička sesija također postaje dostupna na Keycloak čvorovima na site2.

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

Postavljanje predmemorija

U Keycloaku postoje dvije vrste predmemorija:

  • Lokalni. Nalazi se uz bazu i služi za smanjenje opterećenja baze, kao i za smanjenje kašnjenja odgovora. Ova vrsta predmemorije pohranjuje područje, klijente, uloge i korisničke metapodatke. Ova vrsta predmemorije nije replicirana, čak i ako je predmemorija dio Keycloak klastera. Ako se unos u predmemoriju promijeni, poruka o promjeni šalje se preostalim poslužiteljima u klasteru, nakon čega se unos isključuje iz predmemorije. Vidi opis work U nastavku pogledajte detaljniji opis postupka.

  • Replicirano. Obrađuje korisničke sesije, izvanmrežne tokene, a također prati pogreške pri prijavi kako bi otkrio pokušaje krađe identiteta lozinke i druge napade. Podaci pohranjeni u ovim predmemorijama su privremeni, pohranjeni samo u RAM-u, ali se mogu replicirati u klasteru.

Infinispan spremišta

Sjednice - koncept u Keycloaku, odvojene predmemorije tzv authenticationSessions, koriste se za pohranu podataka određenih korisnika. Zahtjevi iz tih predmemorija obično su potrebni pregledniku i Keycloak poslužiteljima, a ne aplikacijama. Ovdje dolazi do izražaja ovisnost o ljepljivim sesijama, a same takve predmemorije ne moraju se replicirati, čak ni u slučaju Active-Active moda.

Akcijski žetoni. Drugi koncept, koji se obično koristi za različite scenarije kada, na primjer, korisnik mora učiniti nešto asinkrono putem pošte. Na primjer, tijekom postupka forget password predmemorija actionTokens koristi se za praćenje metapodataka povezanih tokena - na primjer, token je već korišten i ne može se ponovno aktivirati. Ovu vrstu predmemorije obično treba replicirati između podatkovnih centara.

Predmemoriranje i starenje pohranjenih podataka radi na rasterećenju baze podataka. Ova vrsta predmemoriranja poboljšava performanse, ali dodaje očiti problem. Ako jedan Keycloak poslužitelj ažurira podatke, ostali poslužitelji moraju biti obaviješteni kako bi mogli ažurirati podatke u svojim predmemorijama. Keycloak koristi lokalne predmemorije realms, users и authorization za keširanje podataka iz baze podataka.

Tu je i zasebna predmemorija 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 čvorovima klastera između podatkovnih centara. 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 primitka takve poruke, svaki čvor briše odgovarajuće podatke u svojim lokalnim predmemorijama.

Korisničke sesije. Spremišta s imenima sessions, clientSessions, offlineSessions и offlineClientSessions, obično se repliciraju između podatkovnih centara i služe za pohranu podataka o korisničkim sesijama koje su aktivne dok je korisnik aktivan u pregledniku. Ove predmemorije rade s aplikacijom koja obrađuje HTTP zahtjeve krajnjih korisnika, tako da su povezane s ljepljivim sesijama i moraju se replicirati između podatkovnih centara.

Zaštita grubom silom. Predmemorija loginFailures Koristi se za praćenje podataka o pogrešci prijave, kao što je koliko je puta korisnik unio netočnu lozinku. Replikacija ove predmemorije odgovornost je administratora. Ali za toč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 pojavi ovaj problem, replikacija se možda neće aktivirati.

Prilikom pokretanja Infinispan klastera, morate dodati definicije predmemorije 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 klaster Infinispan prije pokretanja klastera Keycloak

Zatim morate konfigurirati remoteStore za Keycloak predmemorije. Za to je dovoljna skripta koja se radi slično kao prethodna kojom se postavlja varijabla 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 HotRoda: remote.cache.host, remote.cache.port i naziv usluge jboss.site.name.

Linkovi i dodatna dokumentacija

Članak su za Habr preveli i pripremili djelatnici Slurm trening centar — intenzivni tečajevi, videotečajevi i korporativna obuka stručnjaka iz prakse (Kubernetes, DevOps, Docker, Ansible, Ceph, SRE)

Izvor: www.habr.com

Dodajte komentar