雑記帳

整理しない情報集

更新情報

OAuth2-Proxyを使ってみる

公開日:

カテゴリ: ソフトウェア

OAuth2/OpenID Connect(OIDC)の認証プロキシである、OAuth2-Proxyというソフトウェアを使ってみます。Dockerでサーバを立てていますが、Go製のためバイナリ単体でも動作します。

この記事ではOAuth2プロバイダにGoogle、サーバソフトにnginxを使用しています。

IdPのシークレットを取得

使用するIdPの接続情報(エンドポイントやシークレット)を用意します。有名どころのOAuthのIdPは主要な接続設定がハードコートされているため、サービス名とクライアントID・シークレットを指定するだけで使用できます。デフォルトはGoogleです。

標準対応している主なOAuth2プロバイダ

  • GitHub
  • GitLab
  • Google
  • Microsoft Entra ID

公式ドキュメントに標準対応しているOAuth2プロバイダの一覧が掲載されています。OAuth2-ProxyはOIDCに対応しているので、一覧に無ければOIDCで接続します。

IdPの準備(Googleの例)

Googleをプロバイダとする場合は、Google Cloud Platform(GCP)のコンソールでシークレットを取得します。クレジットカード情報を登録したり、無料枠の利用開始を行ったりしなくても、IdPとして利用することはできます。

  1. GCPのコンソールからプロジェクトを作成する
    • 「新しいプロジェクトの作成」ボタンから指示に従いプロジェクトを作成
  2. OAuth同意画面を構成する
    • コンソールの左側メニューの「APIとサービス」→「OAuth同意画面」を開き、必要事項を入力します
    • Google Workspaceの場合は組織内の認証用に「内部」という選択肢も使えます(組織外のユーザでは認証できない代わりに、全機能が審査なしで使用可能です)
  3. クライアントIDを発行する
    • コンソールの左側メニューの「APIとサービス」→「認証情報」を開き、中央上の「認証情報を作成」→「OAuth クライアントID」の遷移先で必要事項を入力してクライアントIDを発行します
    • 「アプリケーションの種類」は「ウェブアプリケーション」
    • 「承認済みの JavaScript 生成元」はアプリのURL(末尾にスラッシュなし)
    • 「承認済みのリダイレクト URI」はアプリのURLに/oauth2/callbackを加えたもの
  4. 発行されたクライアント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_idclient_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にリバースプロキシとして展開
  • 通常のアクセス(rootproxy_pass)の前にauth_requestでOAuth2-Proxyにリクエストを送信
  • auth_requestの結果が401の場合はOAuth2-Proxyのログイン画面へ遷移

動作確認

nginxのserver_nameのサーバ(上の設定例ではapp.example.com)にアクセスすると、以下のログイン画面が表示されます。「Sign in with サービス名」を押してIdPの認証画面からログインを行い、ログイン後は通常のアクセス(OAuth2-Proxyを設定しない状態)と同じWebページが表示されれば設定完了です。

OAuth2-Proxy ログイン画面

認証結果のユーザ識別情報を使う

上記のサンプルではユーザの認証を行うことができますが、あくまで認証が通るかをチェックするだけでアプリ側ではユーザを判別できません。アプリ側で認証結果のユーザ情報を扱うには、オプションを設定したり、プロキシの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と使い分けることになります。

カテゴリ: ソフトウェア