【Kubernetes】サイドカー方式でOAtuh2-proxyの認証処理を追加する
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