質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.53%

  • AWS(Amazon Web Services)

    2479questions

    Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

  • nginx

    999questions

    nginixは軽量で高性能なwebサーバーの1つです。BSD-likeライセンスのもとリリースされており、あわせてHTTPサーバ、リバースプロキシ、メールプロキシの機能も備えています。MacOSX、Windows、Linux、上で動作します。

  • Elastic Load Balancing

    36questions

    Elastic Load Balancingは、Amazon社が提供する、 EC2インスタンス間で自動的にトラフィックの負荷分散を行うサービスです。

ELB配下のEC2でnginxのリバースプロキシを使っている環境のみアクセス元IPが取得できない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 5
  • VIEW 6,661

CyberMergina

score 275

ELB × nginx(リバースproxy) × Nodeサーバーの環境で、Nodeサーバー側のシステムにアクセス元IPアドレスを渡したい。

□ 状態(追記しました)

  • リスナー設定
TCP(80) -> TCP(80)
SSL(443) -> TCP(80)

□ 今分かっていること

  • ELB × nginxの環境で、$remote_addrがELBのアドレスになる
  • ELB側でアクセス元IPアドレスがヘッダーの「X-Forwarded-For」に渡されている
    ということは把握しています。
    リバースプロキシを使用しない場合は「X-Forwarded-For」からアクセス元IPアドレスはとれました。

ですが、nginxでリバースプロキシを使用する場合は「X-Forwarded-For」にもELBのアドレスが入ってます。
(これはnginx側でセットされたのでしょうか・・・)

□ nginxの変数
$remote_addr, $proxy_add_x_forwarded_for にはELBのアドレス
$http_x_forwarded_for は空

何か情報をお持ちの方がいらっしゃいましたら、ご教授願います。
よろしくお願いします。


/etc/nginx/nginx.conf

http {
    include         /etc/nginx/mime.types;
    default_type    application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;


    keepalive_timeout   65;
    types_hash_max_size 2048;

    include /etc/nginx/conf.d/*.conf;
    index   index.html index.htm;

#    set_real_ip_from   10.0.0.0/8;
#    real_ip_header     X-Forwarded-For;

}

/etc/nginx/conf.d/default.conf

server {                                                             
server {
  listen 80 proxy_protocol;
  server_name hoge.hoge.jp;
  proxy_request_buffering off;

  set_real_ip_from XX.XX.0.0/XX; # VPC CIDR
  real_ip_header   proxy_protocol;

  location /demo/ { # デモページ(html)
    proxy_pass http://demo.localhost/;
  }

  location / { # socket.io用のjsファイルを読み込むのに使用します。
    proxy_pass http://0.0.0.0:1000;
  }

  location /socket.io/ { # websocket通信
    proxy_pass http://0.0.0.0:2000;
    proxy_set_header X-Real-IP $proxy_protocol_addr;
    proxy_read_timeout 150;
    proxy_set_header Host $host:$server_port;
    proxy_set_header X-Forwarded-For $proxy_protocol_addr;
    proxy_set_header X-Forwarded-Proto http;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }

}

□ 進捗

AWSのELBにProxyProtocolを有効にするような設定が必要?

参考
AWS wiki
How to configure AWS ELB and Nginx for WebSocket protocol? [closed]
Using Proxy Protocol With Nginx

>ProxyProtocolを有効にしました。(追記:2016/02/10 16:28)
恐らくあと少しで解決しそうです・・・!

TaichiYanagiyaさんの案①を参考に設定すると、nginxのログにはアクセス元IPが出てきました!

'$proxy_protocol_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent "$http_referer" ' '"$http_user_agent"';
- - [10/Feb/2016:16:15:01 +0900] "PROXY TCP4 [アクセス元IP] XXX.XXX.XXX.XXX 5602 443" 400 172 "-" "-"

但し、BadRequestが出ています・・・
もう少し調べてみます・・・!

>ProxyProtocolを有効にしました。(追記:2016/02/10 19:21)

ひとまず、現状の設定ファイルの内容に更新しました。
ProxyProtocolを有効にしたことで、Bad Requestが発生しています。

AWSのProxyProtocol使用の前提条件に「プロキシサーバーとロードバランサーの両方で有効になっていること」とありました。
http://blog.serverworks.co.jp/tech/2013/08/01/elb-proxy-protocol/
Nodeサーバー側でも何らかの処理が必要になるのでしょうか・・・?

その方面で検索するとこちらの記事を発見しましたので、調べてみます。

>ProxyProtocolを有効にし、BadRequestは解決。ただ、他のVHと共存できない(追記:2016/02/15 18:04)

  • TCPで通信するVirtualHostのみにした際、BadRequestは解決し、ログファイルにも「"PROXY ..."」ではなく「"GET ..."」という形でログが残るようになっていました

ただ、他にHTTPで通信しているVirtualHost(Server Nameで分けている)が接続できなくなった。

構造は以下の通りになっています。

[ELB1]
 server_name hoge.jp
  HTTPS(443) -> HTTP(80)
  HTTP(80)   -> HTTP(80)
  [nginx]
    root(/var/www/html)

[ELB2]
 server_name node.hoge.jp
  SSL(443) -> TCP(80)
  TCP(80)  -> TCP(80)
  [nginx(Reverce Proxy)]
    / -> 3000port(Nodeサーバー)
    /socket.io/ -> 4000port(WSサーバー)

[ELB3]
 server_name main.hoge.jp
  HTTPS(443) -> HTTP(80)
  HTTP(80)   -> HTTP(80)
  [nginx]
    root(/var/www/main)

※インスタンスは1つ
※現在、最小構成にしているのでProxyサーバー、Appサーバーなど分けていません。

proxy protocolを有効にするとELB1ELB3が接続できなくなります。
(ELBのヘルスチェックで引っかかる)

TCPとHTTPの共存は厳しいのでしょうか・・・?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

default.conf の proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 箇所でヘッダを追加していますので、この設定をはずせばいいです。

という単純な問題ではない?

(2016/02/10 12:40 追記)

ELB で ProxyProtocol を有効にした場合、nginx で listen ディレクティブに proxy_protocol を付ける必要があるようです。
とすると、ELB 80番ポートも HTTP から TCP に変更し、X-Forwarded-For の替わりに ProxyProtocol でアクセス元IPを拾うといいと思います。

(ELBリスナー設定)
    TCP  80 -> TCP 80
    SSL 443 -> TCP 80
    (インスタンス側 80番ポートについて ProxyProtocol を有効にする)

(ELBヘルスチェック設定)
    TCP:80
    (他は任意)

(nginx設定)
server {
    listen 80 proxy_protocol;

    set_real_ip_from   10.0.0.0/8;
    real_ip_header     proxy_protocol;

    (中略)

    location /socket.io/ {
        proxy_pass http://0.0.0.0:2000;
        #proxy_set_header X-Real-IP $remote_addr;    # こちらでもOK?
        proxy_set_header X-Real-IP $proxy_protocol_addr;
        proxy_read_timeout 150;
        proxy_set_header Host $host:$server_port;
        #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;    # こちらでもOK?
        proxy_set_header X-Forwarded-For $proxy_protocol_addr;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}


参考: How to Configure NGINX to Accept the Proxy Protocol

あるいは、現在の 80番ポートをそのまま利用するのであれば、ProxyProtocol 用に別のポートを用意する方法も考えられます。

(ELBリスナー設定)
    HTTP 80 -> HTTP 80
    SSL 443 -> TCP 8080
    (インスタンス側 8080番ポートについて ProxyProtocol を有効にする)

(ELBヘルスチェック設定)
    HTTP:80
    (他は任意)

(nginx設定)
server {
    listen 80;

    set_real_ip_from   10.0.0.0/8;
    real_ip_header     X-Forwarded-For;

    location / {
        proxy_pass http://0.0.0.0:1000;    # これは通常の HTML?
    }
}

server {
    listen 8080 proxy_protocol;

    set_real_ip_from   10.0.0.0/8;
    real_ip_header     proxy_protocol;

    location /socket.io/ {
        proxy_pass http://0.0.0.0:2000;
        # (他、1つ目の設定と同じ)
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/02/10 09:21

    回答ありがとうございます。
    「X-Forwarded-For」の設定を外しても、「X-Forwarded-For」の情報が渡されなくなるだけで、アクセス元のIPが取得できるようにならないのです・・・

    キャンセル

  • 2016/02/10 10:19

    > リバースプロキシを使用しない場合は「X-Forwarded-For」からアクセス元IPアドレスはとれました。
    ということは、その情報をそのまま次の backend に渡すだけです。それもおかしいなら
    https://blog.hello-world.jp.net/nginx/1014/
    こういう話ではなく?

    キャンセル

  • 2016/02/10 10:55

    ELB が WebSocket を扱えないので、WebSocket のリスナーのプロトコルを HTTP ではなく SSL または TCP にしているのでしょうか。
    SSL, TCP だと "X-Forwarded-For" が付かないので、ヘッダからはアクセス元の IPアドレスを拾えないと思います。
    CyberMergina さんが調べられたとおり、WebSocket の場合は ProxyProtocol が必要みたいです。

    キャンセル

  • 2016/02/10 12:46

    回答を修正し、設定例を追記しました。

    キャンセル

  • 2016/02/10 15:38

    [> takotakotさん
    > ということは、その情報をそのまま次の backend に渡すだけです。
    リバースプロキシを介す場合は、nginxのアクセスログを見ても
    X-Forwarded-Forにはアクセス元のIPアドレスではなく、ELBのアドレスが渡されてしまっているのです・・・

    URLの掲載ありがとうございます。
    /etc/nginx/nginx.conf のファイルの方にリンク先で紹介されている指定も
    してみたのですが、今のところ上手くいっていません。

    キャンセル

  • 2016/02/10 15:41 編集

    [> TaichiYanagiyaさん
    ELB が WebSocket を扱えないので、WebSocket のリスナーのプロトコルを HTTP ではなく SSL または TCP にしているのでしょうか。
    はい、その通りです。(記載しておくべき情報でしたね・・・すみません)

    > SSL, TCP だと "X-Forwarded-For" が付かないので、ヘッダからはアクセス元の IPアドレスを拾えないと思います。
    > CyberMergina さんが調べられたとおり、WebSocket の場合は ProxyProtocol が必要みたいです。
    な、なるほど!
    設定例の追記ありがとうございます!試してみます!!

    キャンセル

  • 2016/02/10 23:09

    ELB が付ける先頭行の「PROXY TCP4 ...」を nginx 側で正しく扱えていないようですね。
    設定の「listen 80 proxy_protocol;」でいいはずですが、nginx の再起動(reload ではなく restart)は行なっていますでしょうか。

    キャンセル

  • 2016/02/12 09:54

    [>TaichiYanagiyaさん
    返事が遅くなってしまい、申し訳ありません。
    >ELB が付ける先頭行の「PROXY TCP4 ...」を nginx 側で正しく扱えていないようですね。
    そうみたいです・・・設定を見直してみたりしているのですが今のところ解決しておりません。
    『nginx の再起動』についてですが、サービスの再起動を行うようにしています。(=service nginx restart)

    キャンセル

  • 2016/02/15 10:18

    nginx のバージョンはいくつでしょうか。
    1.8.0 では最初の listen ディレクティブに proxy_protocol が付いていないと有効にならないバグがあり、1.8.1 で修正されています(http://nginx.org/en/CHANGES-1.8)
    VirtualHostなどで他に listen はありませんでしょうか。

    *) Bugfix: the "proxy_protocol" parameter of the "listen" directive did
    not work if not specified in the first "listen" directive for a
    listen socket.

    キャンセル

  • 2016/02/15 11:00

    [> TaichiTanagiyaさん
    情報ありがとうございます!確認してみたところ、nginxのバージョンは1.8.0でした;
    入れ替えを行ってみます!

    キャンセル

  • 2016/02/15 11:57

    [> TaichiTanagiyaさん
    nginxのバージョンを1.8.1にあげましたが、変わらずでした・・・

    キャンセル

  • 2016/02/15 12:13

    切り分けのため、ELB を経由せずに localhost から 80番ポートに telnet などで接続し、「PROXY TCP4 ...」を入力するとどうなりますか?

    $ telnet 127.0.0.1 80
    PROXY TCP4 100.100.100.100 200.200.200.200 0 80<リターン>
    GET / HTTP/1.0<リターン>
    <リターン>

    キャンセル

  • 2016/02/15 13:37

    "PROXY TCP4 100.100.100.100 200.200.200.200 0 80"
    のアドレスの部分をログからコピペしてホストPC内の仮想環境から実行しました。
    結果は同じくBadRequestページが返りました。

    <html>
    <head><title>400 Bad Request</title></head>
    <body bgcolor="white">
    <center><h1>400 Bad Request</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>
    Connection closed by foreign host.

    コネクション切れてしまったので、「GET / HTTP/1.0」はもう一度telnetで接続して実行しました。
    この場合はレスなしでした。

    キャンセル

  • 2016/02/15 14:02

    そうすると、nginx 内の問題ですね。
    80番ポートで listen する他の VirtualHost などがあれば、すべて無効にするとどうでしょうか。

    キャンセル

  • 2016/02/15 15:40 編集

    serverの設定を1つだけにしてみましたが、変わらずBadRequestでした。

    $ cat socket.conf(他のファイルはリネームして、新しく作成しました)
    server {
    listen 80 ;
    server_name hoge.hoge.com;
    proxy_request_buffering off;

    location / {
    proxy_pass http://0.0.0.0:3000;
    }
    }

    $ curl http://hoge.hoge.com
    <html>
    <head><title>400 Bad Request</title></head>
    <body bgcolor="white">
    <center><h1>400 Bad Request</h1></center>
    <hr><center>nginx</center>
    </body>
    </html>

    $ curl http://0.0.0.0:3000
    <!DOCTYPE html>
    <html>
    <head>
    <title>DEMO PAGE</title>
    </head>
    <body>
    <h1>DEMO PAGE</h1>
    </body>
    </html>


    (※ちなみに、この時にProxyプロトコルを無効にすると、ドメインからアクセスできます・・・)

    キャンセル

  • 2016/02/15 15:49

    「listen 80;」ではなく「listen 80 proxy_protocol;」にする必要があるのですが、転記ミスでしょうか?

    キャンセル

  • 2016/02/15 15:53

    ファイル置き換えた時に書き間違えていました!
    もう一度確認してみます・・・!すみません・・・

    キャンセル

  • 2016/02/15 16:23

    他の80番でLISTENしているVirtual Hostの設定を消すと、正常に動作しました!

    + access.log
    [アクセス元IP] - - [15/Feb/2016:16:13:51 +0900] "GET / HTTP/1.1" 200 191 "-" "curl/7.44.0" "-"

    + socket.conf
    server {
    listen 80 proxy_protocol;
    server_name hoge.hoge.jp;
    proxy_request_buffering off;

    location / {
    proxy_pass http://0.0.0.0:3000;
    set_real_ip_from XX.XX.0.0/XX;
    real_ip_header proxy_protocol;
    proxy_set_header X-Real-IP $proxy_protocol_addr;
    proxy_read_timeout 150;
    proxy_set_header X-Forwarded-For $proxy_protocol_addr;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }
    }

    FQDNやサブディレクトリまで入れたパスをみて、
    複数に環境を分けているので、少し整理してみます!!

    キャンセル

  • 2016/02/15 18:50

    ELB1, ELB3 からの接続を 80番以外のポートで待ち受けるしかないと思います。

    キャンセル

  • 2016/02/15 19:00

    > ELB1, ELB3 からの接続を 80番以外のポートで待ち受けるしかないと思います。
    内部のポート番号が重なっていることが原因だったのでしょうか・・・
    ひとまず、ELB2の方を別ポートにしました。

    併せて「セキュリティグループ」にポート番号追加、
    nginxのlistenを修正。

    無事に動作することが確認できました。
    (ELB2の方でも大丈夫ですかね・・・)

    [> takotakot さん
    ご回答いただきありがとうございました。

    [> TaichiYanagiya さん
    ご丁寧に最後までお付き合いいただきありがとうございました。

    (!) 解決策等散らばりすぎたので、別途まとめます。

    キャンセル

同じタグがついた質問を見る

  • AWS(Amazon Web Services)

    2479questions

    Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

  • nginx

    999questions

    nginixは軽量で高性能なwebサーバーの1つです。BSD-likeライセンスのもとリリースされており、あわせてHTTPサーバ、リバースプロキシ、メールプロキシの機能も備えています。MacOSX、Windows、Linux、上で動作します。

  • Elastic Load Balancing

    36questions

    Elastic Load Balancingは、Amazon社が提供する、 EC2インスタンス間で自動的にトラフィックの負荷分散を行うサービスです。