[研究] それぞれのディレクティブの意味を調べる

【検証1】 return ディレクティブによるlocationヘッダ追記、proxy_redirectディレクティブによるlocationヘッダの書換え

【検証1-1】 returnディレクティブの有無によるレスポンスの違い

【検証1-2】 proxy_redirectディレクティブによる下記の書換を試す

ipアドレスとFQDN、ポート番号、ディレクトリ、getクエリ

※ブラウザからhttps://gad2.chinko2にアクセスして各箇所でパケットをキャプチャしてみる

【検証2】 proxy_cookie_pathディレクティブとcookieヘッダ、およびset cookieヘッダ

ブラウザのEditThisCookieツールでクッキーを見てみる

【検証3】  ssl_prefer_server_ciphersディレクティブ

【検証3-1】 「ON」のときと「OFF」のときでSSLパケットをキャプチャしてみる

【検証4】  proxy_set_headerディレクティブ

【検証4-1】 下記のヘッダがそれぞれセットされることを確認する

  host ← $host あるいは 定数値

 X-Real-IP ← $remote_addr あるいは 定数値。そしてこのヘッダは一体何か?

  X-Forwarded-For ← $proxy_add_x_forwarded_for あるいは$remote_userあるいは定数値

  ※proxyサーバにX-Forward-forヘッダが付与されることによって、バックエンドのアクセスロ       グ、アクセスコントロールの動きを変えられることを確認する  

【検証5】 proxy_passディレクティブ

【検証5-1】 IPアドレス、FQDN、URLにポート番号、ディレクトリ、getクエリ

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

【検証1-1】 returnディレクティブの有無によるレスポンスの違い

・returnディレクティブ有り

・リダイレクトするWebサーバのnginxの設定

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

server {
    listen 80;
    server_name gad2.chinko2;
    return 301 https://gad3.chinko3;
}

・・・後略・・・

レスポンスにLocationヘッダがついている

・returnディレクティブ無し

・リダイレクトするWebサーバのnginxの設定

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

server {
    listen 80;
    server_name gad2.chinko2;
    root   /usr/share/nginx/html/gad2.chinko2;
    location / {
        charset utf-8;
        index  index.html index.htm;
    }
}

レスポンスにLocationヘッダがついてない(当然だけど)

 

【検証1-2】 proxy_redirectディレクティブによる下記の書換を試す

ipアドレスとFQDN、ポート番号、ディレクトリ、getクエリ

※ブラウザからhttps://gad2.chinko2にアクセスして各箇所でパケットをキャプチャしてみる

※wiresharkでTLSを復号する場合、サーバ側でDHEを無効にするらしいhttp://cipepser.hatenablog.com/entry/2017/04/08/110945

なので、下記のようにcipherを変更

    ssl_ciphers  kRSA;

wiresharkに以前作ったサーバー秘密鍵(gad2.chinko2とgad3.chinko3用)を登録してプロキシ側nginx(192.168.2.70)でパケットをキャプチャ

・下記の場合

location / {

proxy_pass http://192.168.2.7;

proxy_redirect default;

}

以下を意味している

location / {

proxy_pass http://192.168.2.7;

proxy_redirect http://192.168.2.7 /;

}

http://www2.matsue-ct.ac.jp/home/kanayama/text/nginx/node68.html

つまり、バックエンドからのレスポンスlocationヘッダの値が「http://192.168.2.7」なら「/」につけかえる。

今回は、上記で見たようにlocationヘッダの値は「https://gad3.chinko3」なのでproxy_redirectはlocationヘッダを書き換えない。

locationヘッダは「https://gad3.chinko3」のまま

 

・proxy_redirectを下記のように変えてみる

location / {

proxy_pass http://192.168.2.7;

proxy_redirect https://gad3.chinko3 https://www.google.com;

}

【結果】→ブラウザキャッシュを削除後、ブラウザ再起動して、ブラウザから https://gad2.chinko2 に接続するとgoogleのトップページに飛んだんご

 

【検証1-3】 proxy_redirect default;ではダメなケース?

リダイレクトするバックエンドサーバがリダイレクト先URL(ディレクトリ部分など)を変更するけど、クライアント側エンドポイント(ブラウザ)が最初にリクエストしたURL内のFQDNを知らない場合。

 

今回は、リダイレクトするバックエンドサーバが、クライアント側エンドポイントが最初にリクエストしたURL内のFQDNを知ってるという前提で、バックエンドnginxのreturnディレクティブにこのFQDNをハードコーディングしてしまっているが、このような実装は現実的とは言えない。

locationヘッダを実際に付けるバックエンドサーバは、本来、フロントエンドがどんなFQDN宛てのリクエストを受信しているか知らなくてもいいはず?

 

この場合は2通りの方法が考えられる?

 

1.フロントエンドproxyサーバが最初に受信するFQDNを入れたhostリクエストヘッダに書き換えてバックエンドサーバにhttpリクエストを転送し、バックエンドサーバはリクエストからhostヘッダを取り出してlocationヘッダのFQDNにはめ込む

 

2,フロントエンドproxyサーバが最初に受信するFQDNをキャッシュしておいてバックエンドにhttpリクエストを転送し、バックエンドサーバはproxyサーバがつけたhostヘッダからFQDNまたはIPアドレスを取り出してlocationヘッダのURLを組み立ててレスポンスし、これを受信したproxyサーバがキャッシュから受信したFQDNを取り出してlocationヘッダのFQDN(またはIPアドレス)と取り換えてブラウザに返信するレスポンスのlocationヘッダを組み立てる。

 

【検証1-3-0】 ブラウザでURL窓に入力したホスト名(IPアドレス)とブラウザからのリクエストhostヘッダの関係

下記の3ケースでブラウザから直接リクエストを受信するフロントエンドプロキシサーバでキャプチャしたパケットを見る

・ブラウザからのURL入力値が「https://gad2.chinko2」の場合

【結果】→Host: gad2.chinko2\r\n

・ブラウザからのURL入力値が「https://gad3.chinko3」の場合

【結果】→Host: gad3.chinko3\r\n

・ブラウザからのURL入力値が「https://192.168.2.70」の場合

【結果】→Host: 192.168.2.70\r\n

ブラウザのURL窓のホスト名(IPアドレス)がブラウザからのリクエストhostヘッダの値となった。

 

【検証1-3-1】 プロキシサーバで「proxy_set_header Host $host;」ディレクティブがあるときとないときで、プロキシサーバからバックエンドに転送されるリクエストhostヘッダの値はどう変わるか?

上記の【検証1-3-0】の結果からブラウザからフロントエンドプロキシサーバへのリクエストhostヘッダの値はブラウザのURL窓のホスト名の値となることはわかったが、フロントエンドプロキシサーバがブラウザから受信したリクエストをバックエンドに転送する際に「proxy_set_header Host」ディレクティブで書換えられることを確認する

 

・ブラウザからのリクエストhostヘッダの値が「gad2.chinko2」でフロントエンドproxyサーバの「proxy_set_header Host」ディレクティブが「proxy_set_header Host $host;」の場合

【結果】→Host: gad2.chinko2\r\n

 

・ブラウザからのリクエストhostヘッダの値が「192.168.2.70」でフロントエンドproxyサーバの「proxy_set_header Host」ディレクティブが「proxy_set_header Host $host;」の場合

【結果】→Host: 192.168.2.70\r\n

※フロントエンドproxyサーバの設定を下記のように変更して実行した

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

server {
    listen 192.168.2.70:80 default_server;                             #ipアドレスベースのヴァーチャルサーバに変更

    location / {
        proxy_set_header Host             $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        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";

    location / {
        proxy_set_header Host             $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.2.7;
        proxy_redirect default;
    }
}

 

・ブラウザからのリクエストhostヘッダの値が「gad2.chinko2」でフロントエンドproxyサーバの「proxy_set_header Host」ディレクティブを注釈化した場合

せ7> tcpdump -n -i ens34 -s 0 -w /tmp/20180527_4.pcap host 192.168.2.70 and port 80

で採取したpcapファイルをwiresharkで開いて確認

【結果】→Host: 192.168.2.7\r\n

※フロントエンドproxyサーバの設定を下記のように変更して実行した

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

server {
    listen       443 default_server;
    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";

    location / {
#        proxy_set_header Host             $host;      #ここを注釈化した
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        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";

    location / {
        proxy_set_header Host             $host;
        proxy_set_header X-Real-IP        $remote_addr;
        proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_pass http://192.168.2.7;
        proxy_redirect default;
    }
}

 

・ブラウザからのリクエストhostヘッダの値が「gad2.chinko2」でフロントエンドproxyサーバの「proxy_set_header Host」ディレクティブを注釈化し、かつ

「proxy_pass」ディレクティブを「proxy_pass http://gad2.chinko2」に設定したフロントエンドproxyサーバは、outboundリクエストhostヘッダの値に何をセットしてバックエンドに転送するか?

※フロントエンドproxyサーバが「gad2.chinko2」を名前解決できるように、フロントエンドproxyサーバのhostsファイルに下記を追記

せ70> cat /etc/hosts

192.168.2.7             gad2.chinko2

せ70> diff proxy.conf.bak proxy.conf
20c20
<         proxy_set_header Host             $host;
---
> #        proxy_set_header Host             $host;
24c24,25
<         proxy_pass http://192.168.2.7;
---
> #        proxy_pass http://192.168.2.7;
>         proxy_pass http://gad2.chinko2;

【結果】→Host: gad2.chinko2\r\n

 

ちなみに、フロントエンドproxyサーバが「gad2.chinko2」を名前解決できなかった場合は?

せ70> cat /etc/hosts

#192.168.2.7             gad2.chinko2

せ70> ping gad2.chinko2
ping: gad2.chinko2: 名前またはサービスが不明です

ブラウザキャッシュを削除してブラウザを再起動してブラウザから https://gad2.chinko2 にアクセス

【結果】→Host: gad2.chinko2\r\n   なぜ?

フロントエンドproxyサーバのキャッシュに残っているからか?

再度ブラウザ側のキャッシュも削除してフロントエンドproxyサーバをrestart

【結果】→nginxが起動に失敗。

せ70> journalctl -xe

・・・前略・・・

-- Unit nginx.service has begun starting up.
 5月 28 03:30:59 centos7_sub nginx[3658]: nginx: [emerg] host not found in upstream "gad2.chinko2" in /etc/nginx/conf.d/proxy.conf:25
 5月 28 03:30:59 centos7_sub nginx[3658]: nginx: configuration file /etc/nginx/nginx.conf test failed

・・・後略・・・

もう一度、/etc/hostsでgad2.chinko2の名前解決できるようにしたら、nginxは無事起動した。

【結果】→nginxはproxy_passディレクティブのURIをipアドレス以外にFQDNでも指定できる。ただし、FQDNが名前解決できなければnginxサービスの起動に失敗する

 

【分かったこと】

・「proxy_set_header Host $host」ディレクティブを設定したnginxは、inboundリクエストhostヘッダの値をoutboundリクエストhostヘッダの値にセットしてバックエンドに転送する

「proxy_set_header Host」ディレクティブを設定していないnginxは、proxy_passディレクティブの値をoutboundリクエストhostヘッダの値にセットしてバックエンドに転送する。このとき、proxy_passのURIがIPアドレスならIPアドレスとしてFQDNならFQDNとしてoutboundリクエストhostヘッダの値にセットしてバックエンドに転送する。

※proxy_passディレクティブ内にipアドレス以外にFQDNでURIを指定できるが、FQDNが名前解決できなければnginxサービスは起動自体に失敗する。

 

------20180701追記------------------------

【検証4-1】 下記のヘッダがそれぞれセットされることを確認する

[お題]

proxy_set_headerディレクティブでX-Forward-Forヘッダを下記の①と②でセットした場合の動作検証

① proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
② proxy_set_header X-Forwarded-User $remote_user;

[座学]

proxy_set_headerディレクティブのコンテキストは、http、server、location

ディレクティブのアルファベットの索引

[検証結果]

① proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;

→リクエストに X-Forwarded-For ヘッダが付与されていなかった場合は、自分の(nginxサーバ自身の)ipアドレスを値にした X-Forwarded-For ヘッダを付与

リクエストにすでに X-Forwarded-For ヘッダが付与されていた場合は、既存の値に、「,」区切りで自分の(nginxサーバ自身の)ipアドレスを追記

② proxy_set_header X-Forwarded-User $remote_user;

→リクエストに X-Forwarded-For ヘッダが付与されていなかった場合は、自分の(nginxサーバ自身の)ipアドレスを値にした X-Forwarded-For ヘッダを付与

リクエストにすでに X-Forwarded-For ヘッダが付与されていた場合は、既存の値を自分の(nginxサーバ自身の)ipアドレスで上書き