ドメインレートリミット(unbound)
ドメインレートリミットとは
/services/cache/infra.cに実装されている
例えば 以下のようなレコードが返ってくる権威サーバーがあったとする
QD: www.hoge.co.jp. IN A NS: hoge.co.jp. IN NS ns1.hoge.co.jp. hoge.co.jp. IN NS ns2.hoge.co.jp.
このような場合に次にns1.hoge.co.jpまたはns2.hoge.co.jpに問い合わせを行うが、 この問い合わせ数を制限するものである。
水攻め攻撃が行われ
a.hoge.co.jp b.hoge.co.jp c.hoge.co.jp d.hoge.co.jp
のように名前を変えたアクサスをされると、毎回キャッシュに存在しない問い合わせになるので アクセスがあるたびにhoge.co.jpに問い合わせを行ってしまう。
これを防ぐために攻撃を受けているドメインの権威サーバへのアクセスを N秒以内にM回までに制限する。
Nはunboundでは固定で2秒となっている
設定項目
設定項目は下記の6つ
ratelimit: num
ratelimitサイズで、これが0の場合はドメインratelimit無効になる。
ratelimit-size: size
Hashのキーのサイズ(バイト指定)
ratelimit-slabs: num
Hashのスラブサイズ。マルチスレッドの時のアクセス効率を良くするため、ハッシュ自体を何個に分けるかを指定。
ratelimit-factor: num
ratelimitを超えた場合にどうするかを決める。 0だと必ずFAILを返すようになるここに数値を設定するとratelimitを超えた場合には1/数値だけ 成功するようになる。
ratelimit-for-domain: domain num
指定されたドメインのratelimit値を別な値に置き換える 沢山の子を持つTLDドメインや攻撃を受けているドメインのrate-limitを特別扱いしたい場合等に使える
ratelimit-below-domain: domain num
指定されたドメインのサブドメインすべてのratelimit値を別な値に置き換える 指定されたドメイン自体のrate-limitは置き換わらない。
テスト
テストがしやすいようにconfの項目の値を下記のように設定しておく。
log-queries: yes ratelimit: 3 do-ip4: yes do-ip6: no verbosity: 2 harden-referral-path: no
プライミングとncad.co.jpのキャッシュを終わらせるために一度www.ncad.co.jpに問い合わせてから次のようなスクリプトを実行する
dig @127.0.0.1 a.ncad.co.jp & dig @127.0.0.1 b.ncad.co.jp & dig @127.0.0.1 c.ncad.co.jp & dig @127.0.0.1 d.ncad.co.jp & dig @127.0.0.1 e.ncad.co.jp &
実行結果
May 16 08:53:03 unbound[11879:1] info: 127.0.0.1 b.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: resolving b.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: 127.0.0.1 a.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: resolving a.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: 127.0.0.1 c.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: resolving c.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] notice: ratelimit exceeded ncad.co.jp. 3 May 16 08:53:03 unbound[11879:1] info: response for b.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: reply from <ncad.co.jp.> 182.171.76.42#53 May 16 08:53:03 unbound[11879:1] info: query response was NXDOMAIN ANSWER May 16 08:53:03 unbound[11879:1] info: 127.0.0.1 d.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: resolving d.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: 127.0.0.1 e.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: resolving e.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: response for e.ncad.co.jp. A IN May 16 08:53:03 unbound[11879:1] info: reply from <ncad.co.jp.> 182.171.76.42#53 May 16 08:53:03 unbound[11879:1] info: query response was NXDOMAIN ANSWER
3件でratelimitがかかる為、query responseの行が2行しか無いのがわかる。
ratelimitは最初に超えた時しかログが出ないのでragtelimit exceededの行は1行のみである。
内部構造
キャッシュを管理しているデータ
// infra.h struct infra_cache { ... struct slabhash* domain_rate; // keyはrate_key dataはrate_data 対象ドメインに秒間何回のアクセスがあったかを格納する rbtree_type domain_limits; // aclと同じくdomainのparentを辿れる構造。for-domainとbelow-domainの情報を格納 ... }; struct rate_key { struct lruhash_entry entry; uint8_t *name; // ドメイン名のバイナリ表現(圧縮無し) size_t name_len; // nameのlen }; struct rate_data { int qps[RATE_WINDOW]; // 対象の秒数に何回アクセスがあったかをカウントする time_t timestamp[RATE_WINDOW]; // qpsに対応する時刻(秒単位) }; // infra.c int infra_dp_ratelimit // configで指定されたratelimitの値を格納 0 だと無効
関数
// infra.c // 指定された現在時間からRATE_WINDOW以内のアクセスがratelimitを超えていないか調べる int infra_ratelimit_exceeded(struct infra_cache* infra, uint8_t* name, size_t namelen, time_t timenow) // 指定されたドメインに対するratelimitをインクリメントする インクリメントされた結果ratelimitを超えているかを返す int infra_ratelimit_inc(struct infra_cache* infra, uint8_t* name, size_t namelen, time_t timenow)