Advent Calendar 2020 全部オレシリーズ 8日目です。もう完走は諦めました。(再掲)
Nginx Ingress Controller と oauth2-proxy を組み合わせて簡単に SSO を導入するためのメモです。複数のサービスがあって、Nginx Ingress Controller を使ってて、どれも同じ SSO 設定で良いという場合に便利です。nginx の auth_request 設定を Nginx Ingress Controller がいい感じにやってくれます。
Nginx Ingress Controller のインストール
ドキュメントを見てお好みの方法でインストールしてください。
私は helm でインストールしました。
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install my-release ingress-nginx/ingress-nginx
oauth2-proxy のインストール
Helm Chart はあるにはあるけど、もうアーカイブ状態なんですよね。でも使えます。ここでも使っています。
Keycloak で SSO する場合の例です、次の YAML を customize.yaml
として保存して helm に -f
で渡します。Keycloak のセットアップや Client ID の発行は別途行ってください。Keycloak 以外にも沢山の Provier に対応しています。便利です。
config:
clientID: example-client
clientSecret: 71d0785e-3aeb-4e9c-8790-7dc34699afc1
cookieSecret: bmljaHVjb0xvaHJpZTVnYWhyYWU0ZXY0cmFoMGRvbzZvb0RhaGNoZWVzYXJlNGFT
extraArgs:
provider: keycloak
login-url: https://keycloak.example.com/auth/realms/example/protocol/openid-connect/auth
redeem-url: https://keycloak.example.com/auth/realms/example/protocol/openid-connect/token
validate-url: https://keycloak.example.com/auth/realms/example/protocol/openid-connect/userinfo
scope: profile
ingress:
enabled: true
path: /oauth2
hosts:
- alertmanager.example.com
- kiali.example.com
- prometheus.example.com
annotations:
kubernetes.io/ingress.class: nginx
helm repo add stable https://charts.helm.sh/stable
helm repo update
helm install stable/oauth2-proxy -f customize.yaml
これで次のような Ingress が作成されています。この後出てきますが、各サービスでもそれぞれの Ingress を定義しますが、/oauth2
についてはそれとは別に定義する必要があるというのがここでの重要なポイントです。私はこれになかなか気付けずに時間がかかりました。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
meta.helm.sh/release-name: oauth2-proxy
meta.helm.sh/release-namespace: ingress-nginx
labels:
app: oauth2-proxy
app.kubernetes.io/managed-by: Helm
chart: oauth2-proxy-3.2.3
heritage: Helm
release: oauth2-proxy
name: oauth2-proxy
namespace: ingress-nginx
spec:
rules:
- host: alertmanager.example.com
http:
paths:
- backend:
serviceName: oauth2-proxy
servicePort: 80
path: /oauth2
pathType: ImplementationSpecific
- host: kiali.example.com
http:
paths:
- backend:
serviceName: oauth2-proxy
servicePort: 80
path: /oauth2
pathType: ImplementationSpecific
- host: prometheus.example.com
http:
paths:
- backend:
serviceName: oauth2-proxy
servicePort: 80
path: /oauth2
pathType: ImplementationSpecific
各サービスの Ingress 設定
oauth2-proxy の設定に alertmanager.example.com, kiali.example.com, prometheus.example.com というのを入れてみました。
どれでも同じですが、alertmanager.example.com について設定するとしましょう。おそらく kube-prometheus-stack という helm chart で入れるでしょうから直接 Ingress の manifest を書くことはないでしょうが。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
meta.helm.sh/release-name: kube-prometheus-stack
meta.helm.sh/release-namespace: prometheus
nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped\_request\_uri
nginx.ingress.kubernetes.io/auth-url: http://oauth2-proxy.ingress-nginx.svc.cluster.local/oauth2/auth
name: kube-prometheus-stack-alertmanager
namespace: prometheus
spec:
rules:
- host: alertmanager.example.com
http:
paths:
- backend:
serviceName: kube-prometheus-stack-alertmanager
servicePort: 9093
path: /
pathType: ImplementationSpecific
ここで大事なのは nginx.ingress.kubernetes.io/auth-signin
と nginx.ingress.kubernetes.io/auth-url
という annotation です。
auth_request は nginx が受けたリクエストのヘッダーを一部いじるものの、ほぼそのまま oauth2-proxy に proxy します。そして proxy 先で cookie の値やらドメイン名、URIなどからログイン済みかどうか、アクセスが許可されているのかどうかを判断して、レスポンスを返します。それを受け取った nginx が本来の upstream に proxy したり認証ページへリダイレクトしたりします。
で、nginx.ingress.kubernetes.io/auth-url
に指定するのが認証のための proxy 先です。nginx からアクセスできれば良いため Kubernetes クラスタ内通信で良いので .svc.cluster.local のドメインで指定しています。
一方、nginx.ingress.kubernetes.io/auth-signin
はログインされていない場合のリダイレクト先として使われるため $host
を使って受け付けた Host ヘッダー (VirtualHost) のドメインが入るようにしてあります。
/oauth2/
配下へのアクセスまで auth_request の対象としてしまうとループしてしまうため、そうならないようにするのが先に少し触れた 同じドメインだけど Ingress が2ヵ所で設定されている理由です。ついつい次のように書いてしまいそうになりますが、これではループしてしまうのです。
spec:
rules:
- host: alertmanager.example.com
http:
paths:
- backend:
serviceName: kube-prometheus-stack-alertmanager
servicePort: 9093
path: /
- backend:
serviceName: oauth2-proxy
servicePort: 80
path: /oauth2
同じドメインを複数 Ingress に分けて定義しても Nginx Ingress Controller は同一ドメインは同じ VirtualHost にまとめてくれます。
Nginx Ingress Controller が生成する nginx.conf を見ると何をやっているのかがわかります。
ちなみに、oauth2-proxy はその名の通り、それ単体でも認証付きの proxy として動作します。