Written by Matthias Osswald - October 2021
Since it took me some time to combine the correct resources to protect a microservice in an Istio mesh using an OAuth2-Proxy I would like to share the summarized parts of the setup here.
If a user wants to access the microservice (e.g. nginx) he/she/* will hit the OAuth2-Proxy first. For logged in users the proxy will display the service. For not yet logged in users the proxy will initiate the login flow (e.g. OIDC flow using Keycloak as an IdP). This mean the user will be redirected to the IdP, logs in, is redirected back, the retrieved data is verified (has not been tampered with and comes from the correct IdP) and then the user gets a session and is therefore able to see the service now.
App-User
¯\_(ツ)_/¯ -----> OAuth2-Proxy ---valid jwt--> Microservice (e.g. Nginx)
|
login user
|
IdP (e.g. Keycloak)
From the Istio docs I was able to get the mesh config adaption. You can edit the cm using:
kubectl edit configmap istio -n istio-system
The config look like below, where "oauth2-proxy" is the name of the Istio extension provider, "oauth2-proxy.demo.svc.cluster.local" is your OAuth2-Proxy service running in the "demo" namespace.
extensionProviders:
- name: "oauth2-proxy"
envoyExtAuthzHttp:
service: "oauth2-proxy.demo.svc.cluster.local"
port: "4180"
includeHeadersInCheck:
- "authorization"
- "cookie"
headersToUpstreamOnAllow:
- "authorization"
- "path"
- "x-auth-request-user"
- "x-auth-request-email"
- "x-auth-request-access-token"
headersToDownstreamOnDeny:
- "content-type"
- "set-cookie"
Secondly a AuthorizationPolicy is required to protect your endpoint which is to be protected ("nginx.mydomain.com" in this case). The AuthorizationPolicy rules also support path matching (paths: ["/data"]) and method matching (methods: ["POST"]) or combinations of these. In this case the app to be protected is a nginx instance named "nginx" and running in the "demo" namespace. The Istio VirtualService exposing it exposes the route "nginx.mydomain.com".
When the rules match it will use a "CUSTOM"-action which is the "oauth2-proxy" extension provider which was defined in the mesh.
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: ext-authz
namespace: demo
spec:
selector:
matchLabels:
app: nginx
action: CUSTOM
provider:
name: oauth2-proxy
rules:
- to:
- operation:
hosts: ["nginx.mydomain.com"]
The last missing part is an OAuth2-Proxy instance serving as the extension provider. In this example the OAuth2-Proxy will connect to an OIDC identity provider.
In the example the IdP is a Keycloak instance also running in the mesh exposed on the route "sso.mydomain.com" with a "test-realm" and a "test-client" configured.
apiVersion: v1
kind: Secret
metadata:
name: oauth2-proxy
namespace: demo
stringData:
OAUTH2_PROXY_CLIENT_ID: test-client
OAUTH2_PROXY_CLIENT_SECRET: 42a70abe-e004-5555-8221-3d6f37c89cb2
OAUTH2_PROXY_COOKIE_SECRET: b81b2d5f6bffe355a17bf1605f7bd088
---
apiVersion: v1
kind: Service
metadata:
name: oauth2-proxy
namespace: demo
spec:
selector:
app: oauth2-proxy
ports:
- name: http
port: 4180
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: oauth2-proxy
namespace: demo
spec:
selector:
matchLabels:
app: oauth2-proxy
template:
metadata:
labels:
app: oauth2-proxy
spec:
containers:
- name: oauth2-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:latest
args:
- --provider=oidc
- --cookie-secure=true
- --cookie-samesite=lax
- --cookie-refresh=1h
- --cookie-expire=4h
- --cookie-name=_oauth2_proxy
- --set-authorization-header=true
- --email-domain=*
- --http-address=0.0.0.0:4180
- --upstream=static://200
- --skip-provider-button=true
- --whitelist-domain=.mydomain.com
- --oidc-issuer-url=https://sso.mydomain.com/auth/realms/test-realm
env:
- name: OAUTH2_PROXY_CLIENT_ID
valueFrom:
secretKeyRef:
name: oauth2-proxy
key: OAUTH2_PROXY_CLIENT_ID
- name: OAUTH2_PROXY_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oauth2-proxy
key: OAUTH2_PROXY_CLIENT_SECRET
- name: OAUTH2_PROXY_COOKIE_SECRET
valueFrom:
secretKeyRef:
name: oauth2-proxy
key: OAUTH2_PROXY_COOKIE_SECRET
resources:
requests:
cpu: 10m
memory: 100Mi
ports:
- containerPort: 4180
protocol: TCP
readinessProbe:
periodSeconds: 3
httpGet:
path: /ping
port: 4180
The OAuth2-Proxy docs shows all the configuration options for the "args" section. There are many options to be considered (e.g. Redis cache is recommended for production instead).