Docker で OAuth2 Proxy を動かすサンプル

社内向け Web アプリを作りたい。さらに言えば会社で G Suite (Google Workspace) を契約しているので G Suite アカウントによる SSO でログインできるようにしたい。しかし OAuth2 の認証機能を実装するのは面倒くさい。

そんなとき、[OAuth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/) というリバースプロキシを Web アプリの前段に置くという選択肢がある。

これを試してみたく、Docker を使って動作させるところまでやってみたのでその方法をメモしておく。
※ 今回は G Suite による SSO の実装例を書いているが、OAuth2 に対応した IdP ならだいたい同じ方法でできるはず。

## コード
コンテナを3つ起動する。

- proxy: OAuth Proxy を動かすコンテナ。今回はポート 8000 で外部からアクセスできるようにする。
- web: Web アプリ。HTTP を喋れればどんな言語/フレームワークで実装されていても問題ない。
- redis: OAuth Proxy のセッションデータを格納する。

``` docker-compose.yml
version: '3'

services:

  proxy:
    build: proxy
    ports:
      - 8000:4180
    environment:
      OAUTH2_PROXY_HTTP_ADDRESS: 0.0.0.0:4180
      OAUTH2_PROXY_PROVIDER: google
      OAUTH2_PROXY_CLIENT_ID: 00000000000-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
      OAUTH2_PROXY_CLIENT_SECRET: XXXXXXXXXXXXXXXXXXXXXXXX
      OAUTH2_PROXY_EMAIL_DOMAINS: example.com
      OAUTH2_PROXY_UPSTREAMS: http://web/
      OAUTH2_PROXY_COOKIE_SECRET: Tg06aGvkpWU2S_sryFWGYg==
      OAUTH2_PROXY_SESSION_STORE_TYPE: redis
      OAUTH2_PROXY_REDIS_CONNECTION_URL: redis://redis/
      # 以下、HTTPS ではなく HTTP で動かすための設定
      OAUTH2_PROXY_COOKIE_SECURE: 'false'
      OAUTH2_PROXY_REDIRECT_URL: http://localhost:8000/oauth2/callback

  web:
    build: web

  redis:
    image: redis
    volumes:
      - redis:/data

volumes:
  redis:
```

OAuth2 Proxy はコマンドオプションや設定ファイルなど様々な方法でオプションを指定することができるが、Docker コンテナとして動かす場合は環境変数で指定するのがセオリーだろう。環境変数でオプションを指定する場合は `OAUTH2_PROXY_` prefix が必要になる。

Client ID や Client Secret の入手方法は公式ドキュメントに書いてあるので割愛。

今回は雑に試したいだけだったので HTTP で動かしているが、実際に本番環境で動かす際には証明書をマウントして HTTPS で動かせるようにしたほうがよい。

次に、Docker イメージの設定。
**[2021.06 追記]** 公式の Docker イメージもあるらしい： [quay.io/oauth2-proxy/oauth2-proxy](https://quay.io/repository/oauth2-proxy/oauth2-proxy)

``` Dockerfile:proxy/Dockerfile
FROM alpine

ARG VERSION=v5.1.1
ARG FILENAME=oauth2_proxy-${VERSION}.linux-amd64.go1.14.2

RUN wget -q https://github.com/oauth2-proxy/oauth2-proxy/releases/download/${VERSION}/${FILENAME}.tar.gz \
 && tar xzf ${FILENAME}.tar.gz \
 && mv ${FILENAME}/oauth2_proxy /bin/ \
 && rm -rf ${FILENAME}*

CMD ["/bin/oauth2_proxy"]
```

OAuth2 Proxy はビルド済みバイナリをダウンロードするだけでいいのでとても楽。

``` Dockerfile:web/Dockerfile
FROM php:apache

RUN echo "<?php\nphpinfo();" > /var/www/html/index.php
```

認証後に表示される Web アプリはなんでもいいのだが、例として PHP で `phpinfo()` を表示することにした。

## 動作
`docker-compose up` して http://localhost:8000/ にアクセスするとログイン画面が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/f21ccb33-0779-35e9-67e3-56fbe0bea170.png)

ログインに成功すると Web アプリ (今回は `phpinfo()`) が表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/37779053-cdb9-f8b6-bd58-930ff9603535.png)

Web アプリケーションが受け取るリクエストヘッダをみてみると、OAuth2 Proxy 側から `X-Forwarded-Email` `X-Forwarded-For` `X-Forwarded-User` などが送られてきていることがわかる。ログイン中のユーザを識別するだけであればこの情報で十分そう。

メールアドレスとユーザ ID 以外の情報 (例えば、G Suite アカウントに設定されている名前やアイコンなど) を取得したい場合は、 `OAUTH2_PROXY_PASS_ACCESS_TOKEN: 'true'` を指定するとリクエストヘッダに `X-Forwarded-Access-Token` が付与されてるようになるので、これを利用して API をコールするなどすれば実現できる。

あと気になる点としては、ログイン画面の UI をカスタマイズすることはできないのだろうか。(`OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: 'true'` でスキップすることはできるが)