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).