OAuth2-Proxyを使ってみる
公開日:
カテゴリ: ソフトウェア
OAuth2/OpenID Connect(OIDC)の認証プロキシである、OAuth2-Proxyというソフトウェアを使ってみます。Dockerでサーバを立てていますが、Go製のためバイナリ単体でも動作します。
この記事ではOAuth2プロバイダにGoogle、サーバソフトにnginxを使用しています。
IdPのシークレットを取得
使用するIdPの接続情報(エンドポイントやシークレット)を用意します。有名どころのOAuthのIdPは主要な接続設定がハードコートされているため、サービス名とクライアントID・シークレットを指定するだけで使用できます。デフォルトはGoogleです。
標準対応している主なOAuth2プロバイダ
- GitHub
- GitLab
- Microsoft Entra ID
公式ドキュメントに標準対応しているOAuth2プロバイダの一覧が掲載されています。OAuth2-ProxyはOIDCに対応しているので、一覧に無ければOIDCで接続します。
IdPの準備(Googleの例)
Googleをプロバイダとする場合は、Google Cloud Platform(GCP)のコンソールでシークレットを取得します。クレジットカード情報を登録したり、無料枠の利用開始を行ったりしなくても、IdPとして利用することはできます。
- GCPのコンソールからプロジェクトを作成する
- 「新しいプロジェクトの作成」ボタンから指示に従いプロジェクトを作成
- OAuth同意画面を構成する
- コンソールの左側メニューの「APIとサービス」→「OAuth同意画面」を開き、必要事項を入力します
- Google Workspaceの場合は組織内の認証用に「内部」という選択肢も使えます(組織外のユーザでは認証できない代わりに、全機能が審査なしで使用可能です)
- クライアントIDを発行する
- コンソールの左側メニューの「APIとサービス」→「認証情報」を開き、中央上の「認証情報を作成」→「OAuth クライアントID」の遷移先で必要事項を入力してクライアントIDを発行します
- 「アプリケーションの種類」は「ウェブアプリケーション」
- 「承認済みの JavaScript 生成元」はアプリのURL(末尾にスラッシュなし)
- 「承認済みのリダイレクト URI」はアプリのURLに
/oauth2/callback
を加えたもの
- 発行されたクライアントIDとシークレットをメモする
- 表示されたシークレットは二度と表示されないため、シークレットの再発行が必要です
- コンソールから作成したクライアントIDのページを開き、「クライアントシークレット」セクションの「Add secret」ボタンで新しいシークレットを発行できます
- 古いシークレットは必要なければ削除しましょう
- 表示されたシークレットは二度と表示されないため、シークレットの再発行が必要です
サーバ構築
今回の手順ではDockerを使用していますが、前述の通りGo製のソフトウェアであるため、バイナリ単体でも動作します。単体のバイナリはGitHubのReleaseページで公開されています。
compose.yml
公式のイメージはquay.io
にあります。Docker Hubにもイメージがありますが、そちらはメンテされておらず非常に古いイメージのため使わないほうが良さそうです。
内部のリバースプロキシでアクセスするため、OAuth2-Proxyコンテナのポートを開ける必要はありません。Dockerネットワークは、それぞれの環境に合わせて適宜設定します。
services:
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:latest
container_name: oauth2
volumes:
- ./oauth2-proxy.cfg:/etc/oauth2-proxy.cfg:ro
command:
- --config=/etc/oauth2-proxy.cfg
expose:
- 4180
restart: always
oauth2-proxy.cfg
公式ドキュメントに書いてあるとおりに設定します。
Cookieシークレットの設定が必要なため、ランダムな文字列を作ってコピーしておきます。32文字のBase64でエンコードされた文字列(URLエンコード向けに+
は-
、/
は_
に置き換えたもの)であれば作成方法問いません。
公式ドキュメントには多種多様な生成方法が記載されています。多くの場合は以下3パターンのいずれかで事足りるでしょう。
# Bash
dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64 | tr -d -- '\n' | tr -- '+/' '-_' ; echo
# OpenSSL
openssl rand -base64 32 | tr -- '+/' '-_'
# PowerShell
Add-Type -AssemblyName System.Web
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Web.Security.Membership]::GeneratePassword(32,4))).Replace("+","-").Replace("/","_")
設定ファイルには、ひとまず以下の内容を記載します。client_id
・client_secret
には上述のOAuth2のクライアントID・シークレット、cookie_secret
には先程作成したBase64のランダム文字列をセットします。他の設定値は必要に応じて変更します。
http_address = "0.0.0.0:4180"
reverse_proxy = true
email_domains = [
"*"
]
client_id = ""
client_secret = ""
cookie_secret = ""
nginxの設定
設定は一例です。
server {
listen 443 ssl;
server_name app.example.com
# HTTPS鍵などは省略
location /oauth2/ {
proxy_pass http://oauth2:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
location = /oauth2/auth {
proxy_pass http://oauth2:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Uri $request_uri;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
location / {
auth_request /oauth2/auth;
error_page 401 =403 /oauth2/sign_in;
# アプリへ
proxy_pass http://app;
#root /app;
}
}
色々リバースプロキシの設定が記載されていますが、ポイントは以下の3点です。
- OAuth2-Proxyを
/oauth2
にリバースプロキシとして展開 - 通常のアクセス(
root
やproxy_pass
)の前にauth_request
でOAuth2-Proxyにリクエストを送信 auth_request
の結果が401の場合はOAuth2-Proxyのログイン画面へ遷移
動作確認
nginxのserver_name
のサーバ(上の設定例ではapp.example.com
)にアクセスすると、以下のログイン画面が表示されます。「Sign in with サービス名」を押してIdPの認証画面からログインを行い、ログイン後は通常のアクセス(OAuth2-Proxyを設定しない状態)と同じWebページが表示されれば設定完了です。
認証結果のユーザ識別情報を使う
上記のサンプルではユーザの認証を行うことができますが、あくまで認証が通るかをチェックするだけでアプリ側ではユーザを判別できません。アプリ側で認証結果のユーザ情報を扱うには、オプションを設定したり、プロキシのHTTPヘッダを受け渡したりする必要があります。
まず設定ファイルに以下の設定を追加します。認証に成功するとHTTPヘッダにX-AUTH-REQUEST-*
が含まれるようになります。
set_xauthrequest = true
あとはnginx側で認証プロキシのヘッダを受け渡せば、ユーザの識別情報が取得できます。
location / {
auth_request /oauth2/auth;
error_page 401 =403 /oauth2/sign_in;
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# ...
}
これで、アプリ側はリクエストヘッダのX-User
にユーザ情報、X-Email
にメールアドレスが含まれるようになるので、ユーザを識別できるようになります。
各種エンドポイントについて
公式ドキュメントに、各種エンドポイントの情報が記載されています。主要なものは以下です。
/oauth2/sign_in
- OAuth2-Proxyのログインページを開くURL/oauth2/start
- OAuth2-Proxyのログインページを開かずに直接IdPの認証画面を開くURL/oauth2/callback
- IdPの認証後に、IdPから戻ってくるURL/oauth2/sign_out
- サインアウト(セッションCookieを削除)するURL/oauth2/auth
- 認証済みの場合はHTTP 202、未認証の場合はHTTP 401を返すURL
OAuth2-Proxyのログインページを経由する場合は/oauth2/sign_in
、直接IdPのログインページを開く場合は/oauth2/start
と使い分けることになります。
カテゴリ: ソフトウェア