はじめに
KubernetesのIngressは、外部からのHTTP/HTTPSリクエストをホスト名やパスに応じて適切なServiceへ振り分けるリソースです。Ingressは標準のYAML設定に加え、annotationを設定することでコントローラー固有の多様な機能を利用できます。
今回は、NGINX Ingress Controllerのannotationを活用し、Webアプリへのアクセスに認証機能を組み込む手順を紹介します。
要約
Ingressで提供するWebアプリに認証機能を導入するには、annotationに以下のような設定を追加します。
metadata:
...
annotations:
nginx.ingress.kubernetes.io/auth-signin: "https://[hostname][:port]/login"
nginx.ingress.kubernetes.io/auth-url: "https://[hostname(または別名)][:port(または別ポート)]/auth-check"
...
-
[hostname]は、Webアプリのホスト名と一致する必要があります- ただし、
nginx.ingress.../auth-urlのannotationに指定するホスト名は、NGINX Ingress Controllerが到達可能であれば、Webアプリと異なっていても問題ありません
- ただし、
-
nginx.ingress.kubernetes.io/auth-url- リクエストするクライアントが認証済かどうかをチェックするパスを指定します
-
nginx.ingress.kubernetes.io/auth-signin- ログインするパスを指定します
全体構成の説明
図のように、リバースプロキシとしてNGINX Ingress Controllerを用い、認証用WebアプリとメインのWebアプリへ転送します。
クライアントがメインのWebアプリにアクセスした際、NGINX Ingress Controllerはまず認証用Webアプリの認証確認用パスへリクエストを送信します。認証済みであれば通常通りメインのWebアプリのレスポンスが返され、未認証の場合にはログインページへ誘導されます。この仕組みを動かすには、認証用Webアプリが「認証チェック用エンドポイント」と「ログインページ」を提供していることが必要です。
外部のIdPが利用できる場合は、以下の記事で紹介されているように OAuth2 Proxy を使うことで、自前で認証機能を実装せずに済みます。
しかし本記事では外部IdPは使用せず、筆者が自前で作成した簡易的なフォーム認証アプリを利用して仕組みを説明します。
とりあえず動かしてみたい方
以下の ingress-auth-with-tilt リポジトリをcloneしてセットアップし、tilt up を実行してみてください。
事前準備
前提条件
- Kubernetesクラスターが構築済であること
-
kubectl,helm,helmfileコマンドが使えること
認証用Webアプリの準備
以下のパスを用意している認証用Webアプリが必要です。
- ログイン用(例:
/login)- ログインに成功したらセッションCookieをクライアントに返す
- 認証チェック用(例:
/auth-check)- クライアントの認証状態を確認する。認証されていない場合はUnauthorized(401)を返す
今回は、私が作成した以下の「なんちゃってフォーム認証アプリ」をDockerコンテナで起動します。
以下は実行コマンドの例です。
docker run -d \
--name ts-form-auth-webapp-web \
-p 3000:3000 \
ghcr.io/showchan33/ts-form-auth-webapp-web:v1.0
Dockerが使えない場合は、ts-form-auth-webapp-webのGitHubリポジトリのREADMEをもとにセットアップして、Webアプリを起動してください。
Webブラウザで、
http://localhost:3000/login
にアクセスすると、ログインページが表示されます。
User ID は user1、Password は pass1 と入力して Login ボタンを押下すると、ログインを完了します。
ちなみに、http://localhost:3000/auth-check にアクセスすると現在の認証状態がわかります。Webブラウザには出てきませんが、認証OKならば200、NGなら401のステータスコードがクライアントに返されています。
注意点
以後に記載する内容を実現するには、認証用WebアプリはKubernetesクラスターと同じホストで起動する必要があります。例えばEKS等のマネージドなクラスターを利用する場合、上記の方法だと同じホストに認証用Webアプリを起動するのは難しいはずです。その場合、本記事での説明は割愛しますが、認証用WebアプリをKubernetesのPodで起動して、ServiceやIngress等のリソースを使ってクライアントからアクセスできる状態にしてください。
NGINX ingress controller のインストール
続いて、Kubernetesクラスターに NGINX ingress controller をインストールします。本記事では、Helmfileを使ってインストールする方法を紹介します。
まずは、以下のHelmfileを用意します。
repositories:
- name: ingress-nginx
url: https://kubernetes.github.io/ingress-nginx
releases:
- name: ingress-nginx
namespace: ingress-nginx
createNamespace: true
chart: ingress-nginx/ingress-nginx
version: 4.14.0
以下がインストールコマンドです。
helmfile apply -f helmfile-ingress-nginx.yaml
しばらくすると、各種リソースがデプロイされます。
$ kubectl get ingressclasses
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 81s
$ kubectl get deploy -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx-controller 1/1 1 1 25s
$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.108.38.178 <pending> 80:32553/TCP,443:32142/TCP 47s
ingress-nginx-controller-admission ClusterIP 10.98.178.88 <none> 443/TCP 47s
この例では、以下で NGINX ingress のエンドポイントにアクセス可能です。
http://[hostname]:32553-
https://[hostname]:32142(今回は使わない)
メインのWebアプリのデプロイ
続いて、認証を必要とするメインのWebアプリをデプロイします。本記事ではnginxのコンテナをデプロイしますが、お好きなWebアプリのコンテナを使っていただいて問題ないです。
kubectl create deployment nginx --image=nginx --replicas=1
kubectl expose deployment nginx --port=80 --target-port=80 --name=nginx
全体構成の図を再掲すると、ここまでで真ん中のing(Ingressリソース)以外が出来ている状態です。
認証用WebアプリにプロキシするIngressリソースの作成
仕上げに、以下のマニフェストでIngressリソースを作成します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auth-check-ingress
annotations:
nginx.ingress.kubernetes.io/auth-url: "http://[hostname]:3000/auth-check"
nginx.ingress.kubernetes.io/auth-signin: "http://[hostname]:3000/login"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- backend:
service:
name: nginx
port:
number: 80
path: /
pathType: ImplementationSpecific
以下の2つのannotationを付与しているのがポイントです。
-
nginx.ingress.kubernetes.io/auth-url- リクエストするクライアントが認証済かどうかをチェックするパスを指定します
-
nginx.ingress.kubernetes.io/auth-signin- ログインするパスを指定します
両方とも、「認証用Webアプリの準備」でデプロイしたWebアプリにアクセスするパスです。
このマニフェストを元に、Ingressリソースをデプロイします。
kubectl apply -f auth-check.ing.yaml
リソースが正常に作成されたことを確認します。
$ kubectl describe ingress auth-check-ingress
Name: auth-check-ingress
Labels: <none>
Namespace: default
Address:
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
*
/ nginx:80 (10.0.0.157:80)
Annotations: nginx.ingress.kubernetes.io/auth-signin: http://[hostname]:3000/login
nginx.ingress.kubernetes.io/auth-url: http://[hostname]:3000/auth-check
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Sync 17s nginx-ingress-controller Scheduled for sync
試しに、Ingressのエンドポイント
http://[hostname]:32553
にアクセスしてみます。
本来ならばNGINXのサンプルページが表示されますが、annotationの設定が効いて、以下のように認証ページにリダイレクトされます。
リダイレクトURLには、以下のように rd のクエリが付いています。
http://[hostname]:3000/login?rd=http://[hostname]:32553%2F
これは「認証後にどのURLへ戻すか」を示すためのパラメータで、その値は先ほどアクセスを試みた http://[hostname]:32553%2F になっています。どうやら、NGINX Ingressのデフォルトでこのクエリ文字列をつけてくれるようです。
ユーザID(user1)とパスワード(pass1)を入力すると、
認証が完了してNGINXのサンプルページにリダイレクトされます。
これで、認証を必要とするWebアプリケーションが作れているのを確認できました。
注意点ですが、クエリ rd に書かれたURLにリダイレクトするには、認証用Webアプリ側の実装でクエリ文字列をパースして値を取得し、リダイレクト先として指定する必要があります。私が作った「なんちゃってWeb認証アプリ」では、以下の箇所(55,59,71,77行目あたり)でこれを実装しています。
おわりに
NGINXの設定ファイルで同様の仕組みを実現する場合は、auth_request ディレクティブなどを用いた細かな設定が必要になります。一方、Ingressを利用すれば、必要なannotationを2つ追加するだけで同じ構成を簡潔に実現できます。機会があれば、ぜひIngressのannotationを活用してみてください。





