DEA.FI

Moonforge, osa 2

Moi taas.

Viime kerralla kertoilin taustoja tästä tuoreimmasta Kubernetes-seikkailustani, ja siihen liittyen autentikaatio-asioista. Lupasin kertoilla mitä kokkailin, joten pistetään pöytä koreaksi :>

Kuitenkin ennen sitä muutama metasana itse tuotannosta. Etenkin tällä kaudella minulla on tainnut olla jokaista videota varten kirjoitettuna käsikirjoitus, sekä siitä sitten vielä jatkojalostettuna vuorosanat, jotka ovat olleet esillä heti kameran linssi alapuolella. Tarkoituksenani on aina ollut kirjoittaa teksti siten, että sen voisi lukea sanasta sanaan suoraan, ja lisäarvona videomuodossa olisi sanojen painotus ja lausunta. Tarkoituksena ennen kaikkea helpottaa editointivaihetta, sillä vapaan sanan editoiminen koherentiksi kokonaisuudeksi on perseestä. Ja muutenkin tuntuu, että näillä omilla kyvyillä ja vaatimuksilla jollain tavoin valmisteltu esitys on vain se parempi ratkaisu. Toisaalta olen yrittänyt kuitenkin olla lukematta näitä sanoja suoraan, ja sen sijaan katsoa kameraan, tai muulla tavoin yrittänyt vähän elävöittää esiintymistä - sen mitä omat kommunikaatiotaidot riittävät. Olen onnistunut tässä mielestäni vaihtelevasti, mutta kuitenkin ehkä melko hyvin. Tästä huolimatta yritän tällä kertaa pysytellä tiukasti vuorosanoissa. Jännä nähdä miltä tämä näyttää. Ja viime videolle taisi löytyä aika hyvä kamerakulma - harmi vaan, että kamera on sen verran kaukana, että ei oikein meinaa nähdä enää sinne asti :s

Mutta takaisin asiaan:

Kuten siis kuvasin, niin turvallinen ja helppokäyttöinen pääsynhallinta vaikutti asialta, jota ei oikein ollut olemassa, ja vaikutti siltä, että helpoin tapa olisi tehdä itse jonkinlainen toteutus. Nyt toteutus on tehty, ja olen edelleen sitä mieltä, että päätös oli oikea. Tämä oli lähes petollisen helppoa, mutta toisaalta asiakin on lopulta varsin yksinkertainen, kuten toivottavasti onnistun selostamaan:

Kuten jo tiedättekin, niin monesti minulla on tapana keksiä pyörä uudelleen. Houkuttelevaa se olisi ollut tässäkin tapauksessa, mutta täysin oman IDP:n toteuttaminen on jo muutenkin ihan riittävän riskialatista puuhaa, joten päätin kerrankin olla vähän realistisempi. Käyttäjien tunnistaminen kun on ehkä myös se kaikkein riskialttein asia koko hommassa, joten jos voin käyttää valmista toteutusta siihen, niin muun projektin uskottavuus kasvaa huomattavasti. Ja kun se varsinainen ongelmakin oli nimenomaan kirjautuneiden käyttäjien luvitus, sekä se viime kerralla kuvattu foliohattuilu klusterissa hostattujen palveluiden integritettin suhteen.

Kun tällä pohjustuksella sitten kävin miettimään, että mitä lopulta tarvitaan, niin yllätyin suuresti. Vastaus oli nimittäin, ettei tarvittaisi paljoakaan. Se kuulostaa tietoturvamielessäkin jo varovaisen positiiviselta, sillä mitä vähemmän pinta-alaa, sitä vähemmän sinne mahtuu bugejakin. Ja ei-juuri-mitään kuulosti noin muutenkin sellaiselta kivalta vaihtelulta projektin kooksi - yleensä kun puhutaan vuosikymmenistä :S

Tämähän voi kaikki vielä osoittautua karmivaksi virheeksi, mutta kun ottaa vielä huomioon senkin, että olen toista vuosikymmentä pyörinyt tässäkin maailmassa, ja sen lisäksi jossain määrin työksenikin käyttänyt joistain avaintekniikoista - eli ASP.NETin identiteetistä ja JWT:n pyörittelystä - niin kyllä tästä kohtuullisen varma olo tuli siitä, että minä pystyn tähän. Ja etenkin kun vielä miettii sitä toista vaihtoehtoa, eli Keycloakin säätämistä ilman kokemusta, niin tämä nyt oli vain se pienempi paha.

Mutta nyt kun olen saanut oman olemassaoloni perusteltua, niin käydään sitten lopulta siihen itse asiaan:

---

Koodasin siis palvelun, johon käyttäjä ensin kirjautuu OpenID Connectilla Keycloakissa, ja tämän jälkeen käyttäjä voi sitten palvelun kautta hyväksyä kirjautumisyrityksiä muihin sovelluksiin. Ja siinä se.

Kirjautuminen on toki monipuolinen verbi. Sen voi tässä tapauksessa käsittää kahdella-kolmella tavalla. Lisäksi kerroin, kuinka kaipasin tapaa antaa jollekin sovellukselle lupa tehdä asioita toisessa sovelluksessa käyttäjän nimissä, joten sekin on tässä toteutettu. Ja koska jatkuva kirjautumisten hyväksyminen on hieman rasittavaa, niin käyttäjänä voi ennalta tallentaa hyväksyntänsä eri sovelluksille.

Lisäksi tässä toteutuksessa näkyy vahvasti se, että en oikein enää pidä web-pohjaisista asetustenhallinta-ohjelmista, joten järjestelmän kaikki käyttöoikeudet kuvataan parissa helposti hallittavassa selväkielisessä JSON-dokumentissa. Toki asetustiedostoille olisi mahdollista rakentaa käyttöliittymä tulevaisuudessa. Mut ydinajatuksena kuitenkin se, että tiedosto on helposti versioitavissa ja kuvaa yksiselitteisesti kerralla koko järjestelmän tilan, kuten esim. vaikka Terraformin manifestit tekevät.

Ja sitten vielä lopuksi mitä tulee integrointiin, niin nyt kun kaikki käyttäjän vahvistamat kirjautumiset taotaan JWT-muotoon, niin olisi teoriassa perin helppoa käyttää Traefikin tai kunkin sovelluksen omaa JWT-validaatiota. Totuus ei kuitenkaan ole ihan niin yksinkertainen, joten tarjolla on myös edelliskerralla kuvattu Forward Auth -käyttötapa.

---

Toteutustapa on siis (tietenkin) .NET (7), ja apuna OpenID Connect, Keycloak ja kirjasto JWT-tokeneiden pyörittelyyn.

JavaScript ja frontend eivät oikein ole se mun juttu, ja lisäksi koska tykkään nopeasti latautuvista sivuista, niin käyttöliittymän HTML on esirenderöity palvelimella. Ihan vähän vanilla-JS on kuitenkin mukana toteuttamaan saavutettavutta, mutta skriptien latautumista ei edes tarvitse odottaa, ainakaan kunhan ehdin sen async-määreen sinne lisätä. Vaikka sinänsähän sen merkitys ei ole iso. Ja toki toinen jossain määrin tärkeä näkökulma on myös se, että IDP vaatii paljon HTTP-only keksien käsittelyä, ja se olisikin mahdotonta jos olisi vain pelkkä SPA. Toki se olisi ehkä vähän mahdollisempaa kuin mitä alunperin arvelin, mutta ehkä ainakin tietoturva on hieman helpompi hoitaa kun ei ole olemassa APIa, jossa pitäisi miettiä CORS-asioita. Ainakin edes se, että koko homma toimii myös täysin ilman JS.

Nyt on taas ollut tätä puhuvaa päätä sen verran paljon videolla, että jos _lopultakin_ esittelisin miltä tämä toteutus näyttää sielä pellin alla. Saatatte havaita paljon yhtäläisyyksiä OIDC:n eri floweihin, mutta en kuitenkaan varsinaisesti yritä olla niiden kanssa yhteensopiva, sillä siinä päästään taas siihen kompleksisuuden ongelmaan, että ehkä jotain tekeekin väärin. Kun homma vain tätä käyttötapausta varten, niin kokonaisuus on helpompi pitää hallinnassa. Mutta siis pidemmittä puheitta, lopultakin sitä koodia:

---

Startup + OIDC + settings

Esimerkki miten kirjautuminen Keycloakiin menee, ja miltä claims näyttää ASP.NET:n puolesta.

Kirjautuminen esimerkkisovellukseen (browser flow), policy-konffien esittely.

Saved consent grafana.

Kirjautuminen app-flow sovellukseen ja cross-app token, custom expiry. Btw sneak-peak, tämähän voisi olla seuraavan videon aihe ;)

Forward auth middlewaren esittely. JWT.IO.

Consent-pagen varsinainen esittely koodinkin puolesta. Razor pages, keksit, .well-known reverse-proxy ajatus. Laajemmin esittelyä miksi näin:

---

Huhhuh... Ei mennyt ihan niin kuin suunnitelin, mutta paljon tuli kyllä asiaa sanottua. Ja paljon jäi vielä myös näyttämättä etenkin pienempiä toteutuksen yksityiskohtia. Laita kommenttia alle jos mielessä on jotain.

Nostona edellisestä vielä se, että kuten jo viime videossakin kerroin, niin OIDC tukemattomien sovellusten saaminen niitä tukevien sovellusten asemaan oli yksi suunnitteluperuste. Mielestäni sain sen nyt aika kivasti ja yksinkertaisesti toteutettua edellä esitetyllä kirjautumisen vahvistamisen polulla käyttöliittymän puolesta, ja sitten taas toteutuksen puolesta sovelluskohtaisesti luoduilla JTW-tokeneilla. Olen oikein tyytyväinen. Myös se defense in depth -näkökulma olisi kivasti jatkojalostettavissa näin.

Tähän puoleen - ja koko kokonaisuuteen - liittyen minulla on suuria suunnitelmia. Nyt näytin sen, kuinka käyttäjä voi tallentaa palveluille antamansa luvat, jolloin joka kerta ei tarvitse erikseen sallia käyttöä. Tämän vastakohta olisi se, että epäluotettavalla tietokoneella olisikin mahdollista kirjautua esim. QR-koodia avulla käyttäen apuna luotetumpaa laitetta, kuten omaa puhelinta. Tällöin luotetun laitteen päässä valitaan ja vahvistetaan oikeudet, ja halutessa jätetään joitain oikeuksia antamatta: vaikka minä olenkin järjestelmänvalvojan oikeuksilla about kaikessa, niin voisinkin silti omana itsenäni kirjautua sisään esim. vain-luku oikeuksilla viideksi minuutiksi, ja vain siihen yhteen tiettyyn järjestelmään. Tällöin ei haittaisi vaikka tuon kirjautumisen tietokone olisi kovinkin saastunut, koska sillä ei ole pääsyä täysin oikeuksin edes tuohon yksittäiseen sovellukseen, saati koko omaan käyttäjätiliini.

Tietoturvaa voisi muutenkin parantaa entisestään esim. siten, että tuo tokeneita takova osa palvelusta olisi oma mikropalvelunsa luotetummalla raudalla. Tämän jälkeen kaikki tuleva token-pyynnöt pitäisi sitten olla allekirjoitettu käyttäjän luottaman laitteen julkisella avaimella, ja vasta sen jälkeen palvelu suostuu allekirjoittamaan tokenin omalla avaimellaan. Eli siis kun kirjaudun johonkin sisään, niin kirjaudun tavalla tai toisella sisään, ja tämän jälkeen erikseen hyväksyn puhelimen kehotteen, ja sitten jatkan kirjautumis-flowta tavanomaiseen tapaan. Osana tuota hyväksyntää puhelin pelkän hyväksymisen lisäksi siis suorittaa tuon oman allekirjoituksensa kuhunkin pyyntöön.

Ai että se olisi sitten hienoa. Olisi vielä secure boot ja jokin rautatason kryptomoduuli, ja tosiaan tuo softa siten, että vaatii kaikesta huolimatta tuon ulkoisen laitteen hyväksynnän. Mutta ehkä tämä menee kovinkin paljon liian pitkälle, ja kaiken voi kiertää jonkin tyhmän virheen takia. Tai vaikka hyökkäämällä NTP-palveluja vastaan ja kierrättämällä jotain vanhaa kaapattua tokenia. Tai sitten jotain muuta reittiä hyökkäämällä koko Kubernetes-klusteria vastaan. Mutta onpahan ainakin unelmia.

---

Olen vähän miettinyt josko tämän kehtaisi julkaista avoimena lähdekoodina. Ehkä joku sitten jopa kertoisi auliisti mikä perusasia meni väärin. Tässäkin piti nimittäin vähän kerrata OWASP-suosituksia. Mutta se, että julkaisi jotain vaatisi taas niin kovin paljon extraa, ja sitten pitäisi miettiä lisensointiakin, ja ylläpitoa ja dokumentointia. Ja viestintää vielä isommin. Nyt voi vaan vähän aukoa päätään itsensä vuoksi, ja nähdä päiväunia siitä, että joku sai videosta jotain oikeasti hyödyllistäkin irti.

Btw, kuten jo viime kerrallakin sanoin, niin Kubernetes ja oma konttiregistry on <3 Jopa ilman kummempia CI/CD-putkia. Etenkin nyt kun .NET on sisäänrakennettu tuki konttien rakentamiseen, niin ei tarvitse edes olla Dockerfileä koko projektissa. Olen etenkin siitä ihan lumoissani.

Mutta että tällainen eepos tällä kertaa. Laita toki kommenttia alle, ja jos tykkäsit, niin kerro se toki myös YouTubelle.

Nähdään taas ensi kerralla - aihetta ei vielä ole. Moi moi.