9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

varnish + nginxでPROXY PROTOCOL

Posted at

webサーバ(バックエンド)をロードバランサ下に複数置いて負荷分散をするのは極めて一般的なことですが、
そのときの問題として、バックエンドからはアクセスがロードバランサーから来たものとして扱われてしまいます。

バックエンド側のアクセス元IPが変わってしまうので、アクセスログに元IPが載らなくなってしまいます。
また、アプリケーション側でIP制限をかけたい場合も、標準的な方法(PHPだと$_SERVER['REMOTE_ADDR'])で取得できるIPアドレスが変わってしまうのも困りものです。

この問題を解決するために、ロードバランサーからバックエンドへ元IPを伝えるためのPROXY protocolというプロトコルがあります。

今回はvarnish(ロードバランサ)とnginx(バックエンド)の組み合わせで、PROXY protocolを有効にして、バックエンド側に元IPを伝えてみます。

構成

  • varnish 5(4は未対応)
  • nginx 1.10

今回はvarnishのバージョンの関係上、Ubuntu 17.04(開発版)で検証しました。

varnishとnginxは同一マシンに同居させ、外部からvarnishが80番ポートで受け付けたアクセスを、8080番で待ち受けてるnginxにforwardします。

クライアント → | varnish(80) - (with PROXY Protocol) → nginx(8080) |

varnish

vcl 4.0;

backend default {
  .host = "127.0.0.1";
  .port = "8080";
  .proxy_header = 1;
}

sub vcl_recv {
  return (pass);
}

sub vcl_backend_response {
}

sub vcl_deliver {
}

backend に .proxy_header = 1 を追加することで、PROXY protocolを有効になります。
PROXY Protocolはv1とv2の2つのバージョンがありますが、nginxはv1のみの対応のため、v1(1)を指定します。

nginx

server {
  listen 8080 proxy_protocol;
  server_name _;

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  root /var/www/html;
  index index.html index.htm index.nginx-debian.html;

  set_real_ip_from 127.0.0.1;
  real_ip_header proxy_protocol;

  location / {
    # First attempt to serve request as file, then
    # as directory, then fall back to displaying a 404.
    try_files $uri $uri/ =404;
  }

  location ~ [^/]\.php(/|$) {
    fastcgi_pass unix:/run/php/php7.0-fpm.sock;
    fastcgi_index index.php;

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
  }
}

ポイントは以下の3点。

  • listenproxy_protocolを指定。
  • set_real_ip_from に ロードバランサーのIPを指定。(今回は127.0.0.1
  • real_ip_header proxy_protocolを指定。

で、どうなるの?

ロードバランサーがバックエンドにHTTPリクエストをforwardするときに、通常のHTTPリクエストヘッダの前に1行を挿入します。

PROXY TCP4 192.168.33.1 192.168.33.177 55407 80

左からTCPバージョン、送信元IP、ロードバランサのIP、送信元ポート、ロードバランサのポートなります。
この送信元IPがアクセス元のIPになります。nginxはこの情報を受け取ってアクセス元IPを書き換えます。

$_SERVER['REMOTE_ADDDR']も自動的に書き換わってくれるので、アプリケーションからはダイレクトにアクセスされた場合と同じように処理ができるようになります。

注意!

非常に便利なPROXY PROTOCOLですが、このPROXY行を無条件に受け付けてしまうと、アクセス元IPの改ざんを許してしまうことになります。set_real_ip_fromでロードバランサーからだけのPROXY行を受け付けることが重要です。

追記

今回はロードバランサーにvarnishを使いましたが、AWS ELBもPROXY Protocolを有効にできます。設定方法はAWSのドキュメントを参照してください。

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?