自宅の Kubernetes 上で動かしているサービスをインターネットに公開し,そこに Google OAuth 認証を付ける方法を紹介します.
前提とする環境
次の環境を前提とします.
- PPPoE だけでなく,IPv6 IPoE でもインターネットに接続している.
- 固定 IP はもっていない.
- インターネットには YAMAHA RTX830 で接続し,内部ネットワークはその下流の Ubiquiti Dream Machine Pro 以下に配置.
- 独自ドメインを Google Domains で取得済.
- Kubernetes クラスタには MetalLB を配置し,サービスは
type: LoadBalancer
で運用.
該当しない場合,関連した設定は省略できます.
設定
設定は,下記の3ステップで行っていきます.
- INGRESS によるサービスの公開
- Let’s Encrypt による TLS 対応
- Google OAuth 認証の設定
順番に説明していきます.
INGRESS によるサービスの公開
この設定を行うことで,Kubernetes 上のサービスにインターネットからアクセスできるようになります.
設定が必要になるのは,下図の①~⑤の5項目になります.
①~③によってインターネットから自宅サーバの内のリバースプロキシにアクセスできるようにします.④ によって自宅サーバの IP アドレスをドメイン名で指定できるようにします.⑤ はリバースプロキシの設定です.
前提として,RTX830 には下記のような設定があり,pp 1
で PPPoE 接続,tunnele 2
で IPoE 接続がされているものとします.また,pp 1
にはディスクリプタ番号 1000 の NAT が設定されているものとします.
1 2 3 4 5 6 7 8 9 10 11 12 |
pp select 1 description pp OCN pppoe use lan2 (中略) ip pp nat descriptor 1000 pp enable 1 tunnel select 2 tunnel encapsulation map-e tunnel map-e type ocn ip tunnel mtu 1460 ip tunnel nat descriptor 2000 tunnel enable 2 |
内部ルータの RTX830 側のプライベートアドレスは 192.168.4.2
,INGRESS のプライベートアドレスは 192.168.4.250
とします.INGRESS のアドレスの設定方法は以降で説明します.
① 宛先が Port 80, 443 のパケットを内部ルータに中継
RTX830 に対して下記の設定を行います.
1 2 |
nat descriptor masquerade static 1000 1006 192.168.8.2 tcp www nat descriptor masquerade static 1000 1007 192.168.8.2 tcp https |
これにより,PPPoE から入ってきた,Port 80, 443 宛のパケットは 192.168.8.2
に転送されるようになります.
IPv6 IPoE については,外部からアクセスされる際に使えるポートに制約があるので,類似の設定は行いません(行う意味がありません).
② 送信元が Port 80, 443 のパケットは PPPoE で応答
RTX830 に対して下記の設定を行います.
1 2 3 |
ip route default gateway tunnel 2 gateway pp 1 filter 100001 100002 100003 100004 100005 100006 100007 100008 100009(中略) ip filter 100008 pass * * tcp www * ip filter 100009 pass * * tcp https * |
これによって,インターネットから Port 80, 443 にアクセスした際の応答を正常に受け取れるようになります.
PPPoE と IPoE を併用している場合,基本的には IPoE を優先していると思います.そのため,何も設定を行わないとインターネットから Port 80, 443 へアクセスした際の応答も IPoE から返されてしまうことになり,発信元はパケットを破棄してしまいます.
③ 宛先がPort 80, 443 のパケットを INGRESS に中継
Ubiquiti を使っている場合,Network の Setting – Firewall & Security から行います.
具体的にはPort Forwarding に対して以下のような設定を追加します.
設定値はこんな感じ.
④ ダイナミック DNS の更新
大きく次の3点を行います.
- ダイナミック DNS 対象ホスト名の指定
- PPPoE 側の IP アドレスの取得方法準備
- ダイナミック DNS クライアントの設定
ダイナミック DNS 対象ホスト名の指定
Goole Domains の設定画面の DNS の設定の一番下にある「詳細設定を表示」をクリックすると「ダイナミック DNS の管理」の項目が出てくるので,そこから設定を行います.
まず,「xxxx.kubernetes.ドメイン名」(xxxx は任意)でアクセスできるようにしたいので,ホスト名として「*.kubernetes」を指定します.
ホスト名の設定が終わると,そのホスト名の IP アドレスを設定する際に必要になる認証情報が取得できるのでメモしておきます.
PPPoE 側の IP アドレスの取得方法準備
ダイナミック DNS で設定する IP アドレスの取得方法を準備します.RTX830 の場合,下記のようにして SNMP を有効化しておけば,
1 |
snmpv2c host lan1 public |
次のコマンドで IP アドレスを取得できるようになります.(192.168.8.1 は,LAN1 側の RTX830 のアドレス)
1 |
$ snmpget -v 2c -c public 192.168.8.1 .1.3.6.1.4.1.1182.2.6.14.0 -Ov -OQ |
ダイナミック DNS クライアントの設定
クライアントソフトとしては,比較的新しそうな inadyn (In-a-Dyn) を使うことにします.
設定ファイルとして次の内容のファイル inadyn.conf
を用意します.
1 2 3 4 5 6 7 8 9 |
period = 60 user-agent = Mozilla/5.0 provider domains.google.com { hostname = "*.kubernetes.green-rabbit.net" username = XXXXXXXXXXXXXXXX password = XXXXXXXXXXXXXXXX checkip-command = "snmpget -v 2c -c public 192.168.8.1 .1.3.6.1.4.1.1182.2.6.14.0 -Ov -OQ" } |
続いて次の内容の Dockerfile を準備します.inadyn には公式の Docker イメージもありますが,checkip-command
として snmpget
を使いたいので,コンテナを作ることにします.
1 2 3 4 5 6 7 8 9 10 |
FROM ubuntu:22.04 RUN apt-get update && apt-get install -y \ ca-certificates \ inadyn snmp \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* COPY inadyn.conf /etc/inadyn.conf ENTRYPOINT ["/usr/sbin/inadyn", "--foreground"] |
次のように実行してエラーが出なければ OK です.
1 2 |
$ docker build -t inadyn . $ docker run -it --rm inadyn |
上手くいっていると次のように出力されて,ホスト名に対応する IP アドレスが設定されます.
1 |
Update forced for alias *.kubernetes.green-rabbit.net, new IP# 153.172.145.12 |
dig
で名前引きできるようになっているはずです.
1 2 3 4 |
$ dig x.kubernetes.green-rabbit.net (snip) ;; ANSWER SECTION: x.kubernetes.green-rabbit.net. 60 IN A 153.172.145.12 |
⑤ リバースプロキシの設定
リバースプロキシとしては,Ingress NGINX Controller を使うことにします.
Ingress NGINX Controller の IP アドレスを「192.168.4.250」に固定するため,IP アドレスが1つだけの Pool を設定し,Ingress NGINX Controller にはそのプールから IP アドレスを取得するようにします.
具体的な設定としては次の2つを行います.
- MetalLB に専用のプールを準備
- Ingress NGINX Controllerの配置
MetalLB に専用のプールを準備
name: ingress-pool
な IPAddressPool を定義して,L2Advertisement の ipAddressPools
に追加します.
具体的には,次のような YAML を用意して apply します.(一番目の name: ingress-pool
な IPAddressPool は既存の pool です)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: default namespace: metallb-system spec: addresses: - 192.168.4.1-192.168.4.200 --- apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: ingress-pool namespace: metallb-system spec: addresses: - 192.168.4.250/32 --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default namespace: metallb-system spec: ipAddressPools: - default - ingress-pool |
Ingress NGINX Controllerの配置
まず,下記の Quick Start にある YAML をダウンロードします.
ingress-nginx-controlle
r の Service に annotation を追加して,先ほど準備した ingress-pool
からアドレスを取得するようにして apply します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
--- deploy.yaml 2023-06-06 20:12:57.618860314 +0900 +++ ingress-nginx.yml 2023-06-04 15:57:53.035830038 +0900 @@ -334,24 +334,26 @@ --- apiVersion: v1 kind: Service metadata: labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.8.0 name: ingress-nginx-controller namespace: ingress-nginx + annotations: + metallb.universe.tf/address-pool: ingress-pool spec: externalTrafficPolicy: Local ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - appProtocol: http name: http port: 80 protocol: TCP targetPort: http - appProtocol: https |
動作確認
ここまできたら,ブラウザで「http://x.kubernetes.green-rabbit.net/」にアクセスしています.今回は NGINX までアクセスできるかの確認が目的なので,「x」部分は何でもOKです.
以下の用に表示されれば,ひとまず OK です.NGINX の設定を何も行っていないので,NGINX が「404 Not Found」と出力するのが期待値です.
Let’s Encrypt による TLS 対応
続いて,NGINX を実際にリバースプロキシ機能させた上で,TLS 対応を行います.
- cert-manager のインストール
- Let’s Encrypt を使う ClusterIssuer の設定
- Ingress の設定
cert-manager のインストール
公式ページに従って,cert-manager.yaml
を apply すれば OK.

Let’s Encrypt を使う ClusterIssuer の設定
次のような YAML を用意して apply します.name: letsencrypt-production
で指定した「letsencrypt-production」は ingress の設定の際に使います.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-production namespace: ingress-nginx spec: acme: email: kimata@green-rabbit.net server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-production solvers: - http01: ingress: class: nginx |
Ingress の設定
例として,次の設定を行うことにします.
- ネームスペース「ingress-nginx」に Ingress を配置
- ホスト名は,「grafana.kubernetes.green-rabbit.net」
- バックエンドは,ネームスペース「server」の grafana サービス
次のような YAML を用意して apply すれば OK.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: grafana-ingress-tls annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-production acme.cert-manager.io/http01-edit-in-place: "true" namespace: ingress-nginx spec: ingressClassName: nginx tls: - hosts: - grafana.kubernetes.green-rabbit.net secretName: grafana-secret rules: - host: grafana.kubernetes.green-rabbit.net http: paths: - pathType: Prefix path: / backend: service: name: server-grafana port: number: 3000 --- apiVersion: v1 kind: Service metadata: name: server-grafana namespace: ingress-nginx spec: type: ExternalName externalName: grafana.server.svc.cluster.local |
cert-manager.io/cluster-issuer: letsencrypt-production
の「letsencrypt-production」は先ほど ClusterIssuer の設定で使った名前です.
2番目の項目は,ネームスペース跨ぎのために必要な設定です.Ingress と Service は同一ネームスペースにある必要がありるのですが,今回使いたいバックエンドは異なるネームスペースにあります.そこで,Ingress と同じネームスペースに Service を定義して,その中の ExternalName
で別のネームスペースのサービスを参照しています.
別のネームスペースの内容は「サービス名.ネームスペース名.svc.cluster.local」で参照できます.
動作確認
設定してからしばらくしてから,「https://grafana.kubernetes.green-rabbit.net」にアクセスします.上手くいっていれば,証明書の警告無くアクセスできるようになっていると思います.
上手くいっていない場合,次のコマンドで cert-manager のログを確認するとヒントを得られると思います.
1 |
$ kubectl logs -n cert-manager cert-manager-xxxxxxxxxx-xxxxx |
Google OAuth 認証の設定
やることは次の2つです.
- クライアントID、クライアントシークレットの取得
- OAuth2 Proxy の設定
- Ingress の設定
クライアントID、クライアントシークレットの取得
ざっと流れだけ説明します.
- 次の URL にアクセスします.
Google Cloud PlatformGoogle Cloud Platform lets you build, deploy, and scale applications, websites, and services on the same infrastructure ...
- 上の方にある,丸が三角形に配置された部分をクリックします.
※ 右上に「有効化」と書かれたボタンが現れますが,これは有効化する必要ありません. - 右上にある「新しいプロジェクト」をクリックして,プロジェクトを適当に定義します.
- 「OAuth 同意画面」の設定を行います.Google Workspace を使っている場合は「User Type」を「内部」に,そうでない場合は「外部」を選択したうえで「公開ステータス」を「テスト」にして,アクセスできるユーザを限定すると良いです.
- 「認証情報」-「認証情報を作成」をクリックして,「OAuth 2.0 クライアント ID」の作成を行います.アプリケーションの種類は「ウェブアプリケーション」にして下記のように設定します.
- 作成した OAuth 2.0 クライアント ID の右端にある矢印アイコンをクリックすると,「クライアント ID」と「クライアント シークレット」が表示されますので,コピーします.
OAuth2 Proxy の設定
次のような YAML を用意します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
apiVersion: apps/v1 kind: Deployment metadata: labels: app: oauth2-proxy-grafana name: oauth2-proxy-grafana namespace: ingress-nginx spec: replicas: 1 selector: matchLabels: app: oauth2-proxy-grafana template: metadata: labels: app: oauth2-proxy-grafana spec: containers: - name: oauth2-proxy image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0-amd64 args: - --provider=google - --upstream=file:///dev/null - --http-address=0.0.0.0:4180 - --client-id=XXXXXXXX.apps.googleusercontent.com - --client-secret=GOCSPX-XOAthXXXXXXX - --cookie-domain=.kubernetes.green-rabbit.net - --cookie-name=_oauth2_proxy_grafana - --email-domain=green-rabbit.net - --whitelist-domain=.green-rabbit.net - --cookie-refresh=1h - --cookie-secret=XXXXXXXXXXXXXXXX - --cookie-csrf-per-request=true - --cookie-csrf-expire=10m ports: - containerPort: 4180 protocol: TCP --- apiVersion: v1 kind: Service metadata: labels: app: oauth2-proxy-grafana name: oauth2-proxy-grafana namespace: ingress-nginx spec: ports: - name: http port: 4180 protocol: TCP targetPort: 4180 selector: app: oauth2-proxy-grafana type: LoadBalancer |
環境に合わせて,次の箇所は書き換えてください.
- 名前やラベルの接尾辞.今回 Grafana 用なので「-grafana」を付与しています.
--client-id
や--client-secret
の指定は,先ほど入手した「クライアント ID」と「クライアント シークレット」に置き換えます.--cookie-domain
,--email-domain
,--whitelist-domain
は,お使いのドメインに変更します.--cookie-name
は,複数のサイトに個別に認証をつける場合,ユニークになる名前をセットしておくとよいと思います.--cookie-secret
は,任意のランダム文字列.文字数の規定がありますので,16文字で指定すると良いと思います.
Ingress の設定
前のステップで設定した Ingress 設定の annotations に「ginx.ingress.kubernetes.io/auth-signin」と「nginx.ingress.kubernetes.io/auth-url」の指定を追加します.これにより,最初にアクセスしたブラウザは,「https://grafana.kubernetes.green-rabbit.net/oauth2/auth」に飛ばされるようになります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: gragana-tls annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-production acme.cert-manager.io/http01-edit-in-place: "true" + nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$http_host$request_uri + nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth namespace: ingress-nginx spec: ingressClassName: nginx tls: - hosts: - grafana.kubernetes.green-rabbit.net secretName: grafana-secret rules: - host: grafana.kubernetes.green-rabbit.net http: paths: - pathType: Prefix path: / backend: service: name: server-grafana port: number: 3000 |
続いて,「https://grafana.kubernetes.green-rabbit.net/oauth2/auth」用の Ingress を設定します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: grafana-oauth2-proxy annotations: kubernetes.io/ingress.class: nginx namespace: ingress-nginx spec: ingressClassName: nginx tls: - hosts: - grafana.kubernetes.green-rabbit.net secretName: grafana-secret rules: - host: grafana.kubernetes.green-rabbit.net http: paths: - pathType: Prefix path: /oauth2 backend: service: name: oauth2-proxy-grafana port: number: 4180 |
これにより,先ほど定義した OAuth2 Proxy が使われるようになります.
動作確認
「https://grafana.kubernetes.green-rabbit.net/」にアクセスすると,良く見慣れた Google のログイン画面が表示されると思います.無事にログインできると,本来表示したかったサービスが表示されるはずです.
お疲れさまでした.
参考文献
- 自宅Kubernetesクラスタはじめました
- Ingress と cert-manager の設定を参考にしました.特に MetalLB で専用プールを用意して IP を固定する部分は丸パクリです.
- Kubernetes環境における証明書管理の自動化
- Ingress と cert-manager の設定が詳しめに解説されています.現時点で情報の鮮度も良さそうです.
- Google-OAuth2 With Kubernetes Nginx Controller
- Google の OAuth2 との連携させる方法はここをベースにしています.ただ,少し情報古いかも.
- External OAUTH Authentication
- Ingress-Nginx Controller 公式の,外部 OAuth 認証の解説ページです.
- 単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる
- OAuth 2.0 のフローと技術的な弱点が分かりやすく解説されています.
- OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る
- OAuth 2.0 と OpenID Connect の関係がなんとなく見えてきます.この記事ではでてきてないですが,Google の OpenID Connect に準拠した OAuth 2.0 を使うと,Google 基準のバリバリのリスクベース認証の恩恵に預かれるというメリットがでかいと個人的には思います.
コメント