在 k8s 中授权外部应用程序的端点
在代码级别实现授权通常很简单。
类似地,公开具有内置身份验证支持的外部工具(例如 Grafana)也可以相对容易地处理。
真正的挑战出现在处理缺乏对身份验证或授权的本机支持的简单工具或服务时,需要外部机制来确保安全访问。
解决此问题的一种方法是限制通过 VPN 的访问或使用 IP 地址范围保护它,这仍然是最可靠的方法之一。
然而,我最近探索了一种替代方案:重用 Keycloak(我们已经在使用的 oauth 提供程序之一)来验证对特定端点的访问。
可能适用于此的工具是**OAuth2 Proxy**,它支持广泛的 OAuth 提供商,例如 Facebook、Google、GitLab 和 Azure。
额外的要求是将这些工具分组到单个 URL 下,例如 **tools.example.com/tool-name**,以简化访问和管理。
**oauth2-proxy 基本设置:**
apiVersion: apps/v1 kind: Deployment metadata: labels: k8s-app: oauth2-proxy name: oauth2-proxy namespace: default spec: replicas: 1 selector: matchLabels: k8s-app: oauth2-proxy template: metadata: labels: k8s-app: oauth2-proxy spec: containers: - args: - --provider=keycloak-oidc - --oidc-issuer-url=https://{ISSUER_HOST}/realms/{REALM_NAME} - --code-challenge-method=S256 - --allowed-role={ROLE} - --email-domain=* - --upstream=file:///dev/null - --http-address=0.0.0.0:4180 - --client-id={CLIENT_ID} - --client-secret={CLIENT_SECRET} - --session-cookie-minimal env: - name: OAUTH2_PROXY_COOKIE_SECRET value: {COOKIE_SECRET} image: quay.io/oauth2-proxy/oauth2-proxy:latest imagePullPolicy: Always name: oauth2-proxy ports: - containerPort: 4180 protocol: TCP --- apiVersion: v1 kind: Service metadata: labels: k8s-app: oauth2-proxy name: oauth2-proxy namespace: default spec: ports: - name: http port: 4180 protocol: TCP targetPort: 4180 selector: k8s-app: oauth2-proxy
**入口配置(假设您已经有一个名为 tools-example-com-tls 的 TLS 密钥)**
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tools namespace: default annotations: nginx.ingress.kubernetes.io/proxy-body-size: 15m spec: ingressClassName: nginx tls: - hosts: - "tools.example.com secretName: tools-example-com-tls rules: - host: "tools.example.com" http: paths: - path: /oauth2 pathType: Prefix backend: service: name: oauth2-proxy port: number: 4180 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tools-external-auth-oauth2 namespace: default annotations: nginx.ingress.kubernetes.io/proxy-body-size: 15m nginx.ingress.kubernetes.io/auth-url: "https://$host/oauth2/auth" nginx.ingress.kubernetes.io/auth-signin: "https://$host/oauth2/start?rd=$escaped_request_uri" nginx.ingress.kubernetes.io/use-regex: "true" spec: ingressClassName: nginx tls: - hosts: - "tools.example.com" secretName: tools-example-com-tls rules: - host: "tools.example.com" http: paths: - path: /certificates pathType: Prefix backend: service: name: cert-checker port: number: 8081
在我们的案例中,其中一个工具是证书检查器的仪表板。
**额外奖励:**
此外,我们的解决方案包括部署在遵循 s1-app、s2-app 等模式的地址的多个有状态服务,每个相应的服务都通过 ASP.NET 公开自己的工具实例。
例如,我们可能希望在**tools.example.com/s1/quartz**和**tools.example.com/s2/quartz**等地址公开相关的 quartz-ui 实例。

**鉴于此需求,Ingress 资源 tools-external-auth-oauth2 由多个路径组成,例如:**
- path: /s[0-9]+/quartz pathType: ImplementationSpecific backend: service: name: tools-nginx-reverse-proxy port: number: 80
**nginx反向代理配置:**
apiVersion: v1 kind: ConfigMap metadata: name: tools-nginx-reverse-proxy-config namespace: default data: nginx.conf: | events { worker_connections 1024; } http { resolver 10.3.0.10 valid=10s; #KubeDNS or CoreDNS internal ip server { listen 80; location ~ ^/s([0-9]+)/quartz{ set $service_name "s$1-app.default.svc.cluster.local"; rewrite ^/s([0-9]+)/quartz$ /quartz break; proxy_pass http://$service_name:80; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_read_timeout 90; proxy_send_timeout 90; } location ~ ^/s([0-9]+)/quartz/(.*\.(css|js|png|jpg|jpeg|gif|svg|woff2|woff|ttf))$ { set $service_name "s$1-app.default.svc.cluster.local"; rewrite ^/s([0-9]+)/quartz/(.*)$ /$2 break; proxy_pass http://$service_name:80; } location / { return 404; } } } --- apiVersion: apps/v1 kind: Deployment metadata: name: tools-nginx-reverse-proxy namespace: default spec: replicas: 1 selector: matchLabels: app: tools-nginx-reverse-proxy template: metadata: labels: app: tools-nginx-reverse-proxy spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 volumeMounts: - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: tools-nginx-reverse-proxy-config --- apiVersion: v1 kind: Service metadata: name: tools-nginx-reverse-proxy namespace: default spec: selector: app: tools-nginx-reverse-proxy ports: - protocol: TCP port: 80 targetPort: 80
**笔记:**
封面图片(显然)是由 bing ai 创建的。
**有用的资源:**
https://oauth2-proxy.github.io/oauth2-proxy/
https://www.keycloak.org
https://github.com/mogensen/cert-checker
https://github.com/guryanovev/CrystalQuartz