ブログ | NGINX

NGINX Plus と fail2ban による動的 IP 拒否リスト

NGINX-F5 水平黒タイプ RGB の一部
リアム・クリリー サムネイル
リアム・クリリー
2017年9月19日公開
写真: アーノルド・ラインホールド – 自身の作品、CC BY‑SA 3.0

気づいていないかもしれませんが、あなたのウェブサイトは常に脅威にさらされています。 WordPress を実行している場合、ボットがスパムを送信しようとしています。 ログインページがある場合は、ブルートフォースパスワード攻撃が行われます。 検索エンジンのスパイダーも望ましくない訪問者とみなすことができます。

望ましくない、疑わしい、悪意のあるアクティビティからサイトを守るのは簡単なことではありません。 NGINX App Protectや、パートナーから入手できるNGINX Plus 認定モジュールなどの Web アプリケーション ファイアウォールは効果的なツールであり、セキュリティ スタックの一部として検討する必要があります。 ほとんどの環境では、セキュリティが多すぎるということはなく、多層アプローチが常に最も効果的です。

このブログ記事では、Web セキュリティ スタックの別のレイヤーとしてのfail2banの使用について説明します。 Fail2ban は、ログ ファイルを継続的に監視して疑わしいアクティビティを検出し、事前に設定された 1 つ以上のアクションを実行する侵入検知システム (IDS) です。 通常、fail2ban は失敗したログイン試行を監視し、問題のある IP アドレスを一定期間ブロック (禁止) します。 これは、ブルートフォース パスワード攻撃に対するシンプルでありながら効果的な防御です。

NGINX Plus R13では、ネイティブのキーバリュー ストアと新しい NGINX Plus API を導入しました。これにより、NGINX Plus の構成と動作を外部システムによって制御できる、豊富な動的構成ソリューションが可能になります。 このブログ記事では、fail2ban を使用して、複数の認証失敗イベントの原因となった IP アドレスからのリクエストを無視するように NGINX Plus を自動的に再構成する方法についても説明します。

エディター – NGINX Plus R16以降では、クラスター内のすべての NGINX Plus インスタンス間でキー値ストアを同期できます。 (クラスター内の状態共有は、他の NGINX Plus 機能でも利用できます。) 詳細については、ブログNGINX Plus 管理者ガイドをご覧ください。

IP 拒否リストにキーバリューストアを使用する

NGINX Plus キーバリューストアは、3 つの主な特徴を持つネイティブのインメモリストアです。

  1. キーと値のペアはJSONオブジェクトとして表現されます
  2. キーと値のペアはAPIを通じて完全に管理されます
  3. これらの値は、NGINX Plusで通常の設定変数として使用できます。

キー値ストアは、 keyval_zoneディレクティブで名前が付けられた共有メモリ ゾーンを作成することによって定義されます。 このキー値ストアには、HTTP POSTメソッドを使用して JSON オブジェクトを API に送信することで、初期値セットを設定できます。次に、 keyvalディレクティブは、ルックアップ キーとして使用する既存の変数 (例では$remote_addr ) と、そのキーの対応する値から評価される新しい変数の名前 ( $num_failures ) を定義します。

NGINX Plus キーバリューストアの設定と管理

API は、ロケーションブロックを NGINX Plus API エンドポイントとして指定することで有効になります。


server {
listen 1111;
allow 127.0.0.1; # ローカルホストからのアクセスのみを許可し、
deny all; # リモートアクセスを禁止します。

location /api {
api write=on; # 読み取り/書き込みモードの NGINX Plus API エンドポイント
}
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
listen 80;

location / {
root /usr/share/nginx/html;
if ($num_failures) {
return 403;
}
}
}

vim: syntax=nginx

キーと値のペアを追加する前に、ブラックリストストアのコンテンツを要求すると、空の JSON オブジェクトが返されます。

$ curl http://localhost:1111/api/6/http/keyvals/denylist {}

HTTP POSTメソッド ( curlコマンドの-d引数の形式) を使用して新しい JSON オブジェクトを送信し、キー値ストアに初期のキー値ペアを入力できるようになりました。 複数のキーと値のペアを空のキーと値のストアにPOST し、その後は個別に POST することができます。

$ curl -iX POST -d '{"10.0.0.1":"1"}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 201 作成されました...

キーと値のペアは、キーをnullの値でPATCH することによって削除されます。

$ curl -iX PATCH -d '{"10.0.0.1":null}' http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 コンテンツなし...

DELETEメソッドを送信することにより、すべてのキー値ペアをキー値ストアから削除できます。

$ curl -iX DELETE http://localhost:1111/api/6/http/keyvals/denylist HTTP/1.1 204 コンテンツがありません...

IP 拒否リストの簡単な実装を構成できるようになりました。


server {
listen 1111;
allow 127.0.0.1; # ローカルホストからのアクセスのみを許可し、
deny all; # リモートアクセスを禁止します。

location /api {
api write=on; # 読み取り/書き込みモードの NGINX Plus API エンドポイント
}
}
keyval_zone zone=denylist:1M;
keyval $remote_addr $num_failures zone=denylist;

server {
listen 80;

location / {
root /usr/share/nginx/html;
if ($num_failures) {
return 403;
}
}
}

vim: syntax=nginx

この構成スニペットは、ポート 80 を Web サーバーとして構成します。 18 行目では、 $num_failures変数を、 $remote_addr変数 (クライアント IP アドレス) に対応するキーと一致する、拒否リスト共有メモリ ゾーン内のキーと値のペアの値部分に評価します。 $num_failures を評価するこのプロセスは、次のように順番に表現するとより明確になります。

  1. $remote_addrの値をブラックリストのキーバリューストアに渡す
  2. $remote_addrがキーと完全に一致する場合、そのキーと値のペアの値を取得します。
  3. 値を$num_failuresとして返します

そして、クライアントのIPアドレスがキーバリューストアにPOSTされている場合、すべてのリクエストはHTTPになります。403禁止エラー。 同じ目的でマップブロックを使用する場合と比べて、このアプローチの利点は、IP 拒否リストを外部システムで制御できることです。

fail2ban を使用して IP 拒否リストを動的に管理する

前述のように、fail2ban は、ログ ファイル内の疑わしいアクティビティや悪意のあるアクティビティを検出し、システムを保護するためのアクションを実行するためによく使用されます。 デフォルトのアクションでは、ログに記録された IP アドレスから発信されたすべてのパケットをドロップするようにiptablesを構成します。 このアプローチは、悪意のある行為者をブロックするのには効果的ですが、特にパスワードを忘れた場合、正規のユーザーにとっては非常に悪いユーザー エクスペリエンスをもたらします。 これらのユーザーにとっては、Web サイトが単に利用できないように見えます。

次の設定は、問題のある IP アドレスを NGINX Plus のブラックリストキー値ストアに送信する fail2banアクションを示しています。 NGINX Plus は、より役立つエラー ページを表示すると同時に、その IP アドレスにリクエスト レート制限を適用します。これにより、アクションが攻撃の一部である場合、攻撃は効果的に無効化されます。

NGINX Plus と同じホストに fail2ban がデフォルトでインストールされ、すべての設定が/etc/fail2banディレクトリにあることを前提としています。

次の fail2ban アクションは、NGINX Plus API を使用して、上記の簡単な例と同じように、拒否リストのキー値ストア内の「禁止された」 IP アドレスを追加および削除します。 nginx-plus-denylist.confファイルを/etc/fail2ban/action.dディレクトリに配置します。


[定義]
actionban = curl -s -o /dev/null -d '{"":""}' http://localhost:1111/api/6/http/keyvals/denylist
actionunban = curl -s -o /dev/null -X PATCH -d '{"":null}' http://localhost:1111/api/6/http/keyvals/denylist

次の fail2ban jail は、 NGINX のHTTP 基本認証モジュールを使用して失敗したログイン試行を検出し、 nginx-plus-denylist.confで定義されたアクションを適用する組み込みフィルターを有効にします。 他にも多くの組み込みフィルターが用意されており、NGINX アクセス ログ内の不要なアクティビティを検出するために簡単に作成できます。


[デフォルト]
bantime = 120
banaction = nginx-plus-denylist

[nginx-http-auth]
enabled = true

次の NGINX Plus 構成スニペットは、デフォルトの「NGINX へようこそ」ページに HTTP 基本認証を適用します。 password_site.confファイルを/etc/nginx/conf.dディレクトリに配置します。


server {
listen 1111;
allow 127.0.0.1; # ローカルホストからのアクセスのみを許可し、
deny all; # リモートアクセスを禁止します。

location /api {
api write=on; # 読み取り/書き込みモードの NGINX Plus API エンドポイント
}
}

keyval_zone zone=denylist:1M state=denylist.json;
keyval $remote_addr $num_failures zone=denylist;

limit_req_zone $binary_remote_addr zone=20permin:10M rate=20r/m;

server {
listen 80;
root /usr/share/nginx/html;

location / {
auth_basic "closed site";
auth_basic_user_file users.htpasswd;

if ($num_failures) {
^.* /banned.html を書き換えます。
}
}

location = /banned.html {
limit_req zone=20permin burst=100;
}
}

vim: syntax=nginx

11 行目は、上記のdenylist_keyval.confと同様にkeyval_zone を定義しますが、 stateパラメータが追加されています。このパラメータは、キー値ストアの状態が保存されるファイルを指定するため、NGINX Plus を停止して再起動しても保持されます。 キー値ストアは、状態ファイルを指定せずに、定期的な構成の再読み込み後も保持されます。 (1行目から10行目は表示されていませんが、 denylist_keyval.confと同じです。)

14 行目は、 20perminという共有メモリ ゾーンを作成し、各クライアント IP アドレスに対して 1 分あたり 20 件の最大要求レートを指定します。 このレート制限は、fail2ban によって IP アドレスが拒否リストに追加されたときに適用されます (行 30)。

serverブロックは、デフォルトのポート (80) でリッスンする Web サーバーを定義します。すべてのリクエストはlocation /ブロック (行 20) によって照合されます。 ルートディレクティブ (18 行目) は、Web コンテンツが存在する場所を指定します。 21 行目から 22 行目では、このサイトには HTTP 基本認証が必要であり、承認されたユーザーのパスワードのデータベースがusers.htpasswdファイルにあることを指定しています。 auth_basic_user_fileディレクティブのドキュメントでは、そのファイルの形式について説明しています。 この構成はテスト目的のため暗号化されていないことに注意してください。HTTP 基本認証を使用するすべての実稼働 Web サイトでは、トランスポート セキュリティのためにSSL/TLS を使用する必要があります。

クライアントのIPアドレスがブラックリストに登録されている場合(24~26行目)、HTTP403エラー コードでは、リクエストを/banned.htmlに書き換え、その URI と完全に一致するロケーションブロックで処理します (29 行目)。 これにより、ログイン失敗回数が多すぎるためユーザーの IP アドレスがブロックされたことを説明する静的 Web ページが返されます。 また、悪意のあるクライアントがシステム リソースを不必要に消費するのを防ぐための制限的なレート制限 (行 30) も適用します。

IP アドレスが拒否リストに登録されたときにユーザーに表示されるページ

(このサンプル Web ページの HTML は、この投稿の GitHub リポジトリでbanned.htmlとして入手できます。)

次のようにして、連続したログイン試行の失敗とそれに対応する fail2ban アクティビティをシミュレートできます。

$ curl -i http://admin:password@www.example.com/HTTP/1.1 401 権限がありません...
$ curl -i http://admin:admin@www.example.com/
HTTP/1.1 401 権限がありません...
$ curl -i http://admin:pass1234@www.example.com/
HTTP/1.1 401 権限がありません...
$ curl -i http://admin:letmein@www.example.com/
HTTP/1.1 401 権限がありません...
$ curl -i http://admin:fred@www.example.com/
HTTP/1.1 401 権限がありません...
$ http://admin:P@ssw0rd@www.example.com/ をカールします
<!DOCTYPE html>
<html>
<head>
<title>禁止</title>
...
$ テール -f /var/log/fail2ban.log
2017-09-15 13:55:18,903 fail2ban.filter [28498]: 情報 [nginx-http-auth] 172.16.52.1 が見つかりました 2017-09-15 13:55:28,836 fail2ban.filter [28498]: 情報 [nginx-http-auth] 172.16.52.1 が見つかりました 2017-09-15 13:57:49,228 fail2ban.filter [28498]: 情報 [nginx-http-auth] 172.16.52.1 が見つかりました 2017-09-15 13:57:50,286 fail2ban.filter [28498]: 情報 [nginx-http-auth] 172.16.52.1 が見つかりました 2017-09-15 13:57:52,015 fail2ban.filter [28498]: 情報 [nginx-http-auth] 172.16.52.1 が見つかりました 2017-09-15 13:57:52,088 fail2ban.actions [28498]: 通知 [nginx-http-auth] 172.16.52.1 を禁止 2017-09-15 13:59:52,379 fail2ban.actions [28498]: 通知 [nginx-http-auth] 172.16.52.1 の禁止を解除

この動的 IP 拒否リスト ソリューションは、これ以上の構成変更なしで実行できるようになりました。 Fail2ban は NGINX ログ ファイルを監視し、API を使用して禁止された IP アドレスを NGINX Plus キー値ストアに追加します。120 秒後 ( jail.localで設定された禁止時間)、問題のある IP アドレスは再び NGINX Plus API を使用して拒否リストから削除され、そのアドレスからのログイン試行が再び受け入れられます。

NGINX Plus API とキー値ストアにより、このタイプの統合が可能になります。 NGINX Plus 構成ファイルの構築やリロードの実行を必要とせずに、高度な構成ソリューションを実現できるようになりました。 これらの機能をどのように活用して独自の動的構成ソリューションを作成しているのか、ぜひお聞かせください。 下記にコメントを追加してください。

NGINX Plus をお試しいただくには、今すぐ30 日間の無料トライアルを開始するか、弊社にお問い合わせの上、使用事例についてご相談ください


「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"