先に要点
- ネットワーク機器の ACL(Access Control List)は、通信を許可するか拒否するかのルールを並べた一覧です。ルーターやスイッチのインターフェースに、入ってくる向き・出ていく向きを指定して適用します。
- 押さえるべき性質は3つです。上から順に照合して最初に一致したルールで決まる、どのルールにも一致しない通信は拒否される、ステートレスな ACL では戻りの通信も別に許可が要る。
- この3つを Linux のパケットフィルタで再現しました。同じ2行のルールでも、順番を入れ替えるだけで接続できる・できないが逆転し、入りだけを許可した設定では要求は届いているのに応答が返らず接続できませんでした。
- Cisco の ACL と AWS のネットワーク ACL の公式ドキュメントも、同じ性質を説明しています。ルールを足すときは末尾に足すのではなく、位置を決めて差し込むのが事故を防ぐ要点です。
「ACL で許可したはずなのに繋がらない」「ルールを1行足したら別の通信が止まった」。ネットワーク機器の ACL で起きるトラブルの多くは、設定の書き間違いではなく、ACL の評価の仕方を取り違えていることが原因です。
この記事は、ネットワーク機器の ACL の基本を整理したうえで、つまずきやすい3つの性質を実際に動かして確かめた結果をまとめます。Cisco の ACL と AWS のネットワーク ACL の公式ドキュメントで、同じことが書かれている箇所もあわせて示します。
ネットワーク機器の ACL とは
ACL は、どの通信を通し、どの通信を止めるかを1行ずつ書いたルールの一覧です。1行ごとに「許可(permit)」か「拒否(deny)」と、対象の条件(送信元・宛先のアドレス、プロトコル、ポート番号など)を書きます。
作った ACL は、機器のインターフェースに向きを指定して適用します。Cisco の設定例では次のようになっています。
interface Ethernet0/0
ip address 10.1.1.1 255.255.255.0
ip access-group 1 in
access-list 1 permit 10.1.1.0 0.0.0.255
最後の行が ACL の中身で、3行目の in が「このインターフェースに入ってくる通信に適用する」という意味です。出ていく通信に適用するなら out を指定します。
標準 ACL と拡張 ACL
Cisco の番号付きの ACL には、番号の範囲で2つの種類があります。
| 種類 | 番号の範囲 | 書ける条件の例 |
|---|---|---|
| 標準 ACL | 1〜99、1300〜1999 | access-list 1 permit 10.1.1.0 0.0.0.255(アドレスの範囲) |
| 拡張 ACL | 100〜199、2000〜2699 | access-list 101 deny icmp any 10.1.1.0 0.0.0.255 echo(プロトコル・送信元・宛先・種類) |
Cisco のドキュメントは、ACL を通信の送信元にもっとも近いインターフェースに適用するのがよいとしています。止める通信が、途中の回線や機器を通る前に落とせるためです。
ワイルドカードマスク
上の例の 0.0.0.255 は、サブネットマスクではなくワイルドカードマスクです。Cisco のドキュメントの説明では、0 のビットは「完全に一致すること」、1 のビットは「無視してよい」を表します。
サブネットマスクから求めるときは、255.255.255.255 から引きます。255.255.255.255 − 255.255.255.0 = 0.0.0.255 なので、10.1.1.0 0.0.0.255 は「先頭3つの数字が 10.1.1 で、最後の数字は何でもよい」という範囲になります。
試した環境
ネットワーク機器の代わりに、Docker のコンテナに Linux のパケットフィルタ(iptables v1.8.13)を入れて、ACL と同じ動きを再現しました。
サーバー役
ルールを設定する側
ポート80で Web サーバーを動かし、iptables でルールを設定します。
クライアント役
要求を送る側
別のコンテナから、4秒でタイムアウトする設定で HTTP の要求を送ります。
iptables は、何も設定しなければ通信を許可する(既定の方針が ACCEPT)点が、多くのネットワーク機器の ACL と違います。そこで、暗黙の拒否を試すときは既定の方針を DROP(拒否)に変えています。ルールの評価の考え方は同じなので、ACL の性質を確かめる用途には十分です。
性質1:上から順に照合し、最初に一致したルールで決まる
ポート80について、「すべて拒否」と「クライアントのネットワークからは許可」の2行を用意し、順番だけを入れ替えて試しました。
まず、拒否を上に置いた場合です。
iptables -A INPUT -p tcp --dport 80 -j DROP
iptables -A INPUT -p tcp --dport 80 -s 172.28.0.0/16 -j ACCEPT
結果は接続できませんでした。ルールごとの一致件数を表示すると、理由がはっきり分かります。
num pkts bytes target prot opt in out source destination
1 4 240 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
2 0 0 ACCEPT tcp -- * * 172.28.0.0/16 0.0.0.0/0 tcp dpt:80
1行目の拒否に4つのパケットが一致し、2行目の許可には1つも届いていません。許可のルールは書いてあるのに、一度も評価されていないということです。
次に、同じ2行の順番を入れ替えます。
num pkts bytes target prot opt in out source destination
1 7 454 ACCEPT tcp -- * * 172.28.0.0/16 0.0.0.0/0 tcp dpt:80
2 0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
今度は接続できました。1行目の許可に一致し、2行目の拒否は評価されていません。
Cisco のドキュメントも、ルーターは一致するものが見つかるまで順に探すと説明しています。AWS のネットワーク ACL も、番号の小さいルールから評価し、一致したらそれ以降のルールは評価しないと明記しています。
ルールを足すとき、つい末尾に書き足してしまいがちです。広い範囲の拒否がすでにあると、末尾の許可は一度も評価されずに終わります。足す前に、既存のルールのどこに入れるかを決めてください。
性質2:どのルールにも一致しない通信は拒否される
次に、ポート22(SSH)だけを許可するルールを書き、既定の方針を拒否にしました。ポート80についての拒否は1行も書いていません。
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -P INPUT DROP
結果は接続できませんでした。
Chain INPUT (policy DROP 4 packets, 240 bytes)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
どのルールにも一致しなかった4つのパケットが、既定の方針(policy DROP)で落ちています。
ネットワーク機器の ACL では、これが最初から組み込まれています。Cisco のドキュメントは「すべての ACL の末尾には、明示的に許可されていない通信に対する暗黙の拒否がある」と説明し、許可のルールが1行も無ければすべての通信が止まるとしています。AWS のネットワーク ACL にも、番号がアスタリスク(*)の拒否のルールがあり、削除できません。
どこかで止められているのではなく、そもそも許可が無い可能性を先に疑うと早く原因にたどり着けます。
性質3:ステートレスな ACL では、戻りの通信も許可が要る
3つ目は、もっとも気づきにくい性質です。通信は行きと帰りの両方があって初めて成り立ちます。
入りだけ許可すると、要求は届くのに応答が返らない
サーバー側で、入ってくるポート80は許可し、出ていく通信は既定で拒否にしました。
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT DROP
結果は接続できませんでした。出ていく方向の一致件数を見ると、原因が分かります。
Chain OUTPUT (policy DROP 8 packets, 480 bytes)
num pkts bytes target prot opt in out source destination
サーバーが返そうとした応答の8つのパケットが、出口で捨てられています。要求はサーバーに届いているのに、クライアントには何も返ってこないので、クライアントからはタイムアウトにしか見えません。
戻りの通信を許可するルールを足す
出ていく方向に「送信元ポートが80の通信は許可」を1行足すと、接続できるようになりました。
iptables -A OUTPUT -p tcp --sport 80 -j ACCEPT
num pkts bytes target prot opt in out source destination
1 6 508 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp spt:80
このように、通信の状態を覚えずに1パケットずつ判断するのがステートレスなフィルタです。行きを許可しても、帰りは別のルールで許可しなければ通りません。
状態を覚えるフィルタなら、戻りは自動で通る
最後に、出ていく方向を「すでに確立した通信の続きなら許可」という1行にしました。
iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
これでも接続できました。ポート番号を書かなくても、入りを許可した通信の応答だから通す、という判断をしています。これがステートフルなフィルタです。
AWS では、セキュリティグループとネットワーク ACL がちょうどこの違い
AWS の公式ドキュメントは、2つの違いを次のように整理しています。
| セキュリティグループ | ネットワーク ACL | |
|---|---|---|
| 適用する単位 | インスタンス | サブネット |
| ルールの種類 | 許可のみ | 許可と拒否 |
| ルールの評価 | すべてのルールを見てから判断 | 番号の小さい順に、一致するまで |
| 戻りの通信 | 自動で許可(ステートフル) | 明示的な許可が必要(ステートレス) |
ネットワーク ACL で戻りの通信を許可するときに難しいのが、応答の宛先のポート番号が決まっていないことです。応答は、要求を送った側が一時的に選んだポート(エフェメラルポート)に返ります。AWS のドキュメントでは、その範囲は送る側によって違うと説明されています。
| 要求を送る側 | 使うポートの範囲 |
|---|---|
| 多くの Linux カーネル | 32768〜61000 |
| Windows Server 2008 以降 | 49152〜65535 |
| Elastic Load Balancing・NAT ゲートウェイ・Lambda | 1024〜65535 |
実際にはさまざまなクライアントが来るので、AWS は1024〜65535 を開けたうえで、その範囲の中で止めたいポートがあれば、拒否のルールを許可より前に置く方法を示しています。ここでも性質1の「順番」が効いてきます。
なお AWS は、通信の制御にはセキュリティグループを主に使い、ネットワーク ACL は大まかな制御や、特定の通信を止める2つ目の守りとして使うよう勧めています。
ルールを足すときは、位置を決めて差し込む
性質1のとおり、ACL はどこに書くかで結果が変わります。Cisco の名前付き ACL には、そのためのシーケンス番号の仕組みがあります。
Cisco のドキュメントによると、この機能が入る前は、ACL のルールは末尾にしか足せず、途中に入れたいときは ACL 全体を設定し直す必要がありました。今は、番号を指定せずに入れたルールには10から10刻みで番号が振られ、間の番号を指定すればその位置に差し込めます。
Device(config)# ip access-list standard tryon
Device(config-std-nacl)# 15 permit 10.5.5.5 0.0.0.255
Device(config-std-nacl)# exit
10番と20番のルールがある ACL に、15番として差し込む例です。番号が詰まってきたら、ip access-list resequence コマンドで振り直せます。また、IOS XE 16.12 からは、各ルールに説明(remark)を付けられます。
AWS のネットワーク ACL も、ルール番号は1〜32766で、後から間に入れられるように10や100の刻みで作ることを勧めています。
変更するときに気をつけること
遠隔から機器を操作しているときに、その通信を止めるルールを適用すると、機器に入れなくなります。適用前に、自分の接続元が許可の範囲に入っているかを確かめます。
切り分け
ルールごとの一致件数で確かめる
今回の実験でも、どの行に一致したかは件数を見るとすぐ分かりました。使っている機器で、ルールごとの一致件数を表示する方法をマニュアルで確認しておくと、切り分けが速くなります。
記録
なぜそのルールがあるかを残す
理由の分からないルールは誰も消せず、並びが複雑になっていきます。remark などの説明の機能を使います。
戻りの通信
戻りの通信を忘れない
「要求は届いているのに応答が無い」ときは、出口側のルールと、機器がステートレスかどうかを確認します。
よくある質問
最後に「すべて拒否」を書く意味はありますか
暗黙の拒否があるので、動きは変わりません。ただ、明示的に書いておくと、ACL を読む人に「ここに無いものは止める設計だ」という意図が伝わります。
ACL とファイアウォールは何が違いますか
どちらも通信を許可・拒否する仕組みで、ACL はルールの一覧そのものを指す言葉です。今回の実験のように、状態を覚えないで1パケットずつ判断するものと、確立した通信を覚えて戻りを自動で通すものがあるので、使っている機器や機能がどちらの動きをするかを確認するのが実務では大切です。
AWS ではセキュリティグループだけで足りますか
AWS のドキュメントは、セキュリティグループだけでインスタンスを守ることもできるとしたうえで、ネットワーク ACL を追加の守りとして使えると説明しています。ネットワーク ACL はサブネット全体にかかるので、セキュリティグループの設定を誤ったインスタンスがあったときの保険になります。
ネットワーク ACL で止められない通信はありますか
AWS のドキュメントでは、Route 53 Resolver への DNS の問い合わせや、インスタンスメタデータサービスへの通信などは、ネットワーク ACL では止められないとされています。
まとめ
- ACL は許可・拒否のルールの一覧で、インターフェースに向きを指定して適用する
- 上から順に照合し、最初に一致したルールで決まる。同じ2行でも順番で結果が逆転した
- どれにも一致しない通信は拒否される。拒否を書いていなくても止まる
- ステートレスな ACL では戻りの通信も許可が要る。入りだけ許可すると、要求は届いても応答が出口で捨てられる
- AWS ではセキュリティグループがステートフル、ネットワーク ACL がステートレス
- ルールは末尾に足さず、シーケンス番号などで位置を決めて差し込む
参考リンク
- Cisco:Configure and Filter IP Access Lists
- Cisco:IP Access List Entry Sequence Numbering(IOS XE 16.12)
- AWS:Control subnet traffic with network access control lists
- AWS:Custom network ACLs for your VPC(エフェメラルポート)
- AWS:Infrastructure security in Amazon VPC(セキュリティグループとネットワーク ACL の比較)
- AWSで最初に覚えたい基本用語まとめ|EC2・IAM・S3・VPCのつながりを整理
- 用語集:ACL
- 用語集:ファイアウォール
- 用語集:VLAN