如何通过OAuth2与Keycloak实现Istio授权并解决登录重定向问题
问题背景
基于《API Authentication: Configure Istio IngressGateway, OAuth2-Proxy and Keycloak》及Istio官方授权策略文档,尝试通过OAuth2与Keycloak实现Istio入口网关的外部认证。
预期效果
用户访问<ingress host>/app时,自动重定向至Keycloak登录页面,完成认证后可访问后端服务。
当前异常
访问<ingress host>/app时页面加载10秒后返回403;若在10秒内执行kubectl delete -f authorization-policy.yaml删除授权策略,可正常触发Keycloak登录重定向。
相关配置文件
oauth2.yaml
apiVersion: v1 kind: Service metadata: labels: app: oauth-proxy name: oauth-proxy spec: type: NodePort selector: app: oauth-proxy ports: - name: http-oauthproxy port: 4180 nodePort: 31023 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: oauth-proxy name: oauth-proxy spec: replicas: 1 selector: matchLabels: app: "oauth-proxy" template: metadata: labels: app: oauth-proxy spec: containers: - name: oauth-proxy image: "quay.io/oauth2-proxy/oauth2-proxy:v7.2.0" ports: - containerPort: 4180 args: - --http-address=0.0.0.0:4180 - --upstream=http://test-web-app:3000 - --set-xauthrequest=true - --pass-host-header=true - --pass-access-token=true env: # OIDC Config - name: "OAUTH2_PROXY_PROVIDER" value: "keycloak-oidc" - name: "OAUTH2_PROXY_OIDC_ISSUER_URL" value: "http://192.168.1.2:31020/realms/my_login_realm" - name: "OAUTH2_PROXY_CLIENT_ID" value: "my_nodejs_client" - name: "OAUTH2_PROXY_CLIENT_SECRET" value: "JGEQtkrdIc6kRSkrs89BydnfsEv3VoWO" # Cookie Config - name: "OAUTH2_PROXY_COOKIE_SECURE" value: "false" - name: "OAUTH2_PROXY_COOKIE_SECRET" value: "ZzBkN000Wm0pQkVkKUhzMk5YPntQRUw_ME1oMTZZTy0=" - name: "OAUTH2_PROXY_COOKIE_DOMAINS" value: "*" # Proxy config - name: "OAUTH2_PROXY_EMAIL_DOMAINS" value: "*" - name: "OAUTH2_PROXY_WHITELIST_DOMAINS" value: "*" - name: "OAUTH2_PROXY_HTTP_ADDRESS" value: "0.0.0.0:4180" - name: "OAUTH2_PROXY_SET_XAUTHREQUEST" value: "true" - name: OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER value: "true" - name: OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY value: "true" - name: OAUTH2_PROXY_SKIP_PROVIDER_BUTTON value: "true" - name: OAUTH2_PROXY_SET_AUTHORIZATION_HEADER value: "true"
keycloak.yaml
apiVersion: v1 kind: Service metadata: name: keycloak spec: type: NodePort selector: app: keycloak ports: - name: http-keycloak port: 8080 nodePort: 31020 --- apiVersion: apps/v1 kind: Deployment metadata: name: keycloak spec: selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak spec: containers: - name: keycloak image: quay.io/keycloak/keycloak:17.0.0 ports: - containerPort: 8080 args: ["start-dev"] env: - name: KEYCLOAK_ADMIN value: "admin" - name: KEYCLOAK_ADMIN_PASSWORD value: "admin"
istio-operator.yaml
apiVersion: install.istio.io/v1alpha1 kind: IstioOperator spec: meshConfig: accessLogFile: /dev/stdout extensionProviders: - name: "oauth2-proxy" envoyExtAuthzHttp: service: "oauth-proxy.default.svc.cluster.local" port: "4180" # The default port used by oauth2-proxy. includeHeadersInCheck: ["authorization", "cookie","x-forwarded-access-token","x-forwarded-user","x-forwarded-email","x-forwarded-proto","proxy-authorization","user-agent","x-forwarded-host","from","x-forwarded-for","accept","x-auth-request-redirect"] # headers sent to the oauth2-proxy in the check request. headersToUpstreamOnAllow: ["authorization", "path", "x-auth-request-user", "x-auth-request-email", "x-auth-request-access-token","x-forwarded-access-token"] # headers sent to backend application when request is allowed. headersToDownstreamOnDeny: ["content-type", "set-cookie"] # headers sent back to the client when request is denied.
gateway.yaml
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: test-gateway namespace : istio-system spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - '*'
virtual-service.yaml
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: gateway-vs spec: hosts: - '*' gateways: - istio-system/test-gateway http: - match: - uri: prefix: /oauth2 route: - destination: host: oauth-proxy.default.svc.cluster.local port: number: 4180 - match: - uri: prefix: /app route: - destination: host: test-web-app.default.svc.cluster.local port: number: 3000
authorization-policy.yaml
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: example-auth-policy spec: action: CUSTOM provider: name: "oauth2-proxy" rules: - to: - operation: paths: ["/app"] notPaths: ["/oauth2/*"]
问题分析与修复方案
核心原因
- ExtAuthz模式适配错误:Istio的CUSTOM授权策略会向oauth2-proxy发送同步Check请求,但当前oauth2-proxy启用
--set-xauthrequest=true时,期望处理反向代理流量而非标准ExtAuthz接口,导致请求超时后返回403。 - 重定向头未传递:Istio的ExtensionProvider配置中未包含
location头,oauth2-proxy返回的302重定向响应无法正确转发给客户端。 - 授权策略规则范围错误:
paths: ["/app"]仅匹配根路径,未覆盖子路径;notPaths规则逻辑无效,因为/oauth2流量由VirtualService直接路由,不会经过该授权策略。
修复步骤
1. 调整OAuth2-Proxy配置,支持ExtAuthz重定向
修改oauth2-proxy的Deployment参数,添加--auth-request-redirect=true以适配Istio的ExtAuthz模式:
args: - --http-address=0.0.0.0:4180 - --upstream=http://test-web-app:3000 - --set-xauthrequest=true - --auth-request-redirect=true # 启用重定向响应支持 - --pass-host-header=true - --pass-access-token=true
2. 更新Istio ExtensionProvider配置
在istio-operator.yaml的headersToDownstreamOnDeny中添加location头,确保重定向地址能传递给客户端:
headersToDownstreamOnDeny: ["content-type", "set-cookie", "location"]
3. 修正授权策略规则
调整路径匹配范围,确保覆盖/app下所有子路径:
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: example-auth-policy spec: action: CUSTOM provider: name: "oauth2-proxy" rules: - to: - operation: paths: ["/app/*"] # 匹配/app下所有子路径
4. 应用配置并验证
- 重新应用所有修改后的配置:
kubectl apply -f oauth2.yaml kubectl apply -f istio-operator.yaml kubectl apply -f authorization-policy.yaml - 重启oauth2-proxy pod使配置生效:
kubectl rollout restart deployment oauth-proxy
修复后流程说明
用户访问/app时,Istio触发ExtAuthz请求到oauth2-proxy;oauth2-proxy检测到未认证,返回带Location头的302响应,Istio将该响应转发给客户端,引导用户跳转到Keycloak登录页。登录完成后,Keycloak回调到/oauth2/callback,oauth2-proxy验证凭证并设置会话Cookie,再重定向回/app;此时Istio再次触发ExtAuthz请求,oauth2-proxy验证Cookie有效后返回允许访问的响应,Istio将请求转发到后端服务。
内容的提问来源于stack exchange,提问作者Krishnan KK

