Eksempeloppsett av Ztoperator
For å illustrere hvordan en Ztoperator-AuthPolicy
fungerer, kan vi se på følgende eksempel-applikasjon kalt demo-app. demo-app
er en Python-applikasjon med et enkelt REST-API, som også distribuerer en Single Page Application (SPA). I kildekoden til demo-app
finnes det ingen mekanismer for tilgangsstyring eller beskyttelse av REST-API-endepunktene, og den har heller ikke funksjonalitet for å logge inn brukere med Microsoft Entra ID. Ztoperator-AuthPolicyen
tar seg av alt dette.
Kildekoden til demo-app
kan finnes på github.com/kartverket/skip-tilgangsstyring-demo. Dette repoet lar deg også teste og verifisere oppførselen til Ztoperator med et lokalt Kubernetes-cluster som likner på SKIP.
demo-app
består av tre ressurser:
- En
AzureAdApplication
som setter opp en klientregistrering mot Kartverkets Microsoft Entra ID. - En Skiperator-
Application
som enkelt ruller ut applikasjonen på SKIP. - En Ztoperator-
AuthPolicy
som håndhever at angitte endepunkter må presentere et gyldigaccess_token
, og i tillegg logger inn brukere der det er angitt.
Manifestene for disse ressursene finner du i tilgangsstyring-apps på GitHub.
🔹 AzureAdApplication
Før man går i gang med å sette opp en Ztoperator-AuthPolicy
må man ha en klienregistrering opprettet hos en OAuth 2.0 identitetstilbyder. Ettersom demo-app
er ment for intern bruk av Kartverket-ansatte, så er Microsoft Entra ID en passende identitetstilbyder. En AzureAdApplication
kan brukes for å opprette klientregistreringen, og følgende manifest oppretter en. Den er konfigurert til å tillate alle brukere innenfor Kartverket's Entra ID tenant, og har en redirect-url som peker til applikasjonen sin konfiguererte redirect-url. Dette er noe vi senere konfigurerer i Ztoperator-AuthPolicyen
, og Ztoperator håndterer dette endepunktet for oss. Etter å ha opprettet en klientregistrering i Microsoft Entra ID oppretter Azurerator også en Kubernetes-hemmelighet kalt entraid-secret
. Klientregistreringen kan ses i Azure Portal.
Redirect-url er noe demo-app
ikke trenger å forholde seg til. Ztoperator-AuthPolicy
konfigures med en redirect-url når man konfigurerer innlogging av brukere, og håndterer callbacks i authorization code flow.
apiVersion: nais.io/v1
kind: AzureAdApplication
metadata:
name: entraid-reg
namespace: ztoperator-demo
spec:
claims:
groups:
- id: a6578085-e0ee-4168-bf97-1b3026f6f3bd # Kartverket@kartverket.no
replyUrls:
- url: https://demo-app.atgcp1-sandbox.kartverket.cloud/oauth2/callback
secretName: entraid-secret
apiVersion: v1
kind: Secret
metadata:
name: entraid-secret
namespace: ztoperator-demo
type: Opaque
data:
AZURE_APP_CERTIFICATE_KEY_ID: ++++++++
AZURE_APP_CLIENT_ID: ++++++++
AZURE_APP_CLIENT_SECRET: ++++++++
AZURE_APP_JWK: ++++++++
AZURE_APP_JWKS: ++++++++
AZURE_APP_NEXT_CERTIFICATE_KEY_ID: ++++++++
AZURE_APP_NEXT_CLIENT_SECRET: ++++++++
AZURE_APP_NEXT_JWK: ++++++++
AZURE_APP_NEXT_PASSWORD_KEY_ID: ++++++++
AZURE_APP_PASSWORD_KEY_ID: ++++++++
AZURE_APP_PRE_AUTHORIZED_APPS: ++++++++
AZURE_APP_TENANT_ID: ++++++++
AZURE_APP_WELL_KNOWN_URL: ++++++++
AZURE_OPENID_CONFIG_ISSUER: ++++++++
AZURE_OPENID_CONFIG_JWKS_URI: ++++++++
AZURE_OPENID_CONFIG_TOKEN_ENDPOINT: ++++++++
🤖 Skiperator-Application
demo-app
er bygget til et Docker-image og lastet opp på GitHub Container Registry med følgende image-url: ghcr.io/kartverket/skip-tilgangsstyring-demo
. Den kan da deployes på SKIP med følgende manifest og nås på https://demo-app.atgcp1-sandbox.kartverket.cloud
.
Den uthevede delen av koden er viktig for at innlogging av sluttbrukere skal fungere. Hvorfor denne konfigurasjonen er viktig etterlates som en oppgave for leseren. Det brukeren må forholde seg til er hvilket secretName
det skal refereres til. Dette må matche en Kuberenetes-hemmelighet som Ztoperator oppretter som har navneformatet <NAVN PÅ AUTHPOLICY>-envoy-secret
. Ettersom demo-app
sin Ztoperator-AuthPolicy
heter auth-policy
, blir det korrekt med "secretName": "auth-policy-envoy-secret"
.
apiVersion: skiperator.kartverket.no/v1alpha1
kind: Application
metadata:
name: demo-app
namespace: ztoperator-demo
spec:
podSettings:
annotations:
sidecar.istio.io/userVolume: '[
{
"name": "istio-oauth2",
"secret": {
"secretName": "auth-policy-envoy-secret" # 👈 Må matche <NAVN PÅ AUTHPOLICY>-envoy-secret
}
}
]'
sidecar.istio.io/userVolumeMount: '[
{
"name": "istio-oauth2",
"mountPath": "/etc/istio/config",
"readonly": true
}
]'
image: ghcr.io/kartverket/skip-tilgangsstyring-demo@sha256:cde1805d4e87b99a6d8d73fd6ed6666eaef1691a598f0377d3a716366cc4c9e8
port: 5000
ingresses:
- demo-app.atgcp1-sandbox.kartverket.cloud
⛔️ Ztoperator-AuthPolicy
Vi har nå registrert en klient hos Microsoft Entra ID ved hjelp av Azurerator, og spunnet opp demo-app
som en Skiperator-Application
. Det siste steget er nå å beskytte applikasjonen med en Ztoperator-AuthPolicy
. Følgende manifest beskytter demo-app
og håndterer innlogging av brukere opp mot Microsoft Entra ID. Under går vi gjennom hvordan og hvorfor vi har konfigurert Ztoperator-AuthPolicy
som vi har gjort.
apiVersion: ztoperator.kartverket.no/v1alpha1
kind: AuthPolicy
metadata:
name: auth-policy
namespace: ztoperator-demo
spec:
enabled: true
selector:
matchLabels:
app: demo-app
wellKnownURI: https://login.microsoftonline.com/7f74c8a2-43ce-46b2-b0e8-b6306cba73a3/v2.0/.well-known/openid-configuration
audience:
- dcc29452-1fbc-4805-aeb9-cdb8f4e62147
ignoreAuthRules:
- paths:
- /*
authRules:
- paths:
- /api/beskyttet*
methods:
- POST
denyRedirect: true
- paths:
- /beskyttet
autoLogin:
enabled: true
loginPath: /login
logoutPath: /logout
redirectPath: /oauth2/callback
scopes:
- dcc29452-1fbc-4805-aeb9-cdb8f4e62147/.default
oAuthCredentials:
clientIDKey: AZURE_APP_CLIENT_ID
clientSecretKey: AZURE_APP_CLIENT_SECRET
secretRef: entraid-secret
outputClaimToHeaders:
- claim: preferred_username
header: x-email
- claim: name
header: x-name
spec.enabled
spec.enabled
lar deg toggle om tilgangsstyring skal være aktivert eller ikke.
spec.selector
spec.selector
spesifiserer hvilke pod
-er som skal beskyttes. Når man deployer en applikasjon med Skiperator så vil alle pod
-er få labelen app: <NAVN PÅ SKIPERTOR-APPLICATION>
.
spec.wellKnownURI
spec.wellKnownURI
trengs for å utføre OpenID Connect Discovery og hente ut relevant informasjon om identitetstilbyderren brukt under autentisering. Det er her man knytter Ztoperator-AuthPolicy
til en identitetstilbyder.
spec.audience
spec.audience
spesifiserer tiltenkt mottaker av aksess-tokenet. Dette er typisk en klient-ID eller en annen unik identifikator knyttet til klientregistreringen utført tidligere. Dette feltet et viktig å inkludere for å skille klientregistreringer hos en identitetstilbyder fra andre. spec.audience
sikrer at kun din klientregistrering kan bruke tokenet til å aksessere applikasjonens beskyttede endepunkter.
spec.ignoreAuthRules
I utgangspunktet er alle endepunkter mot demo-app
åpne, noe som angis med paths: [/*]
. Når HTTP-verb ikke er spesifisert, gjelder regelen for alle metoder. Dersom et endepunkt ikke er eksplisitt definert – eller det finnes overlapp mellom spec.ignoreAuthRules
og spec.authRules
– vil Ztoperator alltid falle tilbake til å beskytte endepunktet.
spec.authRules
POST
mot endpunktene /api/beskyttet*
(/api/beskyttet
og alle under-stier) skal være beskyttet. Ettersom kall mot /api/beskyttet*
gjøres av en Single Page Application i en AJAX-request, har man ikke muligheten til å følge 302 Redirect
og logge inn brukeren automatisk. For å løse dette kan man spesifisere denyRedirect: true
for å forhindre at Ztoperator returnerer en 302 Redirect
(den sender da en 401 Unauthorized
i stedet), og heller omdirigere brukeren selv i frontend-koden til den konfigurerte innloggingsstien (/login
i dette tilfellet). Stien /beskyttet
skal også beskyttes, og ettersom det ikke spesifiseres denyRedirect: true
vil uautentiserte brukere automatisk bli omdirigert til innlogging mot Microsoft Entra ID.
spec.autoLogin
spec.autoLogin
lar deg konfigurere innlogging mot identitetstilbyderen. Innlogging vil bli gjort automatisk hvis brukere aksesserer beskyttede endepunkt uten en sesjon (med mindre spec.authRules[].denyRedirect
er satt til true
). I tillegg konfigureres det en dedikert innloggingssti som brukere kan navigeres til for å starte en inlogget sesjon.
spec.autoLogin.enabled
lar deg toggle om innlogging skal være aktivert eller ikke.spec.autoLogin.loginPath
spesifiserer hvilken sti man skal aksessere for å starte en innlogget sesjon. Etter vellykket innlogging vil en forespørsel sendes til den konfigurerte innloggingsstien i applikasjonen din, og det er da opp til deg å håndtere hva som skal skje.spec.autoLogin.logoutPath
spesifiserer hvilken sti man skal aksessere for å logge ut brukeren lokalt og opp mot den konfigurerte identitetstilbyderen (RP-initiated logout). Etter vellykket utlogging vil brukeren bli omdirigert til nettsiden sin rotsti (dvs.https://<HOST>/
).spec.autoLogin.redirectPath
spesifiserer omdirigeringssti brukt under Authorization Code Flow. Dette er en sti som du ikke tregner å håndtere i applikasjonskoden din, men den må samsvare med en av de konfigurerte omdirigeringsstiene i klientregistreringen din.spec.autoLogin.scopes
spesifiserer hvilke scopes som skal brukes i under Authorization Code Flow. I dette eksempelet er det spesifisert som<CLIENT ID>/.default
, som er et scope Microsoft Entra ID tilbyr som implisitt inneholder alle tilgjengelige scopes for klientregistreringen.
Ztoperator-AuthPolicy
benytter seg av cookies for å lagre innlogget sesjon i nettleseren. OIDC Single Logout (SLO) benytter seg av iframe
for å logge brukeren ut av alle nettstedene som brukeren har en aktiv SSO-sesjon hos. Ettersom cookies som Ztoperator-AuthPolicy
setter er HTTP-only så vil de ikke kunne aksesseres i en iframe
, og SLO er derfor ikke støttet av Ztoperator-AuthPolicy
.
spec.oAuthCredentials
spec.oAuthCredentials
spesifiserer hvilken klient-ID og klient-hemmelighet som skal brukes under innlogging av brukere.
spec.oAuthCredentials.secretRef
spesifiserer hvilken Kubernetes-hemmelighet som har hemmelightene.spec.oAuthCredentials.clientIDKey
spesifiserer hvilken data-nøkkel i Kuberenetes-hemmeligheten som har klient-IDen.spec.oAuthCredentials.clientSecretKey
spesifiserer hvilken data-nøkkel i Kuberenetes-hemmeligheten som har klient-hemmeligheten.
spec.outputClaimToHeaders
spec.outputClaimToHeaders
spesifiserer hvilke claims i access_token
som skal sendes som HTTP-headers til applikasjonen.
👀 Lett å overse
Selv om Ztoperator-AuthPolicy
skal gjøre det lettere å sikre appllikasjonen sin med OAuth 2.0 og OIDC, så er det fortsatt enkelte ting man lett kan overse.
Audience-begrensing i ID-porten og Maksinporten
Både ID-porten og Maksinporten benytter seg av audience-begrensing, en mekanisme basert på RFC 8707 Resource Indicators for Oauth2, som motvirker at tokens kan misbrukes mot andre APIer enn de som er tiltenkt. En konsekvens av dette er at det er klienten sitt ansvar å be om et audience-begrenset token. Dette er støttet i Ztoperator-AuthPolicy
med feltet spec.acceptedResources
. Dette feltet spesifiserer hvilke APIer tokenet skal kunne brukes mot.
Client authentication
Per nå støtter Ztoperator kun å autentisere klienter ved bruk av client_secret_post
. Selve klient-autentiseringen gjøres av Ztoperator, men det er viktig at det er konfigurert i klientregistreringen mot identitetstilbyderen at man bruker client_secret_post
.
Klientregistrering med Digdirator antar at klienten kun benytter seg av private_key_jwt
som klient-autentiseringsmetode, noe som gjør at klientintegrasjoner satt opp med Digdirator per nå ikke er kompatibelt med Ztoperator-AuthPolicy
.