kubernetes上でのOAuth2 proxyコンテナの利用方法をメモ。
nginxの使い方について詳しく知りたい方は、この本がお勧めです。
手元にあれば、実装時にいちいち検索しなくて済むかもしれません。
リンク
OAuth2 proxy
ドキュメントはこちら。
構成
リバースプロキシにnginxを使用する方法です。
nginxのauth_requestでOAuth2 proxyに認証を依頼し、認証成功時にアプリケーションにプロキシされます。
認証方法はOIDC。
config
各configはkubernetesのconfigMapに登録します。
nginx.conf
user nginx;
worker_processes 1;
pid /var/run/nginx.pid
daemon off; # dockerで起動する場合、フォアグラウンドで起動する必要がある
events {
worker_connections 2048;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /dev/stdout main;
error_log /dev/stderr error;
sendfile on;
# tcp_nopush on;
# gzip on;
keepalive_timeout 65;
# oauth2 proxyの為に、HTTPヘッダー用のバッファ領域を増やす
proxy_buffers 8 32k;
proxy_buffer_size 32k;
# "Request Header Or Cookie Too Large"の対応
large_client_header_buffers 8 32k;
client_header_buffer_size 32k;
server_token off;
upstream backend {
server 127.0.0.1:8080; # アプリケーションコンテナ
}
upstream oauth-proxy {
server 127.0.0.1:4180; # OAuth2 proxyコンテナ
}
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 403 ssl;
ssl_certificate /etc/nginx/ssl/tls.crt; # 中間証明書がある場合は連結する
ssl_certificate_key /etc/nginx/ssl/tls.key;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:EC
DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_session_cache builtin:1000 shared:SSL:10m;
client_max_body_size 32m;
client_body_buffer_size 64k;
location /oauth2/ {
proxy_pass http://oauth-proxy;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Sheme $sheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
# 他ドメインに転送する場合
# proxy_set_header X-Auth-Request-Redirect $sheme://$host$request_uri;
}
# 認証用エンドポイント、「=」なことに注意
location = /oauth2/auth {
proxy_pass http://oauth-proxy;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Sheme $sheme;
# nginxのauth_requestにbodyは不要
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
satisfy any;
auth_request /oauth2/auth;
error_page 401 /oauth2/sign_in;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
auth_request_set X-User $user;
auth_request_set X-Email $email;
auth_request_set X-ID $empid;
auth_request_set X-Roles $roles;
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
proxy_pass http://backend/;
}
location /proxy/ {
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://oauth-proxy;
}
}
}
oauth2_proxy.cfg
http_address = "0.0.0.0:4180"
upstream = ["http://127.0.0.1:8080/"]
# メールアドレスのドメインを指定、アスタリスクで全て許容
email_domain = ["*"]
# 認可するロール
permission_policies = []
oidc_issuer_url = "https://xxx.xxx.xxx/dex"
cookie_secure = true
# 認証方法を指定
provider = "oidc"
# X-Forwarded-Access-Token
pass_access_token = true
# Authorization Bearer header
pass_authorization_header = true
reverse_proxy = true
set_xauthrequest = true
set_authorization_header = true
skip_provider_button = true
# ヘルスチェック用エンドポイントもこちらに記載する
skip_auth_regex = [
"^(/|/forbidden|/health|/favicon.ico)$",
"^(/|/forbidden|/health|/favicon.ico)$",
"^(js|image|css|stylesheets)/.*$",
".(png|css|ico|js)$"
]
configMapとSecret
上記configはconfigMapに、tlsの鍵データとoauth2proxy用各パラメータはSecretに登録します。
cookie secretをpythonで生成する際に、byte数が(16, 24, 32)のいずれかである必要があります。
$ NAMESPACE=xxxxx
$ alias k=kubectl
# tls作成
$ k -n=${NAMESPACE} create secret tls pki-tls --key=secret/server.key --cert=secret/server.crt
secret/pki-tls created
# OAuth2 proxy 各パラメータをbase64エンコードして、マニフェストに記載する
$ echo -n "<client-id>" | base64
$ echo -n "<client-secret>" | base64
# cookie secretの生成、byte型になっていたり改行が含まれているとエラーとなるので注意
$ python -c 'import os,base64; print(base64.urlsafe_b64encode(os.random(16)).decode(), end="")' | base64
$ cat secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: oauth2proxy
namespace: xxx-xxx-xxx
type: Opaque
data:
client-id: <base64 client-id>
client-secret: <base64 client-secret>
cookie-secret: <base64 cookie-secret>
$ k apply -f secret.yaml
secret/oauth2proxy created
# configMap
$ k create cm nginx-config --from-file=config/nginx.conf
$ k create cm proxy-config --from-file=config/oauth2_proxy.cfg
# 確認コマンド
$ k -n=${NAMESPACE} describe secret kpi-tls
$ k -n=${NAMESPACE} describe secret oauth2proxy
# 要jqコマンドのインストール
$ k -n=${NAMESPACE} get secret kpi-tls -o json | jq -r '.data."tls.crt"'
$ k -n=${NAMESPACE} get cm nginx-config -o json | jq -r '.data."nginx.conf"'
$ k -n=${NAMESPACE} get cm proxy-config -o json | jq -r '.data."oauth2_proxy.cfg"'
# 24byteであることの確認
$ k -n=${NAMESPACE} get secret oauth2proxy -o json | jq -r '.data."cookie-secret"' | base64 -d | wc -c
24
マニフェストファイル
特に変わった使い方はしていません。
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: test-app namespace: xxx-xxx-xxx spec: replicas: 2 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 minReadySeconds: 10 template: spec: volumes: - name: secret-volume secret: secretName: pki-tls - name: nginx-volume configMap: name: nginx-config - name: oidc-volume configMap: name: proxy-config containers: - image: nginx name: nginx ports: - containerPort: 443 resources: limits: memory: 64Mi requests: cpu: 100m memory: 64Mi volumeMounts: - mountPath: /etc/nginx/ssl name: secret-volume readOnly: true - mountPath: /etc/nginx/nginx.conf subPath: nginx.conf name: nginx-volume readOnly: true - image: oauth2_proxy # PJで管理しているイメージです、公開されているものと同じはず args: ["-config=/etc/oauth2/oauth2_proxy.cfg"] ports: - containerPort: 4180 resources: limits: memory: 512Mi requests: cpu: 100m memory: 512Mi volumeMounts: - mountPath: /etc/oauth2/oauth2_proxy.cfg subPath: oauth2_proxy.cfg name: oidc-volume readOnly: true env: - name: OAUTH2_PROXY_CLIENT_ID valueFrom: secretKeyRef: name: oauth2proxy key: clinet-id - name: OAUTH2_PROXY_CLIENT_SECRET valueFrom: secretKeyRef: name: oauth2proxy key: cookie-secret - name: OAUTH2_PROXY_COOKIE_SECRET valueFrom: secretKeyRef: name: oauth2proxy key: clinet-id - name: OAUTH2_PROXY_HTTP_ADDRESS # ここで指定しないと、127.0.0.1で起動してしまう? value: "0.0.0.0:4180" - name: OAUTH2_PROXY_REDIRECT_URL value: <登録したリダイレクトURLを指定> - image: python-app # 詳細は割愛、実装したのはflaskアプリです name: python-app ports: - containerPort: 8080 resources: limits: cpu: 1000m memory: 2G lifecyle: preStop: exec: command: ["/bin/sh", "-c", "sleep 20"] readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 5 failureThreshold: 10 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 60 periodSeconds: 10 timeoutSeconds: 10
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: test-ingress namespace: test-namespace annotations: ingress.zlab.co.jp/backend-config: '{"xxx-xxx-xxx": {"443": {"tls": true, "sni": "<ドメイン名を記載>"}}}' spec: tls: - secretName: pki-tls rules: - host: <ドメイン名を記載> http: paths: - backend: serviceName: testsvc servicePort: 443
service.yaml
apiVersion: v1 kind: Service metadata: name: testsvc labels: app: test-app namespace: xxx-xxx-xxx spec: selector: app: test-app ports: - protocol: TCP port: 443 targetPort: 443