介绍

  • 是一款对k8s应用做身份验证的反向代理。工作模式有两种:

image-20241121102327853

在我们的示例中,使用的是右边的middleware模式,作用于ingress-nginx。

下载

helm repo add --force-update oauth2-proxy https://oauth2-proxy.github.io/manifests
helm repo update oauth2-proxy
helm pull oauth2-proxy/oauth2-proxy --version 7.7.1

配置oauth2proxy

创建redis password

oauth的sessionStorage选择使用redis,oauth helm chart会安装redis subchart。这里需要一个redis密码,保存到secret里。

export REDIS_PASSWD=$( openssl rand -base64 32 | head -c 32 | base64 )
kubectl -n oauth2-proxy create secret generic oauth2-proxy-redis --from-literal=redis-password=$REDIS_PASSWD
# 需要打上标签,后续安装helm的时候会被纳入到helm管理
kubectl label secrets oauth2-proxy-redis -n oauth2-proxy "app.kubernetes.io/managed-by"="Helm"
kubectl annotate secrets oauth2-proxy-redis -n oauth2-proxy "meta.helm.sh/release-name"="oauth2-proxy"
kubectl annotate secrets oauth2-proxy-redis -n oauth2-proxy "meta.helm.sh/release-namespace"="oauth2-proxy"

sessionStorage负责认证状态维护

sessionStorage:
  type: redis     # 使用 Redis 存储用户会话状态
redis:
  enabled: true   # 启用内置 Redis

这样的架构使得:

  • Nginx 负责流量路由和认证检查
  • OAuth2 Proxy 负责具体的 OAuth 认证流程
  • Redis 负责会话状态存储
  • 所有受保护的服务都能自动获得统一的身份认证

集成github认证

  1. 先去github生成一个OauthAPP:(Developer applications),复制client idclient secret

  2. application name:oauth2proxy-local

  3. Homepage URL:https://oauth2proxy.hanxux.local/

  4. Authorization callback URL:https://oauth2proxy.hanxux.local/oauth2/callback

  5. 创建一个cookie secret

export COOKIE_SECRET=$( openssl rand -base64 32 | head -c 32 | base64 )
  1. oauth2proxy的values.yaml里面添加github认证相关参数
config:
  existingSecret: oauth2-proxy-creds
extraArgs:
  provider: github
  whitelist-domain: .hanxux.local
  cookie-domain: .hanxux.local
  1. 传入gitub oauth app的client id和secret

方法1-创建secret保存

  • 创建k8s secret
export CLIENT_ID="Ov23liF0QvSRG51yPKq1"
export CLIENT_SECRET="xxx"
kubectl create namespace oauth2-proxy
kubectl -n oauth2-proxy create secret generic oauth2-proxy-creds --from-literal=cookie-secret=$COOKIE_SECRET --from-literal=client-id=$CLIENT_ID --from-literal=client-secret=$CLIENT_SECRET

方法2-直接写入values

  • 注:上面创建secret的方式oauth2proxy没有读取到。遂采取直接把cookie_secret、client_id和client_secret三个参数直接写到values.yaml里面的方式
config:
  # Add config annotations
  annotations: {}
  # OAuth client ID
  clientID: "Ov23liF0QvSRG51yPKq1"
  # OAuth client secret
  clientSecret: "xxx"
  # Create a new secret with the following command
  # openssl rand -base64 32 | head -c 32 | base64
  # Use an existing secret for OAuth2 credentials (see secret.yaml for required fields)
  # Example:
  #existingSecret: oauth2-proxy-creds
  cookieSecret: "xxx"
  # The name of the cookie that oauth2-proxy will create
  # If left empty, it will default to the release name
  cookieName: ""

方法3-github secrets

  • 上述方式不安全,会直接暴露secret。后续修改了secret加载方式,先存到github repository secrets里面,再从workflow中读取
export helmChartVersion=${{env.oauth2proxyVersion}}
export helmRepoName='oauth2-proxy'
export helmChartName='oauth2-proxy'
export REDIS_PASSWORD=$(kubectl get secret --namespace "oauth2-proxy" oauth2-proxy-redis --kubeconfig $KUBECONFIG -o jsonpath="{.data.redis-password}" | base64 -d)

helm upgrade -i oauth2-proxy -n oauth2-proxy \
oci://${{ env.harborURL }}/${{ env.harborProjectName }}/$helmRepoName/$helmChartName \
--version $helmChartVersion \
--history-max 5 \
-f ./base/external/oauth2-proxy/values.yaml \
--set config.clientID='${{ secrets.OAUTH2PROXY_CLIENT_ID}}' \
--set config.clientSecret='${{ secrets.OAUTH2PROXY_CLIENT_SECRET}}' \
--set config.cookieSecret='${{ secrets.OAUTH2PROXY_COOKIE_SECRET}}' \
--set global.redis.password=$REDIS_PASSWORD \
--insecure-skip-tls-verify \
--kubeconfig $KUBECONFIG

配置Https

  • 首先部署出certmanager --> 创建clusterissuer --> 创建给oauth2proxy ingress https的secret --> helm values.yaml的ingress.tls部分配置secret、host

  • 创建oauth2proxy的tls secret

tee certificate-oauth2proxy.yaml <<'EOF'
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: cert-oauth2proxy
  namespace: oauth2-proxy
spec:
  secretName: oauth2proxy-tls-cert-secret
  privateKey:
    rotationPolicy: Always
  commonName: oauth2proxy.hanxux.local
  dnsNames:
    - oauth2proxy.hanxux.local
  usages:
    - digital signature
    - key encipherment
    - server auth
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
EOF
  • oauth2proxy的values配置ingress tls
ingress:
  enabled: true
  ingressClassName: nginx-default
  hosts:
  - oauth2proxy.hanxux.local
  tls:
  - secretName: oauth2proxy-tls-cert-secret
    hosts:
    - oauth2proxy.hanxux.local
  path: /
  # Only used if API capabilities (networking.k8s.io/v1) allow it
  pathType: ImplementationSpecific

安装

helm upgrade -i oauth2-proxy -n oauth2-proxy --create-namespace . -f values.yaml
  • 验证安装:oauth2proxy.hanxux.local也要加到本机hosts文件中,https访问hostname即可看到oauthproxy的主页,有用github登录的提示。由于lab用的是自签证书,所以浏览器会报连接不安全。

回调URL

回调 URL 是 OAuth2 授权流程中,授权服务器(GitHub)在用户完成授权后,将用户重定向回去的目标地址

使用回调URL的优点:

  1. 安全验证

  2. GitHub 只会将授权码发送到预先注册的回调 URL

  3. 防止恶意应用窃取授权码

  4. 完成授权流程

  5. OAuth2 Proxy 通过这个 URL 接收 GitHub 返回的授权码

  6. 没有这个回调,授权流程无法完成

  7. 状态管理

  8. 回调中包含 state 参数,用于防止 CSRF 攻击

  9. OAuth2 Proxy 验证 state 确保请求的合法性

实际示例

使用的是 ingress-nginx 的外部认证功能来集成 OAuth2 Proxy。以访问jenkins

sequenceDiagram
    participant User as 用户浏览器
    participant IngressNginx as Ingress-Nginx Controller
    participant OAuth2 as OAuth2 Proxy
    participant GitHub as GitHub OAuth
    participant Jenkins as Jenkins Pod

    Note over User,Jenkins: 1. 初始访问
    User->>IngressNginx: GET https://jenkins.hanxux.local

    Note over IngressNginx,OAuth2: 2. Nginx 外部认证检查
    IngressNginx->>OAuth2: auth_request to https://oauth2proxy.hanxux.local/oauth2/auth
    OAuth2->>IngressNginx: 401 Unauthorized (no valid session)

    Note over IngressNginx,User: 3. 重定向到认证
    IngressNginx->>User: 302 Redirect to https://oauth2proxy.hanxux.local/oauth2/start?rd=https://jenkins.hanxux.local

    Note over User,GitHub: 4. OAuth2 授权流程开始
    User->>OAuth2: GET /oauth2/start?rd=...
    OAuth2->>User: 302 Redirect to GitHub OAuth

    User->>GitHub: GET /login/oauth/authorize?client_id=...&redirect_uri=https://oauth2proxy.hanxux.local/oauth2/callback

    Note over User,GitHub: 5. 用户在 GitHub 授权
    GitHub->>User: 显示授权页面
    User->>GitHub: 用户点击"授权"

    Note over GitHub,OAuth2: 6. GitHub 回调
    GitHub->>User: 302 Redirect to https://oauth2proxy.hanxux.local/oauth2/callback?code=...&state=...
    User->>OAuth2: GET /oauth2/callback?code=...

    Note over OAuth2,GitHub: 7. 交换访问令牌
    OAuth2->>GitHub: POST /login/oauth/access_token (exchange code for token)
    GitHub->>OAuth2: 返回 access_token

    OAuth2->>GitHub: GET /user (获取用户信息)
    GitHub->>OAuth2: 返回用户信息

    Note over OAuth2,User: 8. 设置认证会话
    OAuth2->>User: 设置认证 cookie + 302 Redirect to https://jenkins.hanxux.local

    Note over User,Jenkins: 9. 重新访问原始资源
    User->>IngressNginx: GET https://jenkins.hanxux.local (with auth cookie)

    Note over IngressNginx,OAuth2: 10. 再次进行外部认证
    IngressNginx->>OAuth2: auth_request to https://oauth2proxy.hanxux.local/oauth2/auth (with cookie)
    OAuth2->>IngressNginx: 200 OK + X-Auth-Request-User headers

    Note over IngressNginx,Jenkins: 11. 转发到后端服务
    IngressNginx->>Jenkins: GET / (with user headers)
    Jenkins->>IngressNginx: 返回 Jenkins 页面
    IngressNginx->>User: 返回 Jenkins 页面

OAuth2 Proxy 的作用:

从流程可以看出,OAuth2 Proxy 在这里扮演两个关键角色:

  1. 认证端点 (/oauth2/auth):
  2. Nginx 每次请求都会调用这个端点检查用户是否已认证
  3. 返回 200 表示已认证,401 表示未认证

  4. 认证流程处理器:

  5. /oauth2/start: 开始 OAuth 流程
  6. /oauth2/callback: 处理 GitHub 回调

使用oauth2proxy保护其他app

  • 在其他应用的ingress中添加annotations,详细说明参考ingress官网:OAuth External Auth
  • 这两个annotations会将请求redirect到oauth2proxy,由oauth2proxy将请求转发到配置的3rd party认证endpoint (github)
annotations:
  nginx.ingress.kubernetes.io/auth-url: "http://oauth2-proxy.oauth2-proxy.svc.cluster.local/oauth2/auth"
  nginx.ingress.kubernetes.io/auth-signin: "https://oauth2proxy.hanxux.local/oauth2/start?rd=https%3A%2F%2F<host>.hanxux.local"