nginx 課題12 リダイレクトするサイトをリバースプロキシ その2のつづき

【検証1-4】 returnディレクティブに$hostを指定すればよくね?

PC(ブラウザ) -①→ リバースプロキシサーバ -②→ リダイレクトするwebサーバ

※リダイレクトするwebサーバを以後バックエンドサーバとする

 

のリクエストの流れで考える。

・バックエンドサーバが受け取るリクエストhostヘッダの値は何か?

nginx 課題12 リダイレクトするサイトをリバースプロキシ その2で調べたように、下記の2パターンがあると思われる

[パターン1] inboundリクエストhostヘッダと同じoutboundリクエストhostヘッダ

リバースプロキシサーバが上記①のinboundリクエストhostヘッダの値を、上記の②のoutboundリクエストhostヘッダに付与するパターン

[パターン2] outboundリクエストhostヘッダをリバースプロキシサーバが振り出しなおす

リバースプロキシサーバが「proxy_pass」ディレクティブ(nginxの場合)や、「ProxyPass」ディレクティブ(apacheの場合)などの値に指定されたIPアドレス(あるいはFQDN)を、上記の②のoutboundリクエストhostヘッダに振りなおすパターン

※nginx、apacheのほかにもsquidやBigIPなど他のプロキシプロダクトによってディレクティブ名が違うかもしれない。

※もしかしたら、outboundリクエストhostヘッダの値の振り出し方法は、nginxやそれぞれのプロキシプロダクトによっていくつかの方法が提供されている場合があるかもしれない。(調べてないからわかんないけど)

※プロキシプロダクトのうち、apacheのmod_proxy_httpとnginxのproxyコンポーネントの動作の違いを特に注意。(mod_proxy_http.soはmod_proxy.soのサポートモジュール。つまり、mod_proxy_httpは、mod_proxyのモジュールでありapache本体のモジュールのモジュールという位置づけ)

※そもそも標準プロトコルを読まなくちゃ

 X-Forwarded-Host はオリジナルのホスト名。クライアントが Host リクエストヘッダで渡す。 

 X-Forwarded-Server はプロキシサーバのホスト名。

https://httpd.apache.org/docs/2.2/ja/mod/mod_proxy.html

オリジナルのリクエストが既に同じヘッダを持っていると、 ヘッダが一つ以上の値 (コンマで区切られます) を持つ可能性がある。

 

いずれにせよ、フロントエンドリバースプロキシサーバからバックエンドに転送されるoutboundリクエストhostヘッダの値は[パターン1]のようにクライアント側のエンドポイント(ブラウザなど)から振り出されたURLの中の値である場合と、[パターン2]のように、URL転送途中でクライアント側エンドポイントから振り出されたURLとは異なっている場合がある。

おいらの疑問点は、バックエンドサーバ(リダイレクトするwebサーバ)は、サービスの機能要件次第で、自分が提供するサービスに対してクライアント側エンドポイントからリクエストされるURLをすべて把握して実装する場合と、把握しない場合があると思う。

把握しないケースというのは例えば、

SAASサービスのアプリケーションサーバの場合、エンドユーザが種々のドメインでアクセスしてくることを想定していて、URI内のポート番号、パス、getクエリに関してはアプリケーション内で受信するものとlocationレスポンスヘッダに書き込んでリダイレクトさせるURIを確実に把握して処理系を実装しているはずであるが、URIの中のホスト名(FQDNかIPアドレス)に関してはサーバサイドwebアプリケーション側で感知しないケースが想定される。

―このように、サーバサイドWebアプリケーションがリダイレクトを前提として機能要件を実装しているが、URIの中のホスト名に関しては関知していないケースを想定した場合、ブラウザからサーバ側エンドポイントまでの中間に存在する下図の各リバースプロキシサーバにはどのような要件が期待されるのか?

 

クライアント→RP(リバースプロキシ)1RP2→・・・→RPn→アプリケーションサーバ

 

ポイントとしては、

・アプリケーションサーバに届けるリクエストhostヘッダの値

・アプリケーションサーバに届けるリクエストX-Forward-forヘッダの値

・アプリケーションサーバに届けるその他リクエストヘッダの値

・クライアントに返送するレスポンスset cookieヘッダののcookie値

・クライアントに返送するレスポンスlocationヘッダの値

・クライアントに返送するその他のレスポンスヘッダの値

・2回目以降のリクエストでアプリケーションサーバに届けるcookieヘッダのcookie値

※フロントエンドからアプリケーションサーバまでの区間に内部TLS暗号化通信区間がある場合は?

 

さらに、リバースプロキシサーバの機能として、

・キャッシュ

・コネクションプーリング

・その他セキュリティ上の留意事項

 

は何なのだろうか?

 

ちょっと脱線したけど、実機をいじる検証再開。

※プロダクトのマニュアルとRFCはその後で読むんご

 

--------------------------------------------------------------------------

 

【検証1-4】 returnディレクティブに$hostを指定してみる

 

nginx 課題12 リダイレクトするサイトをリバースプロキシでは、リクエストホスト名とリダイレクトホスト名が違うバーチャルホストを想定していたが、上記の考察のようにSAAS用途のWebサービスの場合、リクエストホスト名とリダイレクトホスト名は同じである方が現実的。

ただし、リダイレクトするWebサーバ(アプリケーションサーバ)は自分へのリクエストURLのホスト名を処理系に含めず、たとえばブラウザから

https://hoge1.com/foo1/bar1?fuga1=puni1&piyo1=ahe1

のようなリクエストを受け取って、

https://hoge1.com/foo2/bar2/kaa2

にリダイレクトさせるといった具合に、URIの中の「hoge1.com」のようなホスト名は、リダイレクト前と後で変わらず、かつリダイレクトするWebサーバ(アプリケーションサーバ)は関知していないってケースが想定される(もちろん他のケースも想定されるけど)

このような場合は、リダイレクトするWebサーバで

        return 301 https://$host;

として、バックエンドサーバのフロントエンド側のすべてのリバースプロキシサーバで、

outboundリクエストhostヘッダの値をinboundリクエストhostに書き換えてバックエンドまで転送すればいいのでは。

つまり、

フロントエンドプロキシサーバでは、

せ70> cat /etc/nginx/conf.d/proxy.conf

server {
    listen       443;
    ssl                  on;
    server_name  gad2.chinko2;

    ssl_certificate      /etc/pki/tls/certs/gad2.chinko2.crt;
    ssl_certificate_key  /etc/pki/tls/private/gad2.chinko2.key;

    ssl_session_timeout  10m;
    ssl_session_cache   shared:SSL:10m;

    ssl_protocols TLSv1.2;
    ssl_ciphers  kRSA;
    ssl_prefer_server_ciphers   on;

    proxy_cookie_path / "/; secure";

    proxy_set_header Host             $host;
    proxy_set_header X-Real-IP        $remote_addr;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://192.168.2.7;
        proxy_redirect default;
    }
}

server {
    listen       443;
    ssl                  on;
    server_name  gad3.chinko3;

    ssl_certificate      /etc/pki/tls/certs/gad3.chinko3.crt;
    ssl_certificate_key  /etc/pki/tls/private/gad3.chinko3.key;

    ssl_session_timeout  10m;
    ssl_session_cache   shared:SSL:10m;

    ssl_protocols TLSv1.2;
    ssl_ciphers  kRSA;
    ssl_prefer_server_ciphers   on;

    proxy_cookie_path / "/; secure";

    proxy_set_header Host             $host;
    proxy_set_header X-Real-IP        $remote_addr;
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://192.168.2.7;
        proxy_redirect default;
    }
}

のように「proxy_set_header host $host」ディレクティブでoutboundリクエストhostヘッダをinboundリクエストhostヘッダと同じにするようにして、

バックエンドWebサーバでは、

せ7> cat /etc/nginx/conf.d/virtualhost.conf

server {
    listen 80;
    server_name gad2.chinko2;

    root /usr/share/nginx/html/gad2.chinko2/;

    location / {
        return 301 https://$host/redirectcontents/;      ←ここのホスト名を「$host」にする
    }

    location /redirectcontents/ {
        charset utf-8;
        index index.html index.htm;
    }
}

server {
    listen 80;
    server_name gad3.chinko3;

    root /usr/share/nginx/html/gad3.chinko3/;

    location / {
        return 301 https://$host/redirectcontents/;      ←ここのホスト名を「$host」にする
    }

    location /redirectcontents/ {
        charset utf-8;
        index index.html index.htm;
    }
}

 

【結果】→成功

 

※注意事項

return 301 https://$host/redirectcontents/ ディレクティブのURIの最後の「/」と

location /redirectcontents/ {・・・} ディレクティブのURIの最後の「/」を付けないとうまくいかなかった。

・return 301 https://$host/redirectcontents のようにURIの最後に「/」を付けないと、locationヘッダ内の値も「https://gad2.chinko2/redirectcontents」となり、ブラウザからリダイレクト先へのリクエストも「https://gad2.chinko2/redirectcontents」のように最後に「/」が付かず、コンテンツが見つからない

・location /redirectcontents {・・・} のようにURIの最後の「/」を付けないと、リダイレクトされてフロントエンドからリクエストされたhttp://gad2.chinko2/redirectcontents/がマッチしない

 

また、リダイレクト設定を下記のようにreturnディレクティブの代わりにrewriteディレクティブにしてもうまくいった。

せ7> cat /etc/nginx/conf.d/virtualhost.conf

server {
    listen 80;
    server_name gad2.chinko2;

    root   /usr/share/nginx/html/gad2.chinko2/;

    location / {
    rewrite ^(.*)$ https://$host/redirectcontents/ redirect;
    }

    location /redirectcontents/ {
        charset utf-8;
        index  index.html index.htm;
    }
}

server {
    listen 80;
    server_name gad3.chinko3;

    root   /usr/share/nginx/html/gad3.chinko3/;

    location / {
    rewrite ^(.*)$ https://$host/redirectcontents/ redirect;
    }

    location /redirectcontents/ {
        charset utf-8;
        index  index.html index.htm;
    }
}

 

【課題】 

1.nginxの変数、ディレクティブ、パス指定等にはいろいろな癖があってapacheとも違うらしいのでいろんなケースで検証して習熟する必要がある

Nginxのリダイレクト設定のメモ

Nginxでのドメインやhttpsへのリダイレクトの設定はrewriteよりもreturnを使おう

nginx の設定をレビューするときの観点をまとめてみた

nginx の proxy_pass へ何を指定するか

2.httpプロトコルのRFCを見てどんなヘッダがあるのか? nginxの各ディレクティブはそれらのヘッダをどのように追加/削除/書換えしてどんな制御を実現するのか?

※先行のapache httpdサーバのディレクティブに対する説明を見てからnginxではどう違うのか考えた方が多分早い

https://httpd.apache.org/docs/2.2/ja/

https://httpd.apache.org/docs/2.4/ja/

ちなみに、2.4だと和訳されてないけど2.2だと和訳されてることがあるんご

 

【検証1-5】 バックエンドwebアプリケーションサーバがレスポンスlocationヘッダに、リクエストhostヘッダのホスト名(ipアドレス)ではなく、別の値で振り出す可能性はあるか?

たとえば、

・バックエンドwebアプリケーションサーバ内でもつパラメータ値(config値、環境変数)

・バックエンドWebサーバのOSがもつipアドレスやhostname値(環境変数、

/proc/sys/kernel/hostname)

 

【検証1-5-1】 バックエンドWebサーバがクライアント側エンドポイントからではアクセスできないホスト名をレスポンスlocationヘッダに設定して返信した場合に、クライアント側エンドポイントからアクセスできるホスト名でレスポンスlocationヘッダをproxy_redirectディレクティブを使って書き換える

 

【検証1-5-2】 バックエンドWebサーバがクライアント側エンドポイントからではアクセスできないホスト名(hostnameコマンドで取得したホスト名)をレスポンスlocationヘッダに設定して返信した場合に、クライアント側エンドポイントからアクセスできるホスト名でレスポンスlocationヘッダをproxy_redirectディレクティブを使って書き換える

 

【検証1-5-3】 バックエンドWebサーバがクライアント側エンドポイントからではアクセスできない自身のipアドレスをレスポンスlocationヘッダに設定して返信した場合に、クライアント側エンドポイントからアクセスできるホスト名でレスポンスlocationヘッダをproxy_redirectディレクティブを使って書き換える