Skip to content

ローカル環境でOAuth2 ProxyとKeycloakを使用した認証を実装する

はじめに

業務で OAuth2 Proxy を使用した認証をサイトに導入する機会がありました。この記事では、OAuth2 Proxy で学習した内容をまとめます。

docker-compose

成果物

https://github.com/kntks/blog-code/tree/main/2024/09/oauth2-proxy-basic

OAuth2 Proxyとは

OAuth2 Proxy は既存のアプリケーションに変更を加えることなく、認証を追加することができるリバースプロキシです。

複数の認証プロバイダーと連携することができ、認証プロバイダーとして、Google、GitHub、GitLab、Microsoft、Keycloak などをサポートしています。

セッションストレージは、Cookie、Redis をサポートしています。

環境構築

バージョン

node は2024年4月現在のLTSかつ最新である、v20.12.2を使用します。

バージョン
MacSonoma 14.5
Docker26.0.0
Docker Composev2.24.5
Keycloak25.0.4
Terraform1.9.5

Keycloak のセットアップ

Docker Compose で Keycloak を定義します。

compose.yaml
services:
keycloak:
image: quay.io/keycloak/keycloak:25.0.4
container_name: keycloak
ports:
- target: 8080
published: 8080
protocol: tcp
mode: host
environment:
- KEYCLOAK_ADMIN=admin
- KEYCLOAK_ADMIN_PASSWORD=admin
command: ["start-dev"]
volumes:
- ./keycloak:/opt/keycloak/data
healthcheck:
# https://gist.github.com/sarath-soman/5d9aec06953bbd0990c648605d4dba07?permalink_comment_id=5120472#gistcomment-5120472
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
interval: 10s
timeout: 5s
retries: 3

Keycloak を起動します。

Terminal window
docker compose up keycloak

Keycloakの設定

Terraformを実行する

Terraform を使用するために Keycloak に terraform クライアント を作成します。

Keycloak で terraform クライアントを作成したら、クライアントシークレットをコピーし、以下ように、client_id, client_secret, url を設定します。

terraform/terraform.tfvars
client_id = "terraform"
client_secret = "xxxxxxxxxxxxxxxxx"
url = "http://localhost:8080"

terraform apply で Keycloak の設定を行います。

Terminal window
cd terraform
terraform init
terraform plan
terraform apply -auto-approve

クライアントの設定

今回、Terraform で作成したクライアントは、myapp という名前です。クライアントの設定は以下の通りです。
Valid redirect URIs には、OAuth2 Proxy のリダイレクト先の URL を設定します。 keycloak-clients-myapp-1

Authentication flow は、Standard flowDirect Grant を選択します。

keycloak-clients-myapp-2

クライアントの設定に Advanced タブがあります。ここにある Proof Key for Code Exchange Code Challenge Method は、S256 を選択します。

keycloak-clients-myapp-advanced-1 keycloak-clients-myapp-advanced-2

ユーザーの設定

Username: myuser、Password: myuser でユーザーを作成しています。

keycloak-users-myuser

nginxの設定

OAuth2 Proxy のドキュメントにある nginx の設定例を参考にし、Docker Compose で起動します。

compose.yaml
nginx:
container_name: nginx
image: nginx:1.27
ports:
- "80:80"
volumes:
- "./nginx.conf:/etc/nginx/conf.d/default.conf"
depends_on:
oauth2-proxy:
condition: service_healthy
nginx.conf
server {
listen 80;
server_name localhost;
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;
proxy_pass http://httpbin/;
}
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;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
}
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 X-Forwarded-Uri $request_uri;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
}

OAuth2 Proxyの設定

OAuth2 Proxy の設定方法は3つあります。

  1. config ファイル
  2. コマンドライン引数
  3. 環境変数

この記事では、基本設定はコマンドライン引数で行い、シークレットは環境変数で設定します。

まず、Docker Compose で OAuth2 Proxy を定義します。

compose.yaml
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0-alpine
container_name: oauth2-proxy
command:
- --http-address=0.0.0.0:4180
- --upstream=http://httpbin:80
- --scope=openid email profile
- --oidc-audience-claim=azp
- --provider=keycloak-oidc
- --client-id=myapp
- --redirect-url=http://localhost:4180/oauth2/callback
- --oidc-issuer-url=http://keycloak:8080/realms/myrealm
- --email-domain=*
- --code-challenge-method=S256
- --insecure-oidc-allow-unverified-email=true
ports:
- "4180:4180"
- "443:443"
environment:
# productionで使用するときはシークレットを環境変数として設定する
OAUTH2_PROXY_CLIENT_SECRET: ""
OAUTH2_PROXY_COOKIE_SECRET: ""
# Keycloakが先に起動していないとエラーが発生するためdepends_onを設定
depends_on:
keycloak:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget --quiet --spider localhost:4180/ping"]
interval: 10s
timeout: 5s
retries: 3

参考:

起動オプションについて

upstream は、認証されたリクエストが転送される先の URL です。今回は、httpbin を使用します。

以下のようなエラーが出る場合、--insecure-oidc-allow-unverified-email=true を追加します。

Terminal window
Error redeeming code during OAuth2 callback: email in id_token isn't verified

引用:https://github.com/oauth2-proxy/oauth2-proxy/issues/1712#issuecomment-1173659513

以下のようなエラーが出る場合、--oidc-audience-claim=azp を追加します。

Terminal window
Error creating session during OAuth2 callback: audience from claim aud with value [account] does not match with any of allowed audiences map[myapp:{}]`

環境変数について

OAuth2 Proxy は、すべてのコマンドライン引数は、環境変数として指定できます。環境変数には、OAUTH2_PROXY_ を先頭につけ、大文字にし、ハイフン - をアンダースコア _ に置き換えます。

Keycloak で作成した myapp のクライアントシークレットと、OAuth2 Proxy の Cookie シークレットは環境変数として設定します。(Cookie シークレットは、python スクリプトを実行し、生成しています。)

参考:Environment variables - OAuth2 Proxy

動作確認

Web ブラウザで http://localhost にアクセスし、Keycloak のログイン画面が表示されることを確認します。

oauth2-proxy-login

しかし、ブラウザは http://keycloak:8080/realms/myrealm/protocol/openid-connect/auth?xxxxxx になり、サイトにアクセスできません。

これは、OAuth2 Proxy の起動オプションに --oidc-issuer-url=http://keycloak:8080/realms/myrealm としているためです。

mac で開いた web ブラウザから http://keycloak にアクセスするためには、/etc/hosts を編集する必要があります。

/etc/hostsに設定を追加する

/etc/hosts
$ cat /etc/hosts
...
127.0.0.1 keycloak
# End of section

再度アクセスする

もう一度、Web ブラウザで http://localhost にアクセスしてみると Keycloak のログイン画面が表示されます。

Username: myuser、Password: myuser でログインすると、httpbin のページが表示されます。 httpbin

まとめ

OAuth2 Proxy で Keycloak を使用した認証を実装する方法を学びました。

Keycloak と連携するときにいくつかエラーが出ますが、insecure-oidc-allow-unverified-email、oidc-audience-claim、などの設定を追加することで解決できます。もし、エラーが出た場合は、ドキュメントを参照して設定を追加してみてください。