1.6. プロキシーまたは SSL ターミネーターの使用
サーバーがプロキシーの背後にある場合、その環境はクライアントがサーバーのパブリックアイデンティティーとして表示される環境とは異なります。バックエンドサーバーのホスト名が異なる場合があり、別のポートをリッスンするか、またはプロキシーのフロントエンドでクライアントが認識する内容とは異なるプロトコルを使用します。多くの Web アプリケーションでは、これは大きな問題ではありません。通常、ほとんどの問題は、サーバーが自己参照可能な URL を生成する必要がある場合に発生します(クライアントが同じサーバーの異なる URL にリダイレクトするためなど)。サーバーが生成する URL は、クライアントが表示するパブリックアドレスおよびポートと一致する必要があります。
認証プロトコルは、特にホスト、ポート、プロトコル(HTTP/HTTPS など)は特定のポートおよびセキュアなトランスポートで特定のサーバーでターゲットである可能性があるためです。プロキシーは、バックエンド内のパブリックでないサーバーにディスパッチする前にプロキシーによって公開されるフロントエンドで受信される要求を変換するため、プロキシーはこの重要な情報に干渉する可能性があります。同様に、パブリック以外のバックエンドサーバーからの応答は、プロキシーのパブリックフロントエンドからの応答がかのように表示されるように調整する必要がある場合もあります。
この問題には、さまざまなアプローチがあります。SAML は、ホスト、ポート、プロトコル情報に信頼されており、高可用性プロキシー(HAProxy)の背後で SAML を設定しているため、これらの問題に対応する必要があります。そうでないと、設定が失敗する可能性が高くなります(通常は暗号化方法ではありません)。
1.6.1. ホスト名およびポートに関する考慮事項
ホストおよびポートの詳細は、複数のコンテキストで使用されます。
- クライアントによって使用される URL のホストおよびポート。
- HTTP リクエストに挿入されるホスト HTTP ヘッダー(クライアント URL ホストから派生)
- クライアントが接続するフロントエンドプロキシーのホスト名。これは、プロキシーがリッスンする IP アドレスの FQDN です。
- クライアント要求を実際に処理するバックエンドサーバーのホストおよびポート。
- クライアント要求を実際に処理するサーバーの仮想ホストおよびポート。
これらの各値がどのように使用されるかを理解することが重要です。そうでないと、不正なホストおよびポートが使用されるリスクがあり、その結果、トランザクションに関与する参加者を検証できないため、認証プロトコルが失敗することがあります。
まずは、要求を処理するバックエンドサーバーを考慮することができます。これは、ホストとポートが評価され、ほとんどの問題が発生する場所です。
バックエンドサーバーは以下を認識する必要があります。
- 要求の URL(ホストとポートを含む)。
- 独自のホストおよびポート。
Apache は、命名のホストをサポートします。これにより、1 台のサーバーで複数のドメインをホストできます。たとえば、example.com で実行しているサーバーは、example.com と example - 2.com の両方に対して要求を処理する場合があり、これらは仮想ホスト名になります。Apache の仮想ホストは、server 設定ブロック内に設定されます。以下に例を示します。
<VirtualHost> ServerName example.com </VirtualHost>
Apache がリクエストを受信すると、HOST
HTTP ヘッダーからホスト情報を収集し、仮想ホストのコレクションでホストを ServerName
に一致しようとします。
ServerName
ディレクティブは、サーバーがそれ自体を識別するために使用する要求スキーム、ホスト名、およびポートを定義します。ServerName
ディレクティブの動作は、UseCanonicalName
ディレクティブにより変更されます。UseCanonicalName
が有効になっている場合、Apache は ServerName
ディレクティブで指定されたホスト名とポートを使用して、サーバーの正規名を構築します。この名前はすべての自己参照 URL で使用され、CGI の SERVER_NAME
および SERVER_PORT
の値に使用されます。UseCanonicalName
が Off
の場合、Apache はクライアントが提供するホスト名とポート(ある場合)を使用して自己参照可能な URL を形成します。
ServerName
にポートが指定されていない場合、サーバーは受信要求からのポートを使用します。信頼性と予測性を最適化するには、ServerName
ディレクティブを使用して明示的なホスト名およびポートを指定する必要があります。ServerName
が指定されていない場合、サーバーはまずオペレーティングシステムにシステムのホスト名について要求してホストの推測を試みます。これが失敗した場合は、システムで存在する IP アドレスの逆引き参照を実行します。その結果、サーバーがプロキシーの背後にあると誤ったホスト情報が生成されるため、ServerName
ディレクティブの使用が必須になります。
Apache ServerName ドキュメントでは、サーバーがプロキシーの背後にある際に、Server
name ディレクティブでスキーム、ホスト、およびポートを完全に指定する必要があります。
サーバーは、リバースプロキシー、ロードバランサー、SSL オフロードアプライアンスなどの SSL を処理するデバイスの背後で実行される場合があります。この場合は、https:// スキームと、クライアントが ServerName ディレクティブで接続するポート番号を指定して、サーバーが正しい自己参照 URL を生成するようにします。
プロキシーが有効な場合、それらは X-Forwarded-*
HTTP ヘッダーを使用して、要求が転送されたことを要求を処理するエンティティーや、元の値が転送前を認識できるようにします。Red Hat OpenStack Platform director の HAProxy 設定は、以下の設定を使用して、フロント接続で SSL/TLS を使用しているかどうかに基づいて、X-Forwarded-Proto
HTTP ヘッダーを設定します。
http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
さらに、Apache はこのヘッダーを解釈しないため、責任は別のコンポーネントに分類され、適切に処理されます。要求を処理するバックエンドサーバーの前に HAProxy が SSL を終了する状況では、Apache は拡張モジュール( mellon
など)が要求のプロトコルスキームを要求する際に X-Forwarded-Proto
HTTP ヘッダーが HTTPS に設定されているため、X-Forwarded-Proto HTTP ヘッダーが HTTPS に設定されていることは無関係です。このため、ServerName
ディレクティブに scheme:://host:port
が含まれ、UseCanonicalName
が有効になっている場合は、mod_auth_mellon
などの Apache 拡張モジュールはプロキシーの背後で適切に機能しません。
Apache がプロキシーの背後でホストされる Web アプリケーションに関しては、転送されたヘッダーを処理する Web アプリケーションの(または Web アプリケーションフレームワーク)が責任となります。そのため、アプリケーションは、転送されたリクエストのプロトコルスキームを Apache 拡張モジュールとは異なる方法で処理します。Dashboard(horizon)は Django Web アプリケーションであるため、X-Forwarded-Proto
ヘッダーを処理する責任があります。この問題は、認証中に horizon が
使用する元のクエリーパラメーターで発生します。Horizon は、認証を実行するために呼び出す keystone URL に元のクエリーパラメーターを追加します。元の
リソース
にリダイレクトするために、horizon により元のパラメーターが使用されます。
horizon が生成する元のパラメーターは、ファクト horizon が HTTPS を有効にして実行していても、https ではなく、スキームとして誤って指定する場合があります。これは、horizon が関数
build_absolute_uri()
を呼び出して元のパラメーターを形成するために発生します。build_absolute_url()
は最終的に Django によって実装されます。Django は、特別な設定ディレクティブを使用して X-Forwarded-Proto
を処理するように強制できます。これは、Django secure-proxy-ssl-header ドキュメントで説明されています。
/var/lib/config-data/puppet-generated/horizon/etc/openstack-dashboard/local_settings
でこの行をコメント解除して、この設定を有効にすることができます。
#SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Django はヘッダーの前に 「HTTP_」
を追加し、ハイフンをアンダースコアに変換することに注意してください。
コメント解除後、Origin
パラメーターは HTTPS スキームを正しく使用します。ただし、ServerName
ディレクティブに HTTPS スキームが含まれている場合でも、Django 呼び出し build_absolute_url()
は HTTPS スキームを使用しません。Django の場合は、SECURE_PROXY_SSL_HEADER
オーバーライドを使用する必要があります。ServerName
ディレクティブでスキームを指定すると動作しません。Apache 拡張モジュールと Web アプリケーションが転送された要求の要求スキームを異なる方法で処理することに注意してください。これには、ServerName
と X-Forwarded-Proto
HTTP ヘッダー技術の両方を使用する必要があります。