まとめ
-
X-Forwarded-For
ヘッダの右から 2 つ目が接続元 IP アドレスです。 - ただし
X-Forwarded-For
の不正な書き換えを防止するため、 CloudFront=>ELB=>EC2 それぞれの通信を保護する必要があります。
CloudFront => ELB を保護する
CloudFrontのELBオリジンへ直接アクセスする通信を制限する方法 | Developers.IO に詳しいです。
要約すると、
- CloudFront のカスタムヘッダに秘密の文字列を設定し、 ELB や EC2 側でそれを検証する
- ELB のセキュリティグループで、 CloudFront の IP アドレスだけを許可するよう設定する (要定期更新)
の 2 種類になります。
ELB => EC2(nginx) を保護する
EC2 のセキュリティグループで、 ELB (もしくは ELB 用のセキュリティグループ) からのアクセスのみを許可するように設定すれば達成できます。
nginx 設定例
set $real_ip "";
# X-Forwarded-For の右から 2 つ目の IP アドレスを接続元として扱う。
# CloudFront => ELB => nginx という構成で、それぞれの通信が保護されている前提。
if ($http_x_forwarded_for ~ "([^, ]+) *, *[^, ]+ *$") {
set $real_ip $1;
}
if ($real_ip = "") {
# X-Forwarded-For が何かおかしい。安全のため、 400 Bad Request で終了する。
return 400;
}
バックエンド (Rails など) にリバースプロキシするなら
proxy_set_header X-Forwarded-For $real_ip;
# や
proxy_set_header X-Real-IP $real_ip;
# など
で渡せます。
- バックエンド側で、 nginx 自身の IP アドレスを転送元として信頼するよう設定する必要があるかもしれません。
- CloudFront/ELB どちらも
X-Real-IP
は使わないので、不正な値が設定されないよう、上書きするか、消しておくかしたほうが良いです。
FastCGI (php-fpm など) に渡すなら
fastcgi_param REMOTE_ADDR $real_ip;
# や
fastcgi_param HTTP_X_FORWARDED_FOR $real_ip;
# や
fastcgi_param HTTP_X_REAL_IP $real_ip;
# など
で 渡せます。
- FastCGI なら
REMOTE_ADDR
も上書きできます。 - やはり、不正な値が設定されないよう、
HTTP_X_REAL_IP
は上書きするか消すかしたほうが良いです。
アクセス制限に利用するなら
http {
map $real_ip $ip_type {
default "default";
1.1.1.1 "Foo";
2.2.2.2 "Bar";
}
server {
set $real_ip "";
# 略。上記の $real_ip 関連の設定。
if ($ip_type = "default") {
return 403;
}
}
}
のような書き方が可能です。
※本当は set_real_ip_from
などを使ったほうが綺麗だと思うのですが、 nginx レベルで CloudFront の IP アドレスを管理する手間が……。
なお、アクセス制限をするのであれば、 CloudFront に AWS WAF をつけるほうがシンプルかつパフォーマンスも良いかと思います。
- ウェブ ACL 1 つで $5/月
- IP アドレス制限用のルール 1 つで $1/月
- 1 ルールに複数の IP アドレスを登録できます
- 他、国や URL などの条件も同じルールとして設定できます (1 ルール=複数の条件のグループ)
- ルール 1 つごとに 100 万ウェブリクエストあたり $0.60
なので、 $6/月+100 万ウェブリクエストあたり $0.60 で利用可能です。
後書き
CloudFront => ELB => WordPress という構成で、 WordPress のブルートフォース攻撃から保護するプラグイン用に上記のような設定を使いました。
※接続元 IP アドレス単位でアクセスをブロックしたりするので、正しく IP アドレスを伝える必要があった。
※何もしないと CloudFront や ELB の IP アドレスがブロックされてしまって、結果すべてのアクセスがブロックされる可能性があった。