• このエントリーをはてなブックマークに追加

はじめに

リバースプロキシなWebサーバとクライアントとがHTTPSでやりとりしているのを,バックエンドなPlackアプリケーションでどうやって確認するのか,ということを,昨晩ようやく知ることができました><

そのへんのメモ.

最初に結論

バックエンドに転送する際に,HTTPヘッダ

X-Forwarded-HTTPS: on

または

X-Forwarded-Proto: https

を追加する,これだけでしたw

Plack::Middleware::ReverseProxy の効果

Plackアプリケーション側で Plack::Middleware::ReverseProxy(以下,P::M::ReverseProxy)を有効にしておけば,先のHTTPヘッダが含まれていて,その値が HTTPS であることを示すものであれば,

$env->{HTTPS} = 'ON'

なる値が入ることを,先ほどソースコードを見てわかりました.

13
14
15
16
17
18
    # in apache2 httpd.conf (RequestHeader set X-Forwarded-HTTPS %{HTTPS}s)
    $env->{HTTPS} = $env->{'HTTP_X_FORWARDED_HTTPS'}
        if $env->{'HTTP_X_FORWARDED_HTTPS'};
    $env->{HTTPS} = 'ON'
        if $env->{'HTTP_X_FORWARDED_PROTO'} && $env->{'HTTP_X_FORWARDED_PROTO'} eq 'https';    # Pound
    $env->{'psgi.url_scheme'}  = 'https' if $env->{HTTPS} && uc $env->{HTTPS} eq 'ON';

確認

ローカルな nginx で,自己署名証明書なSSLを置いて簡単に確認してみます.

nginx 設定

こんな感じ. X-Forwarded-HTTPS ヘッダの有無の2パタンを準備しています:

server {
    listen 443;
    server_name momoco.local;
 
    ssl on;
    ssl_certificate /usr/local/etc/nginx/ssl/_.momoco.local.crt;
    ssl_certificate_key /usr/local/etc/nginx/ssl/_.momoco.local.key;
 
    location /x-forwarded-https-on/ {
        proxy_pass http://localhost:5000/;
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-HTTPS on;
    }
 
    location /x-forwarded-https-off/ {
        proxy_pass http://localhost:5000/;
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-HTTPS on;
    }
}

バックエンドの Plack アプリケーション

リクエストを受け取って,関連してそうな値を返すだけのものです:

use 5.12.0;
use warnings;
use Plack::Builder;
use Plack::Request;
use Data::Dumper;
$Data::Dumper::Terse = 1;
 
sub d { chomp ( my $d = Dumper @_ ); $d }
 
builder {
    # ここを有効にしたり無効にしたり
    enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } 'ReverseProxy';
 
    sub {
        my $env = shift;
        my $req = Plack::Request->new($env);
 
        my $content = sprintf(
            << '        ...',
 
env.HTTP_X_FORWARDED_HTTPS => %s
env.HTTPS                  => %s
 
req.scheme                 => %s
req.base                   => %s
 
        ...
            d( $env->{HTTP_X_FORWARDED_HTTPS} ),
            d( $env->{HTTPS} ),
            d( $req->scheme ),
            d( $req->base ),
        );
 
        return [
            200,
            [ 'Content-Type' => 'text/plain' ],
            [ $content ],
        ];
    };
};

結果

X-Forwarded-HTTPS: on

% curl -k https://momoco.local/x-forwarded-https-on/
 
env.HTTP_X_FORWARDED_HTTPS => 'on'
env.HTTPS                  => 'on'
 
req.scheme                 => bless( do{\(my $o = 'https://momoco.local/')}, 'URI::https' )
req.base                   => 'https'
X-Forwarded-HTTPS ヘッダなし
% curl -k https://momoco.local/x-forwarded-https-off/
 
env.HTTP_X_FORWARDED_HTTPS => undef
env.HTTPS                  => undef
 
req.scheme                 => bless( do{\(my $o = 'http://momoco.local:443/')}, 'URI::http' )
req.base                   => 'http'

ついでに確認:P::M::ReverseProxy が無効な場合

X-Forwarded-HTTPS: on
% curl -k https://momoco.local/x-forwarded-https-on/
 
env.HTTP_X_FORWARDED_HTTPS => 'on'
env.HTTPS                  => undef
 
req.scheme                 => 'http'
req.base                   => bless( do{\(my $o = 'http://momoco.local:443/')}, 'URI::http' )
X-Forwarded-HTTPS ヘッダなし
% curl -k https://momoco.local/x-forwarded-https-off/
 
env.HTTP_X_FORWARDED_HTTPS => undef
env.HTTPS                  => undef
 
req.scheme                 => 'http'
req.base                   => bless( do{\(my $o = 'http://momoco.local:443/')}, 'URI::http' )

ついでに確認:HTTPの場合は?(P::M::ReverseProxy は有効)

nginx 設定はこんな感じ:

server {
    listen 80;
    server_name momoco.local;
 
    location /x-forwarded-https-on/ {
        proxy_pass http://localhost:5000/;
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-HTTPS on;
    }
 
    location /x-forwarded-https-off/ {
        proxy_pass http://localhost:5000/;
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header X-Forwarded-HTTPS on;
    }
}
X-Forwarded-HTTPS: on
% curl -k http://momoco.local/x-forwarded-https-on/
 
env.HTTP_X_FORWARDED_HTTPS => 'on'
env.HTTPS                  => 'on'
 
req.scheme                 => 'https'
req.base                   => bless( do{\(my $o = 'https://momoco.local:80/')}, 'URI::https' )
X-Forwarded-HTTPS ヘッダなし
% curl -k https://momoco.local/x-forwarded-https-off/
 
env.HTTP_X_FORWARDED_HTTPS => undef
env.HTTPS                  => undef
 
req.scheme                 => 'http'
req.base                   => bless( do{\(my $o = 'http://momoco.local/')}, 'URI::http' )

表側の通信が HTTP であっても,ヘッダさえ追加してしまえば,裏側は HTTPS として反応してますね.まぁこのケースに意味はないと思います><

おわりに

以上,HTTPSしているリバースプロキシなWebサーバは,HTTPヘッダ X-Forwarded-HTTPS: onX-Forwarded-Proto: https を追加してバックエンドに転送した方がいいよね,というメモでした.