kubernetes上のサービスにOAuth2 Proxyを使ってGoogle認証を追加する

## tl;dr

- OAuth2 Proxyは既存サービスに変更を加えずにOAuth2認証を追加できるOSSで、Goole認証も追加できるよ
- GCP(Google Cloud Platform)でOAuth2の設定をして、kubernetesのPodの中にoauth2-proxyを追加したら簡単にGoogle認証を追加できたよ

## OAuth2 Proxy とは

プロキシとして動作して、OAuth2認証を追加できるOSSです。

https://oauth2-proxy.github.io/oauth2-proxy/

アーキテクチャ図を見ると、一度通信をoauth2-proxyが受けて、OAuth2認証を行い、認証の通った通信のみを、バックエンドのサービスに繋ぐ様になっていることがしれます。

> https://oauth2-proxy.github.io/oauth2-proxy/
>
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/c2bf94e9-5505-f18a-804d-a34bee2b9336.png)

## やってみたのアーキテクチャ

自宅サーバ（OrangePi5+、ラズパイのようなもの）で自分が使うWebサービスを公開しているのですが、これを使うことで簡単にGoogle認証を組み込めないかやってみました。

なお、自宅サーバは以下のようなものです。

- マシン: OrangePi5+ ARM64 Memory16GB
- SSLのフロント: nginx
- コンテナオーケストレーションツール: k3s

構成は以下の通りです。フロントにSSLを処理するためのnginxが入っていますが、今回は本題ではないので説明を省略します。アプリケーションのnamespaceの方をみてください。Podの中に、oauth2-proxyコンテナと、保護対象のコンテナの2つがある構成とします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/4530167d-dbe5-342f-cef6-711e98e1f94d.png" width="50%" />

## Google認証の準備

Google側の設定は、GCPコンソール上で設定できました。詳細は、OAuth2 Proxyのドキュメントにあります。

https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/google

GCPの「APIとサービス」のメニューの認証情報から、認証情報の作成を行います。

![Screenshot 2024-02-24 at 10.53.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/12ddc424-fc93-3ee2-d6e2-3aa91b90add6.png)

https://chatgpt.74th.tech というURLでサービスをすると以下のように設定します。

- アプリケーション種類: Webアプリケーション
- 名前: 任意
- 承認済みのJavaScript生成元: `https://chatgpt.74th.tech`
- 承認済みのリダイレクトURI: `https://chatgpt.74th.tech/oauth2/callback`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/6dfc1832-cd00-17a3-7cbd-b89a8366db44.png)

これで client_id と client_secret がコピーできるため、それをメモします。

これでGCP側の設定は完了です。

## OAuth2 Proxyの設定ファイルを作る

OAuth2 Proxyは設定ファイルが必要です。Google認証の場合、以下のようになります。

```oauth2-prox.cfg
http_address = "0.0.0.0:3001"
redirect_url = "https://chatgpt.74th.tech/oauth2/callback"
upstreams = ["http://localhost:3000/"]
client_id = "<GCPからコピーしたclient_id>"
client_secret = "<GCPからコピーしたclient_secret>"
oidc_issuer_url = "https://chatgpt.74th.tech"
provider = "google"
cookie_secret = "<作成したcookie_secret>"
```

- http_address: oauth2-proxyのプログラムがリスンするホストとポート
- redirect_url: `https://chatgpt.74th.tech/oauth2/callback`
- upstreams: oauth2-proxyから保護対象のコンテナへのアクセス
- client_id、client_secret: GCPで設定後得た値
- oidc_issuer_url: `https://chatgpt.74th.tech`
- provider: `google`
- cookie_secret: 後述

cookie_secretはただの乱数値でよく、公式サイトにもPythonでの生成例が掲載されています。

> https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview
> ```
> python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
> ```

許可するユーザは、いくつか設定方法がありますが、メールアドレスのリストファイルを作る方法があります。

```authenticated-emails.txt
fugafuga@gmail.com
hogehoge@gmail.com
```

## Kubernetesマニフェストを作る

設定ファイルをマウントしたいため、KustomizeのConfigmap Generatorの機能を使います。

```yaml:kustomization.yaml
resources:
  - deploy.yaml
  - service.yaml
configMapGenerator:
  - name: oauth2-proxy-config
    files:
      - oauth2-proxy-config/oauth2-proxy.cfg
      - oauth2-proxy-config/authenticated-emails.txt
```

Deploymentは以下のように記述しました。

- containersに、アプリケーションと、oauth2-proxyと目的のアプリの2つのコンテナを設定する。
- OAuth2 Proxyの設定ファイルをマウントするように設定する。
- OAuth2 Proxyのコンテナの、プログラムの引数に設定ファイルを指すように設定する。

```yaml:deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: server
  template:
    metadata:
      labels:
        app: server
    spec:
      containers:
        # 目的のアプリ
        - name: server
          image: gcr.io/gcp-project/chatgpt-app:latest
          ports:
            - containerPort: 3000
        # OAuth2 zproxy
        - name: oauth2-proxy
          image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0-arm64
          ports:
            - containerPort: 3001
          args:
            - --config=/etc/oauth2-proxy-config/oauth2-proxy.cfg
            - --authenticated-emails-file=/etc/oauth2-proxy-config/authenticated-emails.txt
          volumeMounts:
            - mountPath: /etc/oauth2-proxy-config
              name: oauth2-proxy-config
      volumes:
        - name: oauth2-proxy-config
          configMap:
            name: oauth2-proxy-config
```

特に紹介すべきことはないですが、Serviceのマニフェストです。

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: server
  namespace: chatgpt
  labels:
    run: server
spec:
  ports:
    - name: http
      port: 3001
      targetPort: 3001
      nodePort: 30310
  selector:
    app: server
  type: NodePort
```

## アクセスすると

OAuth2 Proxyのページが最初に表示され、Google認証に進むことができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/f24c8d49-11f8-5020-cd00-0d2753d7ca49.png)

## 終わりに

簡単に自宅サーバ上のKubernetesで動かすアプリに、Google認証を追加することができました。

ユーザ管理を必要としないアプリケーションであれば簡単に設定できるため、便利に感じます。

Proxyであることで、バックエンドのアプリケーションの脆弱性を気にする必要がない（Cookieハイジャックはありますが）のも、非常にありがたいです。