Basically, this will allow inbound OIDC authentication and authorization for a service using a google issued id_token
First install minikube (i'm using kvm2 but you can use anything)
minikube start --driver=kvm2 --cpus=4 --kubernetes-version=v1.28 --host-only-cidr 192.168.39.1/24
## enable an external LB
minikube addons enable metallb
### get its ip
$ minikube ip
192.168.39.59
### configure the LB, when prompted, pick a range cidr, i entered in the following
minikube addons configure metallb
# -- Enter Load Balancer Start IP: 192.168.39.104
# -- Enter Load Balancer End IP: 192.168.39.110
## optionally start the dashboard
# minikube dashboard
### download and install istio
kubectl create ns istio-system
export ISTIO_VERSION=1.24.0
export ISTIO_VERSION_MINOR=1.24
wget -P /tmp/ https://github.com/istio/istio/releases/download/$ISTIO_VERSION/istio-$ISTIO_VERSION-linux-amd64.tar.gz
tar xvf /tmp/istio-$ISTIO_VERSION-linux-amd64.tar.gz -C /tmp/
rm /tmp/istio-$ISTIO_VERSION-linux-amd64.tar.gz
export PATH=/tmp/istio-$ISTIO_VERSION/bin:$PATH
istioctl install --set profile=demo \
--set meshConfig.enableAutoMtls=true \
--set values.gateways.istio-ingressgateway.runAsRoot=true \
--set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY \
--set meshConfig.defaultConfig.gatewayTopology.forwardClientCertDetails=SANITIZE_SET
kubectl label namespace default istio-injection=enabled
kubectl get svc istio-ingressgateway -n istio-system
export GATEWAY_IP=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $GATEWAY_IP
In this demo, i used a GCP service account json file for the oidc token
gcloud auth activate-service-account --key-file=/path/to/svc_account.json
TOKEN=`gcloud auth print-identity-token --audiences=https://svc1.example.com`
if you decoded the token i got, the claims showed:
{
"aud": "https://svc1.example.com",
"azp": "[email protected]",
"email": "[email protected]",
"email_verified": true,
"exp": 1741234050,
"iat": 1741230450,
"iss": "https://accounts.google.com",
"sub": "100890260483227123173"
}
Which means we need to authoize the iss
, aud
and sub
(preferably) or email
fields
In my case, see the values in the auth-policy.yaml
file below.
For your svc account, edit it appropriately and then apply:
kubectl apply -f istio-ingress-gateway.yaml
kubectl apply -f istio-fe-svc1.yaml -f auth-policy.yaml -f auth-deployment.yaml
Specifically note auth-policy.yaml
. What that enforces is any google issued id_token through the gateway but the backend service will only accept a single source serve.
The source service enforcement can be done either by inspecting to
or from
rules:
rules:
- to:
- operation:
methods: ["GET"]
when:
- key: request.auth.claims[iss]
values: ["https://accounts.google.com"]
- key: request.auth.claims[aud]
values: ["https://svc1.example.com"]
- key: request.auth.claims[email]
values: ["[email protected]"]
or
rules:
- from:
- source:
requestPrincipals: ["https://accounts.google.com/100890260483227123173"]
Once the services are running, call:
curl -s --resolve istio.domain.com:80:$GATEWAY_IP \
-H "Host: svc1.example.com" -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n" http://istio.domain.com/get
### you should see a 200 ok response back
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "svc1.example.com",
"User-Agent": "curl/8.12.1-DEV",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Internal": "true",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/svc1-sa;Hash=f2998dfeeb4690a95ae10fd1145848f26fec61b8fc531d0a49b89f2bd6f4a107;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"
},
"origin": "10.244.0.1",
"url": "http://svc1.example.com/get"
}
200
To verify the authorization steps, change the value of request.auth.claims[email]
or the value of requestPrincipals
and reapply the config
- `istio-ingress-gateway.yaml
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
istio-fe-svc1.yaml
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: svc1-virtualservice
spec:
hosts:
- "svc1.example.com"
gateways:
- my-gateway
http:
- route:
- destination:
host: svc1
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: svc1-destination
spec:
host: svc1
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
loadBalancer:
simple: ROUND_ROBIN
auth-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: svc1
labels:
app: svc1
spec:
ports:
- port: 80
name: http
selector:
app: svc1
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: svc1-sa
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc1
spec:
selector:
matchLabels:
app: svc1
replicas: 1
template:
metadata:
labels:
app: svc1
spec:
serviceAccountName: svc1-sa
containers:
- name: myapp-container
image: kennethreitz/httpbin
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
auth-policy.yaml
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: deny-all-authz-ns
spec:
{}
---
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: igaupolicy
namespace: istio-system
spec:
selector:
matchLabels:
app: istio-ingressgateway
jwtRules:
- issuer: "https://accounts.google.com"
audiences:
- "https://svc1.example.com"
jwksUri: "https://www.googleapis.com/oauth2/v3/certs"
forwardOriginalToken: true
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: igazpolicy
namespace: istio-system
spec:
selector:
matchLabels:
app: istio-ingressgateway
rules:
- to:
- operation:
methods: ["GET"]
when:
- key: request.auth.claims[iss]
values: ["https://accounts.google.com"]
---
apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: svc1-au
spec:
selector:
matchLabels:
app: svc1
jwtRules:
- issuer: "https://accounts.google.com"
audiences:
- "https://svc1.example.com"
jwksUri: "https://www.googleapis.com/oauth2/v3/certs"
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: svc1-az
spec:
action: ALLOW
selector:
matchLabels:
app: svc1
rules:
# - to:
# - operation:
# methods: ["GET"]
# when:
# - key: request.auth.claims[iss]
# values: ["https://accounts.google.com"]
# - key: request.auth.claims[aud]
# values: ["https://svc1.example.com"]
# - key: request.auth.claims[email]
# values: ["[email protected]"]
- from:
- source:
requestPrincipals: ["https://accounts.google.com/100890260483227123173"]
---