多くのNW機器やFW、OS、サーバソフトウェアにはアクセス制御(機構)が実装されています。
このアクセス制御機構は多くの場合、Allow list(ホワイトリスト)やDeny list(ブラックリスト)あるいはその両方を含むルールリスト、俗にいうACL(Access Control List)をもってて、アクセスして来たクライアントのIPやポートと、ACLに書かれているルールを比較して、OK、NGを判別しています。
1つのACLの実装しか扱ってないと中々気がつかないんですが、実はACLの評価方法は実装毎に異なります。
実装毎の具体的な仕様は必要になったらその場で調べればいいんですが、少なくとも「実装ごとに評価方法は違う」と言うことだけは知っておかないと、慣れてない実装でACLを操作した時に嵌ります。
ポイント
新しいシステムのACLを操作するなら下記はの3つは確認した方が良いと思います。特にハマりやすいのが最初の2つです。新しい実装を触る場合は確認しておきましょう。
- デフォルトの挙動(デフォルトポリシー)
- マッチ後の挙動(ファーストマッチかラストマッチか)
- ステートフル型かステートレス型か
デフォルトの挙動(デフォルトポリシー)
デフォルトの挙動とは「ACL内で明示したルールにマッチしなかったもの」に対する扱いです。例えば、下記のようなACLのところに10.0.5.15のIPが来た場合ですね。
192.168.0.0/24 → 不許可
172.16.0.0/12 → 許可
たまに勘違いしてる方がいるんですが、「ACLにルールを書いてない=暗黙のDeny(Block)」とは限りません。そう言う実装もありますが、全てではありません。
例えばLinuxで使われてるiptablesやnftables、FreeBSDのipfやpfのデフォルト設定は「暗黙のAllow (Permit)」です。ブロードバンドルータの簡易的なFirewall(FW)もデフォルトはAllowだったりします。Apache HTTPdなんかは自分で定義したOrderで、デフォルトのポリシーがAllowかDenyかが決まります。
まぁ、こんな感じでデフォルトAllowの実装は意外とあります。
デフォルトの動作についてちゃんと確認しとかないと、Allow List(ホワイトリスト)で作ったつもりのFirewallが、Default Allowの所為でガバガバになってて、全くアクセス制御されてなかった。とか言う事故に繋がります。
ちなみに、nftablesやiptablesに関してはデフォルトポリシーをDENYやREJECTに変更出来ます。BSDのpfやipfilterもたぶんできると思いますが知りません。
評価後の挙動(ファーストマッチかラストマッチか)
評価後の挙動とはマッチしたあとどうなるかと言うことですね。言い換えるとファーストマッチ型かラストマッチ型ということです。
下記の例で192.168.0.1のIPアドレスを持つクライアントがアクセスして来た時にどうなるかを考えます。
192.168.0.0/24 → 不許可
172.16.0.0/12 → 許可
192.168.0.1/32 → 許可
※DefaultはDenyとします。
ここで、「192.168.0.1は192.168.0.0/24に含まれるからブロックされる」と解釈するのは早計です。実装や書き方に依存しますので、もちろん正しいこともありますが常に正解とは限りません。
ルールにマッチするとACLの評価が終わって、その後ろに書かれてるリストは無視する実装だと上記の「192.168.0.1は192.168.0.0/24に含まれるからブロックされる」という解釈は正解です。
しかし、一度マッチしても評価が止まらず、最後までリストを舐めて、最後にマッチした条件(ここでは192.168.0.1/32の部分)で許可される実装もあります。中には行末に明示的に書いおいたDefault denyがマッチして結局ブロックされる場合もあります。
これも実装に依存します。特に主流らしい主流も無い気がしますし、BSD PFやipfilterのように、実装によってはルール毎(行毎)に当該行以降の評価をやめる/続けるを制御出来るものをあります。
例えばBSD PFは下記の上の行のようにquickを入れない場合はマッチしても評価が終わらず次の行が評価されます。下側のようにquickを入れるとルールマッチした時点でルールの評価が終了し、後ろのルール群は評価されません(無視されます)。
pass in log proto tcp from 172.16.0.1 any to any port 25
block in log quick proto tcp from 172.16.0.0/12 any to any port 25
※上記の場合172.16.0.1からSMTPへのアクセスはブロックされます。
許可した筈なのに許可されない、ブロックしたはずなのにブロックされてないというケースは、(その製品等にバクが無いなら)この評価の継続性やデフォルトの挙動をちゃんと把握してないか、認識を誤ってます。
ステートフル型かステートレス型か
ネットワーク通信はほぼ全てが双方向ですので、サーバであれば入ってくる方だけでは無く、出ていく通信も許可する必要があります。
例えばIPが192.168.5.15のメールサーバ(SMTPサーバ)で、Source 172.16.0.1:5555⇨Destination 192.168.5.15:25という通信が発生した際に、逆方向の通信、つまりサーバから見てSource 192.168.5.15:25⇨Destination 172.16.0.1:5555の通信を、Inbound通信のSourceやDestinaionを見て自動的に許可するのがステートフル型、そういういいい感じの許可をステートレス型のFWです。かなり雑な説明ですが。
どちらかを採用してるかは実装によりますし、ステートフル型でもあってもステートフルに許可する設定が入ってないと機能しないことがあります。例えばnftablesだと「ct state established related accept」みたいなワードを入れてあげないステートフルに動作しません。
ステートフル型はコネクションテーブルを管理する都合上、必要なメモリや処理量がステートレスより重くなりがちで、CPUやメモリをそれなりにリッチに必要とします。そのため、CPUやメモリがリーンな組込系のシステムでは、ステートレス型のアクセス制御機構を採用していることがあります。
ステートフル型はテーブルを持ってるので逆方向の通信は通信終了かタイムアウトまでは許可されます。逆に、ステートレス型は明示的に逆方向の通信を許可しないと通信が出来ません。
ステートレス型は所謂許可された通信の戻りの通信かどうかを見ていませんので。そのためステーレス側のACLにDefault Denyみたいなの強力なブロック性能を持つルールをステートフル型のノリで入れると、外に向けて提供していたサービスがブロックされて止まったり、目の前のコンソールが固まることがあるので注意が必要です。
ただ、サーバの場合、設計者や運用者によってはOutboundがガバガバの場合なケースがあります。こう言う場合はあまり気にすることはありませんが、Outbound側もより適切にフィルタリングしたい場合は注意が必要になります。
その他の勘所
そのほかにYuaihoが思うACLのハマりを紹介します。
IP重複の可否
これはnftablesなんかが該当するんですが、ACL内部でIP重複を認めない実装もあります。実装にもよりますが、host単位(/32や/128)だけはなく、レンジとして重複してるのもダメな実装もあり、例えば下記のようなルールはnftコマンドで同一セットに入れることはできません。
192.168.0.0/24
192.168.0.1/32
一方、nftablesの前世代にあたるiptablesはIP・IPレンジの重複を許容しています。
そのため雑に作ってて重複管理なんかしていないiptablesの中身を、何も考えずにnftablesに移植してくるとハマります。
どうなるかというと、リストの後ろ側で重複してるものが弾かれて、nftの再起動に失敗するなどしてハマります。というか、私はハマりました。
ブロックポリシー・Denyポリシー
ブロックポリシー/DenyポリシーというのはルールなどによってDenyされた時、その後Denyされた通信をどう始末するかです。
よくあるのはDROP(静かにパケット破棄して無視する)するかREJECT(拒絶した旨の応答を返す)するかですね。
REJECTはIP FWだとTCP RST等を返したり、Webサーバ等のアプリケーションの場合は403(Forbidden)のような拒絶を示す応答を返したりすることです。
DROPとREJECTのどちらかが良いかは考え方次第で、結局は自分が何を良しとするかです。これについては他のサイトに説明を譲りますが、いずれにしても下記の3要素は考慮したほうが良いです。
1つめはルータやFWなどのOSの前段でもACLを用いてパケットフィルタリングをしてる場合、前段にあるルータやFWと、後段のOSのIP FWのDenyの挙動は合わせた方がいいです。
前段と後段でDenyの挙動を変えると、「特定のポート以外はREJECTで何か返ってくるのに、特定のポートは何も返ってこない」みたいに挙動になります。
これそのポートには何らかのサービスがあることを暗示することに繋がります。これは管理用のSSH等、隠蔽したいサービスやポートを開いている場合は適切ではありません。
2つめはIP FWでDROPを行うとクライアントがTCP SYN等を再送してくることがあります。FWでSYNの時点でログ出力する設定をしてると、DROPの場合この再送によってログの行数が無駄に増えます。大体2〜3回再送してくるので、これに伴ってDROPログも2〜3倍無駄にに増えます。
体感的な傾向では、攻撃観点ではIP FWの場合はREJECTにしてTCP RST送っておくと即座に諦めてくれることが多いです。ただ、REJECTされてもめげないIPはちょくちょくいます。まじでクソですね。またアプリケーションが行うREJECT場合、REJET(Webなら403 Forbidden等)を出しても諦めてくれるのは期待薄です。
ちなみに、YuaihoはIP FWではDROP派です。上記は一見REJECTを推してますが、Yuaihoが所有する機材はDROPしてることの方が圧倒的に多いです。
3つ目はアプリケーションの場合、DROPを返すと何も方法を与えないことができますが、REJECT応答を返すとそのサービスが稼働してること自体はわかってしまいます。ここでバナー隠蔽などの設定が漏れていると、OSやソフトウェアバージョンなどもわかってしまうことがあります。
最後に
ACLはこんな感じで実装ごとに動作挙動が異なります。
某C社の「暗黙のDeny」は結構有名ですが、「暗黙のDeny」は別にACLの標準仕様でもデファクトスタンダードでもありません。これまで見てきたように、本当に実装ごとに違いますし、なんなら暗黙のAllowも普通にあります。
初めて触れるアクセス制御機構を設定するときは、少なくとも「デフォルトの挙動」「マッチ後の挙動」ぐらいは確認しないとガバガバACLを作る可能性があります。事故った時にシャレにならないことになる方は特に注意しましょう・・・。