保護対象のリソースやアプリケーションへのアクセスを要求するユーザーを認証し、NGINX PlusとNGINXでそれらを保護する方法について、お客さまからよくお問い合わせをいただきます。このたび、このような認証システムのリファレンス実装を公開し、GitHubのNGINX, Inc.リポジトリで提供します。本記事では、この実装の仕組み、インストール方法、および独自の認証システムを構築する際のモデルとして活用する方法について説明します。
このソリューションでは、ngx_http_auth_request_moduleモジュールを活用します。このモジュールはNGINX PlusおよびNGINXで認証リクエストを外部サービスに転送します。リファレンス実装では、そのサービスとしてldap‑authというデーモンを使用します。このデーモンはPythonで記述され、Lightweight Directory Access Protocol(LDAP)認証サーバーと通信します。デフォルトではOpenLDAPを使用しますが、Microsoft® Windows® Server Active Directoryのデフォルト構成(2003および2012の両バージョン)でもldap‑authデーモンをテスト済みです。
ldap‑authデーモンは、独自の「コネクター」アプリを開発する際のモデルとして利用できます。このアプリは、別の言語で記述したり、異なる認証システムと組み合わせて導入したり、その両方を行ったりできます。NGINX Professional Servicesチームは、このような適応を支援します。
注記:
- このリファレンス実装は本番環境での使用を目的としたものではなく、独自に実装する際のモデルとしてのみ提供されています。
- 読みやすさを考慮し、この記事では以降NGINX Plusと表記しますが、このリファレンス実装はNGINX Open Sourceでも動作します。前提となるhttp_auth_requestモジュールは、NGINX Plusパッケージとビルド済みNGINXバイナリの両方に含まれています。
リファレンス実装における認証の仕組み
認証を実行するため、http_auth_requestモジュールはldap‑authデーモンにHTTPサブリクエストを送信します。ldap‑authデーモンは仲介役として、このサブリクエストをLDAPサーバー向けに解釈します。NGINX Plusとの通信にはHTTPを使用し、LDAPサーバーとの通信には適切なAPIを使用します。
このリファレンス実装に関心をお持ちの場合、認証を必須にすることで保護したいアプリケーションやその他のリソースがすでにあることを前提としています。ただし、リファレンス実装を簡単にテストできるように、Pythonで記述され、ポート 9000でリッスンするサンプルのバックエンドデーモンを提供しています。このデーモンは、ユーザー認証情報の入力を求め、その情報に基づいてCookieを作成することで、テスト時に実際のHTTPアプリケーションの代わりとして使用できます。

以下では、リファレンス実装における認証プロセスを手順ごとに説明します。詳細は、構成ファイル「nginx-ldap-auth.conf」の設定によって決まります。後述の「リファレンス実装の構成」を参照してください。手順の後にあるフローチャートは、このプロセスをまとめたものです。
- クライアントは、NGINX Plusがリバースプロキシとして機能するサーバー上でホストされている保護対象リソースに、HTTPリクエストを送信します。
- NGINX Plus(具体的にはhttp_auth_requestモジュール)はリクエストをldap‑authデーモンに転送します。認証情報が提供されていないため、ldap‑authデーモンはHTTPコード
401を返します。 - NGINX Plusはリクエストをhttp://backend/loginに転送します。これはバックエンドデーモンに対応します。また、元のリクエストURIを、転送するリクエストの
X-Targetヘッダーに書き込みます。 - バックエンドデーモンは、クライアントにログインフォームを送信します(このフォームは、デーモンのPythonコードで定義されています)。
error_pageディレクティブの設定に従い、NGINXはログインフォームのHTTPコードを200に設定します。 - ユーザーがフォームの[Username]フィールドと[Password]フィールドに入力し、[Login]ボタンをクリックします。フォーム内のコードに従って、クライアントはHTTP
POSTリクエストを生成し、宛先を/loginに指定します。NGINX Plusは、このリクエストをバックエンドデーモンに転送します。 - バックエンドデーモンは、
username:password形式の文字列を作成してBase64エンコードを適用し、エンコードされた文字列を値に設定したnginxauthというCookieを生成してクライアントに送信します。また、JavaScriptによるCookieの読み取りや操作を防ぐために、httponlyフラグを設定します(クロスサイトスクリプティング[XSS]脆弱性への対策)。 - クライアントは元のリクエスト(ステップ1)を再送信します。このとき、HTTPヘッダーの
CookieフィールドにCookieを含めます。NGINX Plusは、ステップ2と同様に、リクエストをldap‑authデーモンに転送します。 - ldap‑authデーモンはCookieをデコードし、認証リクエストでユーザー名とパスワードをLDAPサーバーに送信します。
- 次の処理は、LDAPサーバーによるユーザー認証が成功したかどうかによって異なります:
- 認証に成功すると、ldap‑authデーモンはHTTPコード「
200」をNGINX Plusに送信します。NGINX Plusはバックエンドデーモンにリソースをリクエストします。リファレンス実装では、バックエンドデーモンは次のテキストを返します。
- 認証に成功すると、ldap‑authデーモンはHTTPコード「
Hello, world! Requested URL: URL- The nginx-ldap-auth.confファイルには、認証試行の結果をキャッシュするためのディレクティブが含まれています。キャッシュを無効にするには、以下の「キャッシュ」を参照してください。
- 認証に失敗すると、ldap‑authデーモンはHTTPコード「
401」をNGINX Plusに送信します。NGINX Plusはリクエストを再度バックエンドデーモンに転送し(ステップ3と同様)、この処理が繰り返されます。

コンポーネントのインストール
リファレンス実装に付属するNGINX Plus構成ファイル「nginx-ldap-auth.conf」では、LDAPサーバー以外のすべてのコンポーネント(NGINX Plus、クライアント、ldap‑authデーモン、バックエンドデーモン)が同じホスト上で実行されるように構成されています。これはテスト用途には十分です。テスト時には、LDAPサーバーも同じホスト上で実行できます。
実際のデプロイでは、通常、バックエンドアプリケーションと認証サーバーをそれぞれ別のホストで実行し、NGINX Plusを3台目のホストで実行します。ほとんどの場合、ldap-authデーモンは多くのリソースを消費しないため、NGINX Plusホストまたは任意の別のホストで実行できます。
- 次のGitHubリポジトリのクローンを作成します。
- NGINX Plusがまだ稼働していない場合は、お使いのオペレーティングシステム向けの手順に従ってインストールします。
- LDAPサーバーがまだ稼働していない場合は、インストールして設定します。デフォルトでは、ldap‑authデーモンはOpenLDAPと通信しますが、Microsoft Windows Active Directory 2003および2012もサポートされています。リファレンス実装のテストにのみLDAPサーバーを使用する場合は、GitHubで提供されているOpenLDAPサーバーのDockerイメージを使用するか、Ubuntu 16.04にOpenLDAPとphpLDAPadminをインストールして設定する方法などの手順に従ってサーバーをセットアップできます。Base DN、Bind DN、Bindパスワードに設定した値を控えておいてください。これらの値は、リファレンス実装の設定でNGINX設定ファイルに入力します。
- ldap‑authデーモンを実行するホストに、以下の追加ソフトウェアをインストールします。オープンソースリポジトリからソフトウェアをダウンロードするのではなく、オペレーティングシステムに付属するバージョンの使用を推奨します。
- Pythonバージョン2。バージョン3はサポートされていません。
- Python LDAPモジュール「python‑ldap」(python-ldap.orgオープンソースプロジェクトによって作成)。
- リポジトリのクローンから、以下のファイルを指定されたホストにコピーします:
- nginx-ldap-auth.conf – リファレンス実装のテストに必要な最小限のディレクティブを含むNGINX Plus構成ファイルです。NGINX Plusホストにインストールしてください(従来の構成方式を使用している場合は、/etc/nginx/conf.dディレクトリにインストールします)。構成の競合を避けるため、NGINX Plusとともにインストールされたデフォルトの構成ファイルは、必ず移動するか名前を変更してください。
- nginx-ldap-auth-daemon.py – ldap‑authデーモン用のPythonコード。任意のホストにインストールします。
- nginx-ldap-auth-daemon-ctl.sh – デーモンを起動および停止するためのサンプルシェルスクリプトです。ldap‑authデーモンと同じホストにインストールしてください。
- backend-sample-app.py – テスト時にバックエンドアプリケーションサーバーの代わりとして動作するデーモンのPythonコード。任意のホストにインストールしてください。
- 「リファレンス実装の構成」の説明に従って、NGINX Plus構成ファイルを変更します。変更後、
nginx-tコマンドを実行し、ファイルの構文が有効であることを確認します。
root# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful- NGINX Plusを起動します。NGINX Plusがすでに実行中の場合は、次のコマンドを実行して構成ファイルを再読み込みします。
root# nginx -s reload- 該当するホストで次のコマンドを実行し、ldap‑authデーモンとバックエンドデーモンを起動します。
root# nginx-ldap-auth-daemon-ctl.sh start
root# python backend-sample-app.py- Webブラウザを使用してhttp://nginx-server-address:8081にアクセスします。ブラウザに認証フォームが表示されることを確認してください。フォームに入力して送信した後、有効な認証情報に対してサーバーから想定どおりの応答が返されることを確認します。前述のとおり、バックエンドデーモンは次のテキストを返します。
Hello, world! Requested URL: URLリファレンス実装の構成
以下の変更をnginx-ldap-auth.confファイルに加えます。記載のとおり、必須の変更と任意の変更があります。
LDAPサーバー設定
As implemented in nginx-ldap-auth-daemon.pyに実装されているとおり、ldap‑authデーモンはOpenLDAPサーバーと通信し、認証するユーザーアカウントを指定するパラメーターを渡します。Pythonコードを変更せずに済むよう、nginx-ldap-auth.confファイルには、HTTPヘッダーに値を設定するproxy_set_headerディレクティブが含まれています。この値はパラメーターの設定に使用されます。次の表に、パラメーターとヘッダーの対応関係を示します。
LDAPパラメーター HTTPヘッダーbasedn X-Ldap-BaseDNbinddn X-Ldap-BindDNbindpasswd X-Ldap-BindPasscookiename X-CookieNamerealm X-Ldap-Realmtemplate X-Ldap-Templateurl X-Ldap-URL
- (必須)以下のディレクティブで、太字の値をLDAPサーバー環境に適した値に置き換えてください。特に、nginx-ldap-auth.confファイルでは、LDAPSのウェルノウンポートである636を使用している点に注意してください。ポートを389(LDAPのウェルノウンポート)または別のLDAPポートに変更する場合は、プロトコル名も
ldapsからldapに変更してください。
# URL and port for connecting to the LDAP server
proxy_set_header X-Ldap-URL "ldaps://example.com:636";
# Base DN
proxy_set_header X-Ldap-BaseDN "cn=Users,dc=test,dc=local";
# Bind DN
proxy_set_header X-Ldap-BindDN "cn=root,dc=test,dc=local";
# Bind password
proxy_set_header X-Ldap-BindPass "secret";- (OpenLDAPではなくActive Directoryを使用する場合は必須)次のディレクティブのコメントを、以下のように解除します:
proxy_set_header X-Ldap-Template "(SAMAccountName=%(username)s)";- (任意)リファレンス実装ではCookieベースの認証を使用します。代わりにHTTP Basic認証を使用する場合は、次のように以下のディレクティブをコメントアウトします。
#proxy_set_header X-CookieName "nginxauth";
#proxy_set_header Cookie nginxauth=$cookie_nginxauth;- (任意)ldap‑authデーモンがデフォルトでOpenLDAPサーバーに渡す
templateパラメーターの値を変更する場合は、次のようにディレクティブのコメントを解除し、値を変更します。
proxy_set_header X-Ldap-Template "(cn=%(username)s)";- (任意)realm名をデフォルト値(Restricted)から変更する場合は、次のディレクティブのコメントを解除して変更します。
proxy_set_header X-Ldap-Realm "Restricted";バックエンドデーモンのIPアドレス
バックエンドデーモンがNGINX Plusと同じホストで実行されていない場合は、upstream設定ブロックでバックエンドデーモンのIPアドレスを変更します。
upstream backend { server 127.0.0.1:9000;
}ldap‑authデーモンのIPアドレス
ldap‑authデーモンがNGINX Plusと同じホストで実行されていない場合は、次のproxy_passディレクティブのIPアドレスを変更します。
location = /auth-proxy { proxy_pass http://127.0.0.1:8888;
# ...
}NGINXがリッスンするIPアドレスとポート
クライアントがNGINX Plusと同じホストで実行されていない場合は、このlistenディレクティブのIPアドレスを変更します(任意のクライアントからのトラフィックを受け入れるには、アドレスを完全に削除します)。必要に応じて、NGINXがリッスンするポートを8081から変更することもできます。
server { listen 127.0.0.1:8081;
# ...
}キャッシュ
The nginx-ldap-auth.confファイルでは、データと認証情報の両方をキャッシュできます。必要に応じて、以下の設定を変更できます。
- The
proxy_cache_pathディレクティブは、http設定ブロック内にcacheというローカルディスクディレクトリを作成し、メタデータを保存するauth_cacheというゾーン用に共有メモリ内に10 MBを割り当てます。
proxy_cache_path cache/ keys_zone=auth_cache:10m;- 共有メモリゾーンの名前を変更する場合は、ldap-authデーモンにトラフィックを転送する
proxy_cacheディレクティブ(locationブロック内)でも変更する必要があります。
location = /auth-proxy {
proxy_cache auth_cache;
# ...
}- The
proxy_cache_validディレクティブ(locationブロック内のproxy_cacheと同じブロックに記述)は、HTTPコード200または403が付いたキャッシュ済みレスポンスの有効期間を10分に指定します。
location = /auth-proxy { proxy_cache_valid 200 403 10m;
# ...
}キャッシュを無効にするには、これら3つのディレクティブに加えて、proxy_cache_keyディレクティブもコメントアウトします。
認証システムのカスタマイズ
前述のとおり、http_auth_requestモジュールからのリクエストを受け付ける独自のアプリケーションを作成する際に、ldap‑authデーモンをモデルとして使用できます。別の種類(LDAP以外)の認証サーバーと通信するアプリケーションをPythonで作成する場合は、LDAPAuthHandlerを置き換える新しい認証ハンドラークラスを、nginx-ldap-auth-daemon.pyスクリプトに記述します。
セキュリティに関する注意事項
バックエンドデーモンは、Cookie内のユーザー名とパスワードをBase64でエンコードします。Base64は非常に脆弱な難読化方式であるため、認証情報が抽出され、悪用されるおそれがあります。認証を実効性のあるものにするには、バックエンドアプリケーションで、より高度な暗号化を使用する必要があります。
著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります