タイトルの通り、ELB配下のnginxで正規表現を使ったIP制限をする必要があったのでメモ。
環境
- AmazonLinux
- nginx1.8.0
- ELBに流れたトラフィックがEC2に流れるように設定
前提知識
- ELBを経由するとアクセス元のIPアドレスはリクエストヘッダのX-Forwarded-Forに記載される。nginxでは組み込み変数として$http_x_forwarded_forとして取得可能
- 正規表現でないIPアドレスの許可はallow,denyディレティブを設定することで可能(そもそもSecurityGroupで制限はできる)
- nginxではsetディレクティブを利用することで自分で変数を設定することが出来る
nginx.conf
# example
set $foo "Hello World!";
- ifディレクティブを使うことで条件分岐が出来る。ただし、公式サイトでも利用時の注意が書いてあるので一度利用する場合には見ておいたほうが良い(次項で記載)
ifディレクティブ利用時の注意点
以下の公式記事にあるようにifディレクトリを利用の際には注意が必要。
具体的には以下のような副作用がある
- locationコンテキスト内での利用で意図しない動作になる場合がある(例が記載されている。2つヘッダを加えたつもりが1つしか追加されていない)
- 最悪、SIGSEGVでクラッシュ
とはいえどうしても使いたい場合にはどうするか
-
try_files
を使う - ifディレクティブ内で
return
とrewrite...last
を利用する(これは安全と書いてある) - locationコンテキストでなく、serverコンテキストで利用できないか検討する
- それでもどうしても使いたい場合、テストをしっかりする
という感じだと思います。
なので、ifは絶対使うなということでなく、可能な限りlocationディレクティブで使うのはやめてどうしても使いたい場合も、しっかりテストしてねという話だと思います。
やってみる
今回はデフォルトの状態のnginx.confのserverディレクティブを変更し、正規表現で指定されたIP以外のアクセスの場合、ステータスコード403を返却するようにしてみます。
/etc/nginx/nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
set $invalid_access 1;
# ELB health check
if ($http_user_agent = "ELB-HealthChecker/1.0") {
set $invalid_access 0;
}
# allow IPs
if ($http_x_forwarded_for ~* ^111\.111\.111\.11[0-9]$) {
set $invalid_access 0;
}
if ($invalid_access) {
return 403;
}
以下、ざっと説明。
- 変数 $invalid_access を独自で定義し、デフォルトは1(不正)とする
- UserAgentで ELB-HealthChecker/1.0 となっているものはELBのヘルスチェックなので0(許可)(もっと厳密にやる場合、上記ELBのUAかつアクセスするlocationがヘルスチェック用のパスの場合のみOKとすると良いと思います)
- 正規表現でアクセス可能なIPが$http_forwarded_forに指定されいている場合も0(許可)
- $invalid_accessが1の場合、不正アクセスとみなしステータスコード403を返却する
試しにnginxのインストールしたEC2からELB経由でアクセスしようとすると403となり、正規表現で記載したIPアドレスからのアクセスの場合、HTMLが取得できます。
その他条件を増やす場合も同じようにifディレクティブを増やせば大丈夫かと思います。