Nginx + OAuth2 Proxy + StreamlitでGoogleログイン後にStreamlitにアクセスする環境をローカルコンテナ環境で作ってみた

2024.03.16

こんちには。

データアナリティクス事業本部 機械学習チームの中村(nokomoro3)です。

今回は、Nginx + OAuth2 Proxy + StreamlitでGoogleログイン後にStreamlitにアクセスする環境をローカルコンテナ環境で作ってみます。

実行環境と準備

実行環境としてはWindows 10マシンを使います。

また前提としてRancher Desktopをセットアップ済みであり、Googleの認証情報作成のためにGoogle Cloudにログインできる環境を作成済みという前提で進めます。

Rancher Desktopのセットアップについては以下も参考にされてください。

Streamlitのコンテナ作成

まずはStreamlit単体のコンテナを作成します。

Dockerfileの作成

以下のようなDockerfileを./streamlit/Dockerfileに作成しました。

FROM python:3.9-slim

WORKDIR /app

RUN apt-get update && apt-get install -y \
    build-essential \
    curl \
    software-properties-common \
    git \
    && rm -rf /var/lib/apt/lists/*

RUN git clone https://github.com/streamlit/streamlit-example.git .

RUN pip3 install -r requirements.txt

EXPOSE 8501

HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health

ENTRYPOINT ["streamlit", "run", "streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]

以下の公式ドキュメントを参考に作成しています。

docker-compose.ymlの作成

こちらをdocker-composeで立ち上げるために、./docker-compose.ymlを作成します。

version: '3'

services:
  streamlit:
    container_name: streamlit
    build:
      context: ./streamlit
      dockerfile: Dockerfile
    ports:
      - 8501:8501

動作確認

docker-composeを使って起動します。

docker compose up -d

localhost:8501にアクセスすると、以下のような画面を確認できます。

Nginxのリバースプロキシを追加

次にNginxにアクセス後、Streamlitに転送されるような構成をしていきます。

nginx.confの作成

まずは./nginx/nginx.confを以下のように記載します。

events {
    worker_connections  16;
}
http {
    server {
        listen 80;
        server_name localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        location /streamlit/ {
            proxy_pass              http://streamlit:8501/;
            proxy_http_version      1.1;
            proxy_set_header        Upgrade $http_upgrade;
            proxy_set_header        Connection "upgrade";
        }
    }
}

何も考えずにproxy_passのみの設定を記載すると、WebSocket関連でエラーが出たので以下を参考に設定をしています。

docker-compose.ymlの追記

./docker-compose.ymlにNginxの部分を追記します。

  nginx:
    container_name: nginx
    image: nginx:latest
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 8000:80
    depends_on:
      - streamlit

動作確認

再度立ち上げなおします。

docker-compose down
docker-compose up -d

localhost:8000/streamlitにアクセスすると、先ほどと同じStreamlitの画面を確認できるはずです。

OAuth2 Proxyの導入

OAuth 同意画面の作成

以下に従って、OAuth Providerの設定を行っていきます。

まずは以下にアクセスし使用するプロジェクトを選択します。

  • https://console.cloud.google.com

以下のメニューで、「OAuth 同意画面」を作成します。(なお「OAuth 同意画面」はプロジェクトに対して一つ作成する形になりますので、既に存在する場合は流用などをされてください)

User Typeを「内部」とすることで、Google Cloudのプロジェクトと同じ組織内のGoogleアカウントがログインできるようになります。

アプリ名を適当に入力し、メールアドレスを入力します。

デベロッパーの連絡先としてメールアドレスをこちらにも入力します。

以降は特に変更せず進めて「OAuth 同意画面」の作成は完了です。

認証情報の作成

「認証情報」を作成していきます。

「認証情報を作成」を押下し、「OAuth クライアント ID」をクリックします。

「ウェブ アプリケーション」を選択し、適当な名前を入力して押下します。

承認済みのリダイレクトURIに以下を追記して、「作成」を押下します。

こちらのリダイレクトURIは、後程OAuth2 Proxyの設定で使用します。

最後にクライアントIDとクライアントシークレットが表示されますので、控えておきます。

以上で認証情報の設定は完了です。

docker-compose.ymlの追記

docker-compose.ymlにOAuth2 Proxyのコンテナを追加していきます。

  oauth2-proxy:
    container_name: oauth2-proxy
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    ports:
      - "4180:4180"
    env_file:
      - ./oauth2_proxy.env

また、./oauth2_proxy.envは以下のようにOAuth2 Proxyの設定値を記載しておきます。

OAUTH2_PROXY_PROVIDER=google
OAUTH2_PROXY_CLIENT_ID={作成したクライアントID}
OAUTH2_PROXY_CLIENT_SECRET={作成したクライアントシークレット}
OAUTH2_PROXY_COOKIE_SECRET={パスワードジェネレータなどで作成するランダムな文字列}
OAUTH2_PROXY_REDIRECT_URL={認証情報作成時に記載した承認済みリダイレクトURI}
OAUTH2_PROXY_EMAIL_DOMAINS="*"
OAUTH2_PROXY_HTTP_ADDRESS="0.0.0.0:4180"
OAUTH2_PROXY_COOKIE_SECURE=false

OAUTH2_PROXY_COOKIE_SECRETは自身でパスワードジェネレータなどを使って、ランダムな文字列を作成してください。

nginx.confの修正

最後にNginxからOAuth2 Proxyに転送するよう./nginx/nginx.confに設定を追加します。

events {
    worker_connections  16;
}
http {
    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 /streamlit/ {
            auth_request /oauth2/auth;
            error_page 401 = /oauth2/sign_in;
            proxy_pass              http://streamlit:8501/;
            proxy_http_version      1.1;
            proxy_set_header        Upgrade $http_upgrade;
            proxy_set_header        Connection "upgrade";
        }

        location = /oauth2/callback {
            proxy_pass http://oauth2-proxy:4180;
        }

        location /oauth2/ {
            proxy_pass       http://oauth2-proxy:4180;
            proxy_set_header Host                    $host;
            proxy_set_header X-Real-IP               $remote_addr;
            proxy_set_header X-Scheme                $scheme;
            proxy_set_header X-Auth-Request-Redirect $request_uri;
        }

        location = /oauth2/auth {
            proxy_pass       http://oauth2-proxy: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;
        }
    }
}

動作確認

再度立ち上げなおします。

docker-compose down
docker-compose up -d

localhost:8000/streamlitにアクセスすると、以下のようにOAuth2 Proxyの画面に遷移します。

OAuth 同意画面が表示されますので、アカウントを選択して進みます。

「次へ」を押下して進みます。

すると、streamlitの画面に転送されます。

Nginx側(localhost:8000)にも認証が必要となるよう設定をしましたが、一度Streamlit側で認証済みであればこちらにもアクセスできるようになります。

補足

最終なdocker-compose.ymlの記載

最終的にはStreamlitもOAuth2 ProxyもNginx経由でアクセスするので、ポートの設定を削除してもOKです。

version: '3'

services:
  streamlit:
    container_name: streamlit
    build:
      context: ./streamlit
      dockerfile: Dockerfile

  nginx:
    container_name: nginx
    image: nginx:latest
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    ports:
      - 8000:80
    depends_on:
      - streamlit
      - oauth2-proxy

  oauth2-proxy:
    container_name: oauth2-proxy
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    env_file:
      - ./oauth2_proxy.env

ログインをやり直して動作確認するには

ブラウザの開発ツールのApplicationタブで、StorageのCookiesにhttp://localhost:8000があると思いますので、そちらをClearすれば再度ログインからやり直して動作確認できます。

まとめ

いかがでしたでしょうか。GoogleをIdPとしたログインは組織内部に限定することも可能ですので、社内に限定公開したい場合などに有効に使えそうです。

これらをAWS上にデプロイする方法も今後記事にしたいと思います。本記事が皆様のご参考になれば幸いです。

参考