🔐

Nginx + OAuth2 Proxy で静的 Web サイトに認証機能を追加してみる

2022/12/10に公開

TL;DR

  • OAuth2 Proxy を使って Docusaurus で作成したドキュメントサイトに認証機能をつける
  • OAuth2 Proxy は、認証と認可を外部の認証基盤に委譲するためのリバースプロキシサーバ
  • 外部の認証基盤には、Amazon Cognito(認証・認可およびユーザ管理機能を提供する AWS のマネージドサービス)を利用

はじめに

前回の記事では、ドキュサウルスはコンテナ時代を航海するために、Docker の背中に乗り込みました。
とはいえ、いきなりインターネットに飛び出すのも気が引けますよね。
そこで、今回は信頼できるお友達とだけドキュサウルスに会えるようにしてあげましょう。

イメージとしては👇のような構成になります。

ローカル環境で認証機能の確認をする
ローカル環境で認証機能の確認をする

https://youtu.be/ySjGHYPypck

https://youtu.be/5rkYab-FgFk

OAuth2 Proxy って?

認証と認可を外部の認証基盤に委譲するためのリバースプロキシサーバです。
MIT License の OSS なので誰でも自由に使えます。
Google や GitHub などの OIDC プロバイダーを利用して認証を提供し、アカウントを検証することができます。

詳細が知りたい方は、👇のドキュメントをご覧ください。

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

あれ?、気づいちゃいましたか?
実は OAuth2 Proxy のドキュメントサイトは Docusaurus で作成されているんです🦖

認証するのは誰?

今回は、Amazon Cognito(ユーザープール)を外部の認証基盤として使ってみます。
Cognito は、認証・認可およびユーザ管理機能を提供する AWS のマネージドサービスです。

認証機能を追加してみよう

では、認証基盤の準備ができたので、各種設定ファイルを見ていきましょう。

Nginx の設定ファイルを編集

次のように、OAuth2 Proxy 用のパス設定を追加しています。

server-config/nginx.conf
events {
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    server_tokens off;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    server {
        listen 80;
        server_name localhost;

        location / {
+           auth_request /oauth2/auth;
+           error_page 401 = /oauth2/sign_in;
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

+       location /oauth2/ {
+           proxy_pass http://127.0.0.1:4180;
+           proxy_set_header Host $host;
+           proxy_set_header X-Real-IP $remote_addr;
+           proxy_set_header X-Scheme $scheme;
+       }

+       location /oauth2/auth {
+           proxy_pass http://127.0.0.1:4180;
+           proxy_set_header Host $host;
+           proxy_set_header X-Real-IP $remote_addr;
+           proxy_set_header X-Scheme $scheme;
+           proxy_set_header Content-Length "";
+           proxy_pass_request_body off;
+       }
+   }
}

OAuth2 Proxy の設定ファイルを作成

次に、server-config 配下に OAuth2 Proxy の設定ファイルのテンプレート(oauth2proxy.conf.template)を新規に作成します。
中身は次のようにします。(おそらく最小構成だと思います🙏)

server-config/oauth2proxy.conf.template
provider = "oidc"
http_address = "127.0.0.1:4180"
email_domains = ["*"]
scope = "openid"

cookie_secret = "${COOKIE_SECRET}"
cookie_secure = false
session_cookie_minimal = true

oidc_issuer_url = "https://cognito-idp.ap-northeast-1.amazonaws.com/${USER_POOL_ID}"

client_id = "${APPLICATION_CLIENT_ID}"
client_secret = "${APPLICATION_CLIENT_SECRET}"

redirect_url = "${REDIRECT_URL}"

ファイル内には、次の環境変数を埋め込んでいます。

環境変数 設定値
COOKIE_SECRET Cookie のシード文字列(後ほど、Python で生成します)
USER_POOL_ID Cognito のユーザープール ID
APPLICATION_CLIENT_ID Cognito のアプリケーションクライアント ID
APPLICATION_CLIENT_SECRET Cognito のアプリケーションクライアントのシークレット
REDIRECT_URL Cognito のアプリケーションクライアントのコールバック URL

詳細な設定項目は次を参照してください。

https://oauth2-proxy.github.io/oauth2-proxy/docs/next/configuration/overview

Supervisor の設定ファイルを作成

Supervisor は複数のプロセスを制御してくれるツールです。
今回は、コンテナ起動時に Nginx と OAuth2 Proxy の二つのプロセスを立ち上げますので、これらのプロセスをデーモン化する用途で使用します。

server-config 配下に Supervisor の設定ファイル(supervisord.conf)を新規に作成します。
中身は次のようにします。

server-config/supervisord.conf
[supervisord]
nodaemon=true

[program:nginx]
command=nginx -g "daemon off;"
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
startretries=10
autorestart=true

[program:oauth2_proxy]
command=/usr/local/bin/oauth2-proxy --config /etc/oauth2proxy.conf
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
startretries=10
autorestart=true

コンテナ起動時のスタートアップスクリプトを作成

コンテナ起動時に実行するスタートアップスクリプト(startup.sh)を新規に作成します。
中身は次のようにします。

startup.sh
#!/bin/bash
envsubst < /etc/oauth2proxy.conf.template > /etc/oauth2proxy.conf
/usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf

Dockerfile を編集

Dockerfile を次のように編集します。

Dockerfile
FROM node:lts-alpine3.16 AS build
COPY ./my-website ./
RUN npm install
RUN npm run build

FROM nginx:1.23.2

+ ARG CPU_ARCH=amd64
+ ARG OAUTH2_PROXY_VERSION=7.4.0

+ RUN set -x && \
+     apt update && \
+     apt install --no-install-recommends --no-install-suggests -y \
+     supervisor wget gettext-base

+ RUN wget https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v${OAUTH2_PROXY_VERSION}/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz && \
+     tar xf oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz -C /usr/local/bin/ --strip-components 1 && \
+     rm oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz

COPY ./server-config/nginx.conf /etc/nginx/nginx.conf
+ COPY ./server-config/oauth2proxy.conf.template /etc/oauth2proxy.conf.template
+ COPY ./server-config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+ COPY ./startup.sh /startup.sh
COPY --from=build /build /usr/share/nginx/html

- CMD [ "nginx", "-g", "daemon off;" ]
+ CMD [ "sh", "startup.sh" ]

アーキテクチャやバージョンを指定して OAuth2 Proxy をインストールしています。

OAuth2 Proxy のインストール情報
ARG CPU_ARCH=amd64
ARG OAUTH2_PROXY_VERSION=7.4.0
・・・
RUN wget https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v${OAUTH2_PROXY_VERSION}/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz && \
    tar xf oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz -C /usr/local/bin/ --strip-components 1 && \
    rm oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-${CPU_ARCH}.tar.gz

apt のインストールを高速化するようなオマジナイなんかがついていますが、必要なツールをインストールしているだけです。

  • supervisor は、Nginx および OAuth2 Proxy をデーモン化するため
  • wget は、OAuth2 Proxy のダウンロードで使用するため
  • gettext-base は、テンプレートファイルの環境変数を展開する envsubst を使えるようにするため
必要なツール群のインストール
RUN set -x && \
    apt update && \
    apt install --no-install-recommends --no-install-suggests -y \
    supervisor wget gettext-base

追加した設定ファイルおよびコンテナ起動時に実行するスタートアップスクリプトの COPY を追加しています。

追加した設定ファイルおよびスタートアップスクリプトのコピー
COPY ./server-config/oauth2proxy.conf.template /etc/oauth2proxy.conf.template
COPY ./server-config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY ./startup.sh /startup.sh

コンテナ起動時にスタートアップスクリプトを実行するようにします。

コンテナ起動時にスタートアップスクリプトを実行
CMD [ "sh", "startup.sh" ]

認証機能の確認をしてみよう!

長かったですが、いよいよ認証機能の確認をしてみます。
Docker ビルドして、コンテナを起動してみましょう!

Docker ビルド

それでは、Docker ビルドをしてみましょう。

DOCKER_BUILDKIT=1 docker build --progress=plain --no-cache -t my-website:1.0.0 .

コンテナの起動

コンテナを起動してみます。
コンテナ起動時に Cognito のクレデンシャル情報等の環境変数の値を渡すため、--env オプションを指定します。

docker run --rm -p 80:80 \
  --env COOKIE_SECRET=`python3 -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'` \
  --env USER_POOL_ID=xxxxxxxxxxxxxx_xxxxxxxxx \
  --env APPLICATION_CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxx \
  --env APPLICATION_CLIENT_SECRET=*************************************************** \
  --env REDIRECT_URL=http://localhost/oauth2/callback \
  my-website:1.0.0

http://localhost にアクセスして、認証画面が表示されれば成功です!
作成したユーザで早速ログインしてみましょう✨

ログインしている様子
ログインしている様子

最後に

いかがだったでしょうか?
これで信頼できるお友達だけをドキュサウルスに会わせることができました。
一安心ですね😌

えっ?ローカル環境だから、同一ネットワーク内の人じゃないとドキュサウルスに会えないじゃないかって?
ですよね、それでは次回は認証付きのドキュメントサイトをインターネットに公開してみましょう!
そうすればドキュサウルスも一躍有名になれますね😎✨

ちなみに、今回は静的 Web サイトに認証機能をつけただけなのであまり面白味はないかもしれませんが、コンテナ化しているので当然 K8s 上で動く場合であっても OAuth2 Proxy が使えて、手軽に認証機能を組み込むことができます。
やっぱりコンテナは素晴らしいですね。

それではまた次回のアドベントカレンダー(勝手にシリーズ化)でお会いしましょう😉

Discussion