DoS攻撃に役立つdnsdist設定集
はじめに
この記事は、富士通クラウドテクノロジーズ Advent Calendar 2023の9日目の記事です。8日目では、o108minminさんのN100で楽しい自宅インフラでした。
DoS攻撃に役立つdnsdist設定集
最近、ランダムサブドメイン攻撃(DNS水責め攻撃)がかなり活発的に発生している。この記事では、dnsdistというツールを用いた攻撃対策について、ざっくり解説する。
※1 dnsdistについて、もっと詳しい情報は、DNS Summer Day 2019で発表されたdnsdist - DNSトラフィックに特化したオープンソースの多機能ロードバランシンサーをご覧ください。
※2 ランダムサブドメイン攻撃について、もっと詳しい情報や対策全般については、DNS Summer Day 2023で発表されたランダムサブドメイン攻撃についてをご覧ください。
※3 記事は、断片的な情報が記載されているので、公式ドキュメントを読むことを推奨する。
必要な基礎知識
dnsdistの設定はluaで書くことができるため、非常に柔軟な処理を実現することができるが、この記事では、複雑なlua設定を紹介せず、一旦組み込み関数の活用方法について、簡単にご紹介したい。組み込み関数とは、dnsdistに最初から予め用意された関数だ。これらの関数をluaで呼び出したり、組み合わせたりするイメージで設定を記述する。DoS攻撃に有効な対策として、個人的には以下の2つあると思う。
- cache: cacheを使うことによって、データベース参照処理を大幅に削減することできるため、攻撃には全く同じクエリを効率的に処理することができる
- block:
- ランダムサブドメイン攻撃では、ランダムな値が使われるため、cacheの有効性が下がる場合がある。それを補うことができるのはblockだ。
- dnsdistはロードバランサーの役割をしているため、自分が受付した全体パケットを集計し、特定のルール(Packet Policies)に該当するパケットに対し、指定された処理(Packet Action)を行う。
- ルールは静的ルールと動的ルールがある。
Packet Action
ルールでマッチングされたパケットに対し、様々処理(Packet Action)を適用することができる。イメージしやすいようにいくつの例を記載しておく。
処理の関数名 | 処理の概要 |
---|---|
DelayAction |
レスポンスを指定したミリ秒だけ遅延させる |
DropAction |
パケットをDROPする |
RCodeAction |
指定されたrcodeでクエリをレスポンスに変えて即座に返信する |
上はほんの一部に過ぎないので、詳細はドキュメントを参照すること。
静的ルール
MaxQPSIPRule
-
クライアントからの流量を一定固定する場合に有効である。
-
例えば、下の例では、IPv4アドレスごと、およびIPv6の/48ごとのトラフィックを測定し、そのようなアドレス(範囲)のトラフィックが5qpsを超えた場合、100ms遅延させる。
addAction(MaxQPSIPRule(5, 32, 48), DelayAction(100))
-
静的ルールなので、クライアントの条件に柔軟な対応が難しい場合があるので、無難な値を設定する必要がある。
-
しかし、無難な値である反面、防御効果は下がっしてしまうことがある。
-
後述の
DynBlockRulesGroup:setQueryRate()
等と組み合わせるとこの弱点を改善することができるであろう。
MaxQPSRule
- 上で紹介したMaxQPSIPRuleの適用範囲は、クライアント毎であるのに対し、この MaxQPSRule の適用範囲は、全体である。全体の流量の上限を指定できる。
- 例えば、以下の例では、全体の流量を5qpsで固定する。5qpsから溢れたパケットは全部DROPされる。
-
MaxQPSRule(5)
: 5qpsでパケットをマッチングする -
NotRule(MaxQPSRule(5)
でNOTを取り、5qps以上のパケットをマッチングする
-
addAction(NotRule(MaxQPSRule(5)), DropAction())
- この設定は適用範囲が広いため、防御効果は高いものの、スケールできないので、工夫する必要がありそう。
RegexRule
-
クエリを正規表現でマッチングする
-
正規表現なので、様々なPacket Actionと組み合わせると、かなり柔軟なことができる
-
DropAction
で特定のクエリのパケットを即座にDROPする
addAction(RegexRule("[0-9]{4,}\\.example$"), DropAction())
-
RCodeAction
で特定のクエリのパケットを即座にレスポンスする
addAction(RegexRule("[0-9]{4,}\\.example$"), CodeAction(dnsdist.REFUSED))
-
-
blacklistのクエリを作って防御したり、データベース参照処理なしで即座レスポンスしたりすることができる
QPSAction
-
流量制限を超えたパケットをDROPする
-
MaxQPSIPRuleとMaxQPSRuleよりもやることがシンプルなので、様々な組み合わせ方で防御効果を図れる
-
例えば、RegexRuleと同じようにblacklistの作成を実現できる
addAction("h4xorbooter.xyz.", QPSAction(10)) addAction({"130.161.0.0/16", "145.14.0.0/16"} , QPSAction(20)) addAction({"nl.", "be."}, QPSAction(1))
動的ルール(Dynamic Rule)
静的ルールによるPacket Actionと違い、動的ルールによるPacket Actionは生存時間が短く、ルールによって生成された後、またルールによって削除される。平常時静的ルールで守り、DoS攻撃のような一時的な異常時は、一時的な防御措置を適用することできる。
maintenance() 関数
maintenance()はdnsdistにより1秒おきに呼び出される関数である。この関数の中で動的ルールを定義することで、毎秒動的ルールでパケットがマッチングされる。マッチする場合は、そのルールの定義通りにdnsdistは処理する。
function maintenance()
-- 動的ルール
end
DynBlockRulesGroup クラス
dnsdist 1.3以降では、動的ルールは基本的に DynBlockRulesGroup
クラスを使ってまとめて定義することが推奨される。
-- dbrを作成する
local dbr = dynBlockRulesGroup()
-- このdbrに必要な動的ルールをどんどん追加していく
dbr:setQueryRate(30, 10, "Exceeded query rate", 60)
dbr:setRCodeRate(DNSRCode.NXDOMAIN, 20, 10, "Exceeded NXD rate", 60)
dbr:setRCodeRate(DNSRCode.SERVFAIL, 20, 10, "Exceeded ServFail rate", 60)
dbr:setQTypeRate(DNSQType.ANY, 5, 10, "Exceeded ANY rate", 60)
dbr:setResponseByteRate(10000, 10, "Exceeded resp BW rate", 60)
--- maintenance関数ではapply関数だけかけば良い
function maintenance()
dbr:apply()
end
上の例で示されたように、 DynBlockRulesGroup
クラスには set*()
メソッドがたくさんある。ランダムサブドメイン攻撃に役に立つメソッドをいくつかピックアップし紹介する。
setRCodeRate()
このメソッドはレスポンスのコード(rcode)をベースにマッチングすることができる。ランダムサブドメイン攻撃では、ランダムな値が使われるため、殆どの場合では、NXDOMAINやREFUSEDのrcodeになる。これらのrcodeがある程度の流量で、引き起こているクライアントからパケットをマッチングすると、効果的に防御することができる。
dbr:setRCodeRate(DNSRCode.NXDOMAIN, 20, 10, "Exceeded NXD rate", 60)
dbr:setRCodeRate(DNSRCode.SERVFAIL, 20, 10, "Exceeded ServFail rate", 60)
setRCodeRatio()
- ランダムサブドメイン攻撃の場合、各クライアントからは 1 qpsで、断続的攻撃されると、
setRCodeRate()
では、指定した流量が極端に小さい値になってしまう。 - 値が小さい場合は、攻撃元だけではなく、一般ユーザも巻き込まれてしまう可能性がある。
- この弱点を克服できるのは、
setRCodeRatio()
メソッド。 -
setRCodeRatio()
メソッドは流量をqpsではなく、割合(ratio)でマッチングする。 - 例えば、ランダムサブドメイン攻撃の場合、1 qpsで攻撃してくるクライアントでも、NXDOMAINのrcodeの割合が 80% 以上になる。一方で、一般クライアントは正常なクエリで問い合わせするため、この割合は50%より低い可能性が高いと考えられる。
- このように割合で動的ルールを設定すると、攻撃はのクライアントと正常なクライアントの区別の精度がより高まる。
dbr:setRCodeRatio(DNSRCode.NXDOMAIN, 0.8, 10, "Exceeded NXD rate", 60)
まとめ
dnsdistを使ってランダムサブドメイン攻撃等DoS攻撃の防御方法について、浅く紹介した。静的ルールと動的ルールを使えば、ある程度攻撃の影響を限定することができる可能性があるが、もっと防御力を高めるためには、SaaS等を使用し、多重防止を構築することが重要だと思う。
最後に
10日目では、@ktakaakiさんのSwitchBotのBLEでデータを取ってみたです。お楽しみに!
Discussion