メインコンテンツにスキップ

Nginx CAS 認証と SSO: Casdoor で Nginx 背後のあらゆるアプリを保護

· 1分読む
Yang Luo
Casdoor Maintainer

オープンソースの nginx には CAS モジュールがないため、「nginx CAS 認証」は実際には 2 つのセットアップのいずれかを意味します。nginx が auth_request モジュールを通じてすべてのリクエストについて認証サービスに問い合わせ、Casdoor が応答する場合(直接 /api/forward-auth で、または casdoor-forward-auth や OAuth2 Proxy 経由)、あるいは nginx 背後のアプリ自体が CAS クライアントであり、Casdoor の CAS サーバーエンドポイントと通信する場合です。nginx を再構築できる場合は、Casdoor nginx モジュールが nginx 内部でログインを実行し、nginx を実行したくない場合は、Casdoor のサイトプロキシをアプリの前に配置できます。どの場合も、ログインページは Casdoor のものなので、1 つの SSO セッション、1 組の権限、1 つの MFA ポリシーですべてのアプリをカバーします。

オプションの概要​

オプションログインロジックの実行場所nginx に必要なものCasdoor とのプロトコルアクセス許可されるユーザー選択するタイミング
Casdoor forward authCasdoor, /api/forward-authauth_requestアプリドメイン上の Casdoor セッション Cookieアプリケーション権限、少なくとも 1 分ごとに再チェックnginx が既に前面にあり、余計なサービスを追加したくない場合
casdoor-forward-auth小さな Go サービスauth_requestOIDC 認可コードCasdoor のロールまたはグループ、ロケーションごと多くのサブドメインで共有される 1 つのセッション Cookie が必要な場合
OAuth2 ProxyOAuth2 Proxyauth_requestOIDCallowed_groups、メールドメイン既に OAuth2 Proxy を運用している場合
casdoor-nginx-modulenginx そのものコンパイル済みモジュールPKCE を使用した OIDC、ローカルで検証されたトークンアプリのサインイン済みユーザーなら誰でも追加プロセスなし、さらに API 用のベアラートークンが必要な場合
アプリ内の CAS クライアントアプリなし、単にプロキシするだけCAS 1.0, 2.0, 3.0アプリが属性をどう扱うかによるアプリが既に CAS をサポートしている場合
Casdoor site proxyCasdoor (ポート 80 および 443)nginx なしCasdoor セッションアプリケーション権限アプリの前に他の何も存在しない場合

最初の 3 つの違いは、nginx のサブリクエストに応答するのが誰であるかだけです。

「nginx CAS モジュール」が存在しない理由​

CAS はアプリケーションと CAS サーバー間の会話です。アプリはブラウザをサーバーの /login に service URL で送信し、その URL でサービスチケットを受け取り、サーバー間呼び出しでチケットを検証します。Apache httpd には、Web サーバーレベルでこれを行うための Apereo の mod_auth_cas がありますが、nginx の公式モジュールには CAS モジュールがありません。nginx の場合、標準的な構成要素は ngx_http_auth_request_module です。リクエストを提供する前に、nginx はあなたが選んだ URL へサブリクエストを送信します。2xx の応答はリクエストを通し、401 または 403 はそのコードで拒否し、他のコードはエラーとなります。

ユーザーにとって、CAS ログインと OIDC ログインは同じように見えます。Casdoor のログインページへのリダイレクトと戻りです。したがって、CAS サポートのないアプリの場合、実用的な答えは nginx レベルでの OIDC です。プロトコルの背景情報: What is CAS。

auth_request モジュールはデフォルトでは組み込まれていません。コンパイル時に --with-http_auth_request_module を追加するか、パッケージ化された nginx で nginx -V を確認してください。

オプション 1: Casdoor 独自の forward auth​

Casdoor は nginx のサブリクエスト自体に応答できます。Casdoor に Site を追加し、その Domain をアプリのドメインに、Casdoor app をユーザーがサインインするアプリケーションに設定し、そのアプリケーションの Redirect URLs に https://app.example.com/caswaf-handler を追加します。トラフィックは Casdoor を通過しないため、ホストとポートは空のままにできます。

auth_request はリダイレクトを追跡できないため、Casdoor は未認証のサブリクエストに対して 401 で応答し、ログイン URL を Location に設定します。nginx はこれを 302 に変換します:

server {
server_name app.example.com;

proxy_buffer_size 16k;
proxy_buffers 8 16k;

location = /caswaf-handler {
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
}

location = /casdoor-auth {
internal;
proxy_pass http://casdoor:8000/api/forward-auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

location / {
auth_request /casdoor-auth;
auth_request_set $casdoor_redirect $upstream_http_location;
auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_email $upstream_http_x_forwarded_email;
auth_request_set $casdoor_groups $upstream_http_x_forwarded_groups;
error_page 401 =302 $casdoor_redirect;

proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Email $casdoor_email;
proxy_set_header X-Forwarded-Groups $casdoor_groups;
proxy_pass http://app:3000;
}
}

バックエンドは X-Forwarded-User、X-Forwarded-Email、および X-Forwarded-Groups でユーザーを受信します。アクセスはアプリケーションに対する permission に従い、少なくとも 1 分ごとに再チェックされるため、無効化されたユーザーはすぐにアクセスを失います。サイトの Public paths(webhooks、ヘルスチェック)はログインなしで通過します。

参照として Using your own reverse proxy を、Grafana のウォークスルーとして Add login to an app without OIDC をご覧ください。アプリは nginx 経由でのみアクセス可能にする必要があります。さもないと、誰でも X-Forwarded-User を自分で送信できてしまいます。

オプション 2: casdoor-forward-auth または OAuth2 Proxy​

各アプリ用に Casdoor サイトを作成したくない場合は、Casdoor に対して OIDC フローを実行しサブリクエストに応答する小さなサービスを実行してください。

casdoor-forward-auth は、このための Casdoor のスタンドアロンサービスです(traefik-casdoor-auth を置き換えました)。サーバー状態を持たず、ユーザーを署名付きクッキーに保持します。nginx 向けには /verify を公開しており、ID ヘッダー付きで 200 を返すか 401 を返します。nginx は 401 でその /login にリダイレクトします:

server {
server_name app.example.com;

location = /_casdoor_verify {
internal;
proxy_pass http://casdoor-forward-auth:9999/verify?groups=built-in/dev;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}

location @casdoor_login {
return 302 https://auth.example.com/login?rd=$scheme://$http_host$request_uri;
}

location / {
auth_request /_casdoor_verify;
error_page 401 = @casdoor_login;

auth_request_set $casdoor_user $upstream_http_x_forwarded_user;
auth_request_set $casdoor_roles $upstream_http_x_forwarded_roles;
proxy_set_header X-Forwarded-User $casdoor_user;
proxy_set_header X-Forwarded-Roles $casdoor_roles;
proxy_pass http://app:8080;
}
}

verify URL の roles および groups クエリパラメータにより、各アプリを Casdoor ロールの保持者またはグループのメンバーに制限できます。それらを持たないサインイン済みユーザーは 403 を受けます。ロールとグループはログイン時に読み込まれセッション中に保持されるため、変更はユーザーが再度サインインした後で適用されます。COOKIE_DOMAIN=example.com を設定すると、1 つのログインですべてのサブドメインをカバーできます。同じサービスを Traefik 向けに解説しているのが Traefik guide です。

OAuth2 Proxy も同様に機能し、provider = "oidc" と oidc_issuer_url を Casdoor URL に設定します。その /oauth2/auth エンドポイントは 202 または 401 を返し、user と email ヘッダーには --set-xauthrequest が必要です。また、その nginx 例における転送ヘッダー処理には --reverse-proxy が必要です。重要な Casdoor 設定が 2 つあります。JWT-Standard トークン形式を使用してください。そうしないと未検証のメールアドレスを持つユーザーが拒否されます。また、profile スコープを保持してグループとロールが返されるようにしてください。OAuth2 Proxy with Casdoor と nginx community edition guide を参照してください。

オプション 3: Casdoor nginx モジュール​

casdoor-nginx-module は、別のプロセスなしに nginx 内でログインを行います。セッションがないブラウザは Casdoor のログインページに移動します。モジュールは PKCE でコードを交換し、Casdoor 証明書に対してアクセストークンの署名、exp、aud、タイプを検証し、ユーザーを署名付きクッキーに保持します。API クライアントは Casdoor アクセストークンを伴う Authorization: Bearer を送信でき、Casdoor への呼び出しなしにローカルで検証されます。

これには nginx 1.22 以上と OpenSSL 1.1.1 以上が必要です。既に実行している nginx に対して動的モジュールとしてビルドできます:

./configure --with-compat --with-http_ssl_module --add-dynamic-module=/path/to/casdoor-nginx-module
make modules
cp objs/ngx_http_casdoor_module.so /etc/nginx/modules/
load_module modules/ngx_http_casdoor_module.so;

events {}

http {
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/app.crt;
ssl_certificate_key /etc/nginx/app.key;

casdoor_endpoint https://door.example.com;
casdoor_client_id <client-id>;
casdoor_client_secret <client-secret>;
casdoor_certificate /etc/nginx/casdoor.pem;
casdoor_redirect_uri https://app.example.com/casdoor/callback;
casdoor_token_location /_casdoor/token;

location = /_casdoor/token {
internal;
proxy_pass https://door.example.com/api/login/oauth/access_token;
proxy_ssl_server_name on;
}

location = /casdoor/callback {
casdoor_callback;
}

location / {
casdoor on;
proxy_set_header X-User $casdoor_user;
proxy_pass http://127.0.0.1:3000;
}
}
}

プレースホルダーをアプリの値に置き換えてください。セッションは期限切れになるまで Casdoor で再チェックされないため、ユーザーの無効化はセッション終了時に有効になります。casdoor_session_timeout はそのウィンドウを短縮します。このモジュールにはロールやグループのチェックがありません。NGINX Plus では、代わりに the NGINX Plus guide を参照してください。

オプション 4: アプリが CAS クライアントである場合​

アプリがすでに CAS をサポートしている場合(例えば Apereo CAS クライアントを使用した Java アプリなど)、nginx は単にプロキシし、Casdoor が CAS サーバーとなります。各 Casdoor アプリケーションには /cas/<organization>/<application> の下に CAS エンドポイントがあります: /login と /logout、CAS 1.0 用の /validate、2.0 用の /serviceValidate、/proxyValidate、/proxy、3.0 用の /p3/serviceValidate と /p3/proxyValidate、そして SAML 1.1 検証用の /samlValidate。

アプリはブラウザを /login?service=... に送り、Casdoor は ticket=ST-... を付けて戻り、アプリはそれを検証します:

curl "https://door.example.com/cas/my-org/my-app/p3/serviceValidate?ticket=ST-1234567890&service=https%3A%2F%2Fapp.example.com%2F"

成功時の応答は cas:user と email、表示名、memberOf などの属性を含む cas:serviceResponse です。JSON を得るために format=json を追加してください。よくある落とし穴:

  • すべての service URL はアプリの Redirect URLs にリストされている必要があり、検証時の service はチケットが発行されたものと完全に一致する必要があります。
  • チケットは一度だけ有効で、5 分後に期限切れになります。
  • チケットはメモリ内に保持されるため、複数の Casdoor レプリカがある場合、検証はチケットを発行したレプリカに到達する必要があります(スティッキーセッション)。
  • nginx の後ろでは、Host と X-Forwarded-Proto を渡して、アプリがブラウザが見たものと同じ service URL を構築するようにしてください。

Java クライアントの各 CAS バージョン用のフィルタを含むセットアップについては、Connect a CAS client を参照してください。

オプション 5: Casdoor をリバースプロキシにする​

アプリの前に何も存在しない場合、Casdoor は直接サーブできます。サイトが存在すれば、そのサイトプロキシは gatewayHttpPort と gatewayHttpsPort(デフォルトでは 80 と 443)で待ち受けます。サイトの Host をバックエンドに設定し、証明書を選択し、Casdoor app を設定し、DNS を Casdoor に向けます。ログイン、権限、ヘッダーの動作はオプション 1 と同じです。Sites を参照してください。

MFA の追加​

MFA には nginx やアプリの変更は不要です。Casdoor は OAuth 認可コードや CAS サービスチケットを発行する前に、独自のサインインページで第二要素を要求するため、上記のすべてのオプションで利用可能です。ユーザーは TOTP アプリ、SMS、メール、RADIUS をリカバリコードとともに使用でき、管理者は MFA items を使って組織全体で MFA を必須にできます。これは「CAS 2FA」にも回答します。Casdoor を CAS サーバーとして使用する場合、第二要素は CAS ログインの一部となります。

MFA はログイン時に強制され、リクエストごとではありません。nginx や forward-auth サービスがすでに保持しているセッションは期限切れまで続き、組織の MFA remember time により、信頼できるデバイスで一時的に第二要素をスキップできます。MFA guide と What is MFA を参照してください。

Casdoor にサインインすると、ユーザーは Casdoor に導く他のアプリに新しいパスワード入力を求められずにアクセスできます。Auto signin を使用すると、Casdoor はアカウントピッカーもスキップします。single sign-on と Single logout を参照してください。

どれを選ぶべきか​

  • アプリにログインがなく、nginx がすでに前面にある場合: Casdoor の forward auth。
  • 複数のサブドメインで単一のログインを実現する場合や、場所ごとのロールチェックを行う場合: casdoor-forward-auth (既に利用している場合は OAuth2 Proxy)
  • nginx を自前で構築し、API もベアラートークンで保護する場合: casdoor-nginx-module
  • アプリが CAS に対応している場合: Casdoor の CAS エンドポイント、nginx は単純なプロキシとして使用
  • まだプロキシを導入していない場合: Casdoor のサイトプロキシ

参考資料​