ちょっと話題の記事

nginxを用いてX-Forwarded ヘッダー付のSSL通信が行える環境を構築してみる

2014.08.19

はじめに

本番はELBへのアクセスをHTTPS通信を行い、開発等では通常のHTTPで構築をすると言ったケースもあるかと思います。

そういった場合においてELBからアプリケーションへの転送をHTTPとして構築した場合、 アプリケーションをELB配下に持っていた際にも動く事を意識して開発を進める必要が有ります。

ただ、そういったケースにおいて、アプリケーションを実装を確認するにはどうすればよいでしょうか。

AWSを用いて環境を立ち上げるのも手ですが、 確認したいのはELB配下にHTTPで通信を待っているアプリの挙動を確認するといった際に、 ちょっと大げさな話になってしまいます。

そういったケースにおいて手元で確認したいと言った際に、以下の方法で試す事が可能です。

ELB配下のアプリケーションはELBにきたHTTPSアクセスを認識することができるのか?

ELBのフロントエンドをHTTPSとしバックエンドをHTTPとした場合、 ELBからアプリケーション側へのアクセスはHTTPでのアクセスとなります。 そのため、アプリケーション側では通常のHTTPSか否かの判断方法では判断を行う事が出来ません。

そういったケースに対応するためELBはアプリケーションに通信する際に、 リクエストのヘッダーにX-Forwarded ヘッダーを付与してアプリケーションに通信を行います。

Elastic Load Balancing の概念 - Elastic Load Balancing

アプリケーション側はヘッダーの当該項目を見る事でELB経由か否かを判断する事が出来ます。

構築手順

  • Webアプリケーションを作成します。
  • nginxをインストールします。
  • ELBと同様のHeaderを追加するnginxの設定ファイルを作成します。
  • 設定ファイルをnginxに読み込ませ、WebアプリケーションにELBと同様のHeaderがついて通信ができたことを確認します。

Webアプリケーションの作成

今回は、ELB下か否かを判断するのが目的となります。

そのため、リクエストのヘッダーのみを返すphpを書いてお茶を濁します。

index.php

<?php 
var_dump($_SERVER); 
?>%

動作を確認します。

$ php -S 127.0.0.1:28080 
 (別シェルを立ち上げ) 
$ curl http://127.0.0.1:28080 
array(17) { 
  ["REMOTE_ADDR"]=> 
  string(9) "127.0.0.1" 
  ["REMOTE_PORT"]=> 
  string(5) "63189" 
  ["SERVER_SOFTWARE"]=> 
  string(29) "PHP 5.5.11 Development Server" 
  ["SERVER_PROTOCOL"]=> 
  string(8) "HTTP/1.1" 
  ["SERVER_NAME"]=> 
  string(9) "localhost" 
  ["SERVER_PORT"]=> 
  string(5) "28080" 
  ["REQUEST_URI"]=> 
  string(1) "/" 
  ["REQUEST_METHOD"]=> 
  string(3) "GET" 
  ["SCRIPT_NAME"]=> 
  string(10) "/index.php" 
  ["PHP_SELF"]=> 
  string(10) "/index.php" 
  ["HTTP_USER_AGENT"]=> 
  string(11) "curl/7.30.0" 
  ["HTTP_HOST"]=> 
  string(15) "localhost:28080" 
  ["HTTP_ACCEPT"]=> 
  string(3) "*/*" 
  ["REQUEST_TIME_FLOAT"]=> 
  float(1407974733.5003) 
  ["REQUEST_TIME"]=> 
  int(1407974733) 
}

当然ですが、ELB経由で付加されるHeader(X-Forwarded)は付加されておりません。

nginxインストール

nginxのインストールに関しては、様々な方法があるので省略します。

nginxをインストールし、起動できるところまで行ってください。

以下は私がMac環境で行った一例となります。

$ brew install nginx 
$ nginx 
$ curl http://localhost:8080 
<!DOCTYPE html> 
<html> 
<head> 
<title>Welcome to nginx!</title> 
<style> 
    body { 
        width: 35em; 
        margin: 0 auto; 
        font-family: Tahoma, Verdana, Arial, sans-serif; 
    } 
</style> 
</head> 
<body> 
<h1>Welcome to nginx!</h1> 
<p>If you see this page, the nginx web server is successfully installed and 
working. Further configuration is required.</p> 
 
 
<p>For online documentation and support please refer to 
<a href="http://nginx.org/">nginx.org</a>.<br/> 
Commercial support is available at 
<a href="http://nginx.com/">nginx.com</a>.</p> 
 
 
<p><em>Thank you for using nginx.</em></p> 
</body> 
</html>

ELBと同様のHeaderを追加するnginxの設定ファイルを作成します

nginxにELBと同様にSSLとHeaderの追加を行わせるので、そのための設定ファイルを作成します。

また、SSL通信を行うための証明書の作成も必要です。

証明書の作成

$ openssl req -new -days 365 -x509 -nodes -keyout cert.key -out cert.crt

nginx設定ファイル作成

sslEmulate.conf

events {
    worker_connections  1024;

}
http {
    server {
        listen       443;

        ssl                  on;
        ssl_certificate      ./cert.crt;
        ssl_certificate_key  ./cert.key;

        ssl_session_timeout  5m;

        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers   on;

        location / {
            proxy_set_header Host                   $host;
            proxy_set_header X-Real-IP              $remote_addr;
            proxy_set_header X-Forwarded-Proto      https;
            proxy_set_header X-Forwarded-Host       $host;
            proxy_set_header X-Forwarded-Server     $host;
            proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:28080;
            proxy_redirect http:// https://;

        }

    }

}

動作確認

nginxに先ほど作った設定ファイルを読み込ませます。SSL通信を443ポートで行う場合はroot権限が必要なため sudoをつけて実行をしています。

sudo nginx -c $(cd $(dirname ./sslEmulate.conf) && pwd)/$(basename sslEmulate.conf)

nginxへの設定ファイルを読み込ませることができたならば、先ほどの作ったプログラムに対してSSL経由でアクセスしてみたいと思います。

証明書自体は自前で作った証明書のためcurlで呼び出した際にエラーがでるため、それを無視するオプションをつけて 確認を行います。

$ curl -k https://127.0.0.1:28080 
array(23) {
  ["REMOTE_ADDR"]=>
  string(9) "127.0.0.1"
  ["REMOTE_PORT"]=>
  string(5) "53545"
  ["SERVER_SOFTWARE"]=>
  string(29) "PHP 5.5.11 Development Server"
  ["SERVER_PROTOCOL"]=>
  string(8) "HTTP/1.0"
  ["SERVER_NAME"]=>
  string(9) "127.0.0.1"
  ["SERVER_PORT"]=>
  string(5) "28080"
  ["REQUEST_URI"]=>
  string(1) "/"
  ["REQUEST_METHOD"]=>
  string(3) "GET"
  ["SCRIPT_NAME"]=>
  string(10) "/index.php"
  ["PHP_SELF"]=>
  string(10) "/index.php"
  ["HTTP_HOST"]=>
  string(9) "127.0.0.1"
  ["HTTP_X_REAL_IP"]=>
  string(9) "127.0.0.1"
  ["HTTP_X_FORWARDED_PROTO"]=>
  string(5) "https"
  ["HTTP_X_FORWARDED_HOST"]=>
  string(9) "127.0.0.1"
  ["HTTP_X_FORWARDED_SERVER"]=>
  string(9) "127.0.0.1"
  ["HTTP_X_FORWARDED_FOR"]=>
  string(9) "127.0.0.1"
  ["HTTP_CONNECTION"]=>
  string(5) "close"
  ["HTTP_USER_AGENT"]=>
  string(11) "curl/7.30.0"
  ["HTTP_ACCEPT"]=>
  string(3) "*/*"
  ["REQUEST_TIME_FLOAT"]=>
  float(1408320862.0227)
  ["REQUEST_TIME"]=>
  int(1408320862)
}

ELB経由で付加されるHeader(X-Forwarded)が付与されて通信がされていたことがわかります。

まとめ

開発環境と本番環境をできるだけあわせておくことで、 本番環境にのせた段階でのプログラムの修正といった作業を前もって行うことが可能です。

また、今回のHeaderを用いた場合の挙動については、使っているフレームワーク等によって 実装方法が変わってくるので、フレームワークの挙動を確認するにも今回の方法は有効かと思われます。

参考URL

WordPress をフロント Nginx のリバースプロキシ下で運用する場合に 管理画面を SSL に強制する設定 - tilfin's note