---
title: "サーバーへの接続を絞り、アプリの前に WAF を置く"
description: "erpc worker は最初のデプロイからサーバーへの接続を絞り、erpc.toml にテーブルを 1 つ書けば公開 API の前に WAF を置きます。WAF がすることとしないことも、この頁で確かめられます。"
canonical: "https://erpc.global/ja/doc/erpc-cli/worker-firewall-and-waf/"
locale: "ja"
---

# サーバーへの接続を絞り、アプリの前に WAF を置く

`erpc worker` は、最初のデプロイから、SSH を自分のアドレスからだけ受け入れ、ほかの受信接続をすべて落とします。`[waf]` テーブルを書くと、API の前にフィルタリングするプロキシが置かれます。

このページは、プレリリースの ERPC CLI `v0.1.0-rc.4` について説明しています。

## ファイアウォールで接続を絞る

デプロイは、アプリが初めて起動する前に、サーバーのファイアウォールを設定します。

- SSH は `firewall.ssh_allow` と、デプロイを実行したアドレスからだけ受け入れます。
- ほかの受信接続はすべて落とします。例外は `firewall.app_allow` からの `run.port` です。WAF の後ろにあるアプリでは、代わりに WAF のポートが例外になります。ただしそれは、サーバーの WAF が、`[waf]` が今作るルールをすでに動かしている場合だけです（[下](https://erpc.global/ja/doc/erpc-cli/worker-firewall-and-waf/#アプリの前に-waf-を置く)）。

変更はそれぞれ 120 秒のロールバックのタイマーの下で行われ、新しい接続が入れたときにだけ確定します。そのため、間違った変更で締め出されることはありません。

ただし、許可リストは、それを作ったときのアドレスが変わっても残ります。自宅のアドレスが変わった場合、戻る手段は、ERPC のサーバーなら ERPC のサポート、自分のサーバーならプロバイダーのコンソールです。自宅のアドレスを頼りにする前に、サーバーに届く 2 つ目のアドレス（オフィス、VPN の出口）を `firewall.ssh_allow` に入れておいてください。

アドレスが変わるなら、管理者が使う固定のアドレスをすべて加えます。

```bash
erpc worker firewall allow --to ssh 203.0.113.10 198.51.100.7
```

| コマンド | すること |
| --- | --- |
| `erpc worker firewall list` | サーバーに届いてよい相手を、`erpc.toml` とサーバーのテーブルを並べて表示します |
| `erpc worker firewall allow --to ssh\|app <addresses>` | アドレスを受け入れ、それを適用します。SSH は単一のアドレスからだけ許可でき、ネットワークを受け付けるのは `--to app` のときだけです |
| `erpc worker firewall deny --to ssh\|app <addresses>` | アドレスの受け入れをやめます |
| `erpc worker firewall sync` | サーバーのファイアウォールを、`erpc.toml` に書かれたとおりに戻します |

## アプリの前に WAF を置く

`erpc.toml` にテーブルを 1 つ加えます。

```toml
[waf]
port = 8080
```

すると `erpc worker deploy` は、サーバーで HTTP のリバースプロキシを、専用の systemd ユニット（`erpc-waf-<app>-<env>`）で動かします。このプロキシは特権のない動的ユーザーとして動き、鍵もシークレットも、ワーカーのものは何も持たず、メモリは 1 GiB に抑えられます。`waf.port` で待ち受け、各リクエストをアプリより先に読み、ルールが名指すものを拒否し、残りを `127.0.0.1:<run.port>` のアプリへ転送します。

- **アプリはループバックに置いたままです。** アプリ自身のポートが外に開くことはありません。`erpc worker api` とヘルスチェックは、WAF を通らずにループバックでアプリに届きます。
- **接続してよい相手は、今もファイアウォールのリストが決めます。** そのリストに「全員」と書けるのは、WAF のポートだけです。

  ```sh
  erpc worker firewall allow --to app 0.0.0.0/0     # and ::/0
  ```

  誰も入っていなければ、WAF は待ち受けますが誰も入れず、デプロイがそのことを伝えます。
- **ポートが開くのは、WAF がそこで待ち受けてから**で、それより前には開きません。WAF が立ち上がらなかったデプロイは、理由を示して失敗し、アプリは動いたままです。
- **`[waf]` を変えたとき**、新しいルールをデプロイが置き終えるまで、ポートは新しい相手には開きません。
- **`erpc.toml` から `[waf]` を外して**デプロイすると、サーバーから WAF が取り除かれます。

## WAF の設定

必須なのは `port` だけです。すべてのキーと、その既定値は次のとおりです。

```toml
[waf]
port = 8080          # the public port, 1024 or above; the app keeps [run] port
methods = ["GET", "POST", "PUT", "DELETE"]
allow_paths = []     # every path
deny_paths = []
max_body_bytes = 1048576
max_header_bytes = 16384
rate = { per_second = 50, burst = 100 }
max_connections_per_address = 32
ban = { after = 100, window_secs = 60, for_secs = 600 }
block_probes = true
header_timeout_secs = 10
body_timeout_secs = 30
upstream_timeout_secs = 30
new_connections = { per_second = 50, burst = 100 }
```

| キー | すること | 拒否されたものが受け取るもの |
| --- | --- | --- |
| `methods` | 転送するメソッド。大文字で書きます（最大 16 個。`CONNECT` は不可） | 405 と `Allow` |
| `allow_paths` | 転送するパス。何も並べなければ、すべてのパスです。パターンは 1 つのパスか、`*` で終わればその接頭辞の下のすべてです。`/api/*` は `/api` とその下のすべてを指します。パーセントデコードを 1 回したパスと、大文字・小文字を区別して比べます | 404 |
| `deny_paths` | 拒否するパス。書き方は同じで、先に調べます | 403 |
| `max_body_bytes` | リクエストのボディの上限。最大 64 MiB | 413 |
| `max_header_bytes` | リクエスト行とヘッダーを合わせた上限。1024 から 65536 | 431 |
| `rate` | 1 つのアドレスからの 1 秒あたりのリクエスト数と、その率が掛かる前に同時に受ける数 | 429 と `Retry-After` |
| `max_connections_per_address` | 1 つのアドレスが同時に持てる接続数 | 応答せずに接続を閉じる |
| `ban` | `window_secs` の間に `after` 件のリクエストを止めたら、そのアドレスを `for_secs` の間（最大 30 日）拒否します | 応答せずにその接続を閉じる |
| `block_probes` | スキャナーが求めるもの（`/.env`、`/.git`、`/wp-login.php` など）を拒否します | 403 |
| `header_timeout_secs` | クライアントがリクエストのヘッダー部分を送り終えるまでの時間。1 から 120。ボディの途中で止まったクライアントや、応答を受け取らなくなったクライアントを待つ時間でもあります | ヘッダー部分は 408。止まったボディは 400。受け取られない応答は接続を閉じる |
| `body_timeout_secs` | リクエストのボディ全体にかけてよい時間。1 から 600。WAF がアプリを待つ時間は数えません | 408 |
| `upstream_timeout_secs` | アプリが接続を受けて応答を始めるまでの時間。1 から 600 | 504。アプリが待ち受けていなければ 502 |
| `new_connections` | ファイアウォール自身の上限。1 つのアドレスから WAF のポートへの 1 秒あたりの新しい TCP 接続数で、上限を超えた分は、プロキシが見る前に nftables が落とします。nftables のアドレスの集合がいっぱいの間は、そこに入っていないアドレスは数えられないので、集合に空きが出るまで、この上限はそのアドレスに掛かりません。プロキシ自身の上限（`rate`、`max_connections_per_address`、`ban`）は引き続き掛かります | パケットを落とす |

`rate`、`max_connections_per_address`、`ban`、`new_connections` は `false` で無効にでき（`ban = false`）、書かなかったキーは既定値になります。`erpc worker check` は、どのデプロイよりも前に、`[waf]` テーブルが何をするか、どこが間違っているかを伝えます。

## 設定がなくても WAF が行うこと

- 曖昧さのない 1 つの HTTP/1 リクエストとして読めないリクエストには 400 を返します。正規形でないパスにも 400 を返します。
- プロトコルの切り替え（`Upgrade`）を求めるリクエストには 501 を返します。
- 拒否はどれも ban の件数に数えられ、拒否するたびにその接続を終えます。
- アドレスとは、IPv4 アドレス 1 つ、または IPv6 の `/64` 1 つです。
- WAF は、クライアントからプロキシへの接続に属するヘッダー（`Connection` とそれが名指すヘッダー、`Keep-Alive`、`Proxy-Authenticate`、`Proxy-Authorization`、`Proxy-Connection`、`TE`、`Trailer`）、`Expect`、名前に下線を含むヘッダー、クライアント自身が付けた `Forwarded`、`X-Real-IP`、`X-Forwarded-Proto` を転送しません。`X-Real-IP` には接続元のアドレスを設定し、そのアドレスを `X-Forwarded-For` の末尾に加えます。`X-Forwarded-For` のうち、プロキシ自身が書いたのは最後の項目だけです。

## WAF の動きを見る

| コマンド | 表示するもの |
| --- | --- |
| `erpc worker status` | WAF のポート、転送先、いつからか、そのとき以降の件数 |
| `erpc worker firewall list` | WAF のポートに届いてよい相手と、ルールごとに止めたもの |
| `erpc worker logs --waf` | 止めたリクエスト 1 件ずつ。ルール、ステータス、クライアントのアドレス、メソッド、ターゲットの先頭 200 バイト |

転送したリクエストはログに残らず、ヘッダーやボディがログに残ることはありません。止めたリクエストのクエリは、200 バイトに入る範囲でログに残ります。クライアントが送ったものがターミナルに書き込まれることはありません。WAF はジャーナルを表示可能な ASCII で書きます。

## WAF がしないこと

- **TLS はありません。** ポートでは素の HTTP/1.1 で通信します。
- **HTTP/2 はありません。** HTTP/2 には 400 を返します。
- **WebSocket も、ほかのどの `Upgrade` もありません。** 501 を返します。
- **ボディの中は見ません。** ボディは大きさを測るだけで、読みません。スキャナーの一覧は短く、雑音を減らすフィルターであって保証ではありません。入力の検査は、引き続きアプリが行います。
- **認証はありません。** アプリを呼んでよい相手は、アプリと、ファイアウォールのリストが決めます。
- **1 つのアドレスを 1 つのクライアントとして扱います。** 1 つの NAT、プロキシ、CDN の後ろにいるクライアントは、率も ban も共有します。多くのアドレスを持つクライアントは、アドレスの数だけ率を持ちます。
- **ban と件数は、WAF が再起動すると忘れられます。**
- **覚えておけるアドレスは最大 8192 個**で、同時に持てる接続は最大 1024 です。
- **待ち受けるのは IPv4 だけです。** そのため、`app_allow` の IPv6 の項目は、今はどこにも届きません。
- **パスは、デコードした後のバイト列として比べます。** パスをもっと緩く読むアプリ（大文字・小文字を区別しない、`;` のパラメーターを扱う、2 回デコードする）には、`deny_paths` が覆わない名前のまま届くことがあります。`allow_paths` にはこの隙間はありません。並べていないものは転送しません。
- **読むのが遅いクライアントにも最後まで応答します。** どれほどゆっくりでも応答を受け取り続けるクライアントには、最後まで応答を返します。
- **大量のリクエストで大きなアップロードが途切れることがあります。** WAF は 1 つのスレッドで動くので、大量のリクエストが来ると、大きなアップロードが 408 で途切れることがあります。大きなボディを受けるアプリは、`max_body_bytes` と一緒に `body_timeout_secs` も上げてください。

[ワーカーのガイド](https://github.com/elsoul/erpc-cli/blob/v0.1.0-rc.4/docs/worker.md)には、`app_allow` の変更が次のデプロイまで保留される場合を含め、すべてのルールが載っています。
