nftablesで特定の接続元からの大量アクセスを遮断する
目次
特定の送信元からのDoSのような大量アクセスや、連続したログイン試行を遮断したいと思ったことはないでしょうか。
nftablesではmeter
やset
を使うことでこれを実現することができます。
この記事では、大量アクセスを遮断する方法について紹介します。
注意
nftablesのバージョンやLinux kernelのバージョンで、書き方が異なります。
meter
は古い書き方で、nftables 0.9以降(?)、Linux kernel 4.18以降ではset
を利用します。
この記事では、set
を中心に、meter
についても触れていきます。
nftablesはまだ過渡期のようで、正しい表現のはずなのにバージョンによってはSyntax Errorとなることがあります。
set
については、nftables 0.9.2、Linux kernel 4.19.0で設定投入ができることを確認しました。
設定方針
nftablesの初期状態は以下の様に定義します。
- input方向はdropするポリシーとします
- 設定対象のインタフェースはens3です
- established, relatedなinputは許可しておきます
また、大量アクセスを遮断するために、以下の様な設定を施すことにします。
- (大量アクセスと判断されない)必要なinputだけacceptします
- 今回は、SSHのアクセスを許可することにします
初期状態のinputベースチェイン
初期状態のチェインを事前に載せておきます。
# nft list chain inet filter input table inet filter { chain input { type filter hook input priority 0; policy drop; iifname "ens3" ct state { established, related } accept } }
通信量による制御方法
実際の設定を進める前に、nftablesにおける通信量による制御方法を紹介します。
nftablesではパケット数やバイト数(バイト数はLinux kernel 4.3以降)による条件指定を、limit rate
キーワードで行うことができます。
さらに指定したパケット数やバイト数を「超える」場合という条件の場合は、over
キーワードを付加することで対応できます。
iptabelsの頃はhashlimit
と呼ばれたものに代わる機能です。
具体的には以下の様な書式です。
## パケット数による制御(10パケット/秒までのpingは許可) # nft add rule inet filter input icmp type echo-request limit rate 10/second accept ## バイト数による制御(10Mbytes/秒を超える(over)通信は破棄) # nft add rule inet filter input limit rate over 10 mbytes/second drop
これで制御できるじゃない!って思ったそこのあなた。
上記の記述だけでは、残念ながらマッチする全ての通信に対して通信量制限がかかることとなります。
それでは目的を達成することはできませんね!?
特定の接続元からの通信量制御
さて本題に入りますが、通信量制御を送信元単位で行うためにはIPアドレス単位でlimit rate
をかけなければなりません。
そのためにはIPアドレス毎に状態を保持する方法が必要です。
元々meter
を使うことで実現していましたが、最新のnftablesでは汎用的なset
を使うことで実現することになっています。
では早速、それぞれの方法を紹介していきます。
設定方法(set)
最新のnftables、Linux kernelを利用しているあなたは、set
を使いましょう。
set
とはナニモノかというと、IPアドレスや何やらの汎用的なリストを作ることができるものです。
例えば初期状態のベースチェインに示したような、{ established, related }
もset
の一部(匿名セット)です。
set
の使い方については説明するより実際のルールを見た方が分かりやすいです。
以下のルールを見てみましょう。
# nft add set inet filter sshv4-counter { type ipv4_addr\; timeout 10m\; flags dynamic, timeout\; } # nft add rule inet filter iifname "ens3" tcp dport 22 ct state new add @sshv4-meter { ip saddr limit rate 1/minute } accept # nft list ruleset set sshv4-meter { type ipv4_addr size 65535 flags dynamic,timeout timeout 10m } chain input { type filter hook input priority filter; policy drop; iifname "ens3" ct state { established, related } accept iifname "ens3" tcp dport 22 ct state new add @sshv4-meter { ip saddr limit rate 1/minute } accept }
1行目で、sshv4-counter
というset
を作成しています。
;
はエスケープが必要なことに注意してください。
このset
は次のような設定になっています。
- IPv4アドレスのリスト(
type ipv4_addr
) - 追加された値は10分で削除(
timeout 10m
) - 動的に追加でき、タイムアウトが設定(
flags dynamic, timeout
)
なおflags
については後ほど紹介します。
2行目では、少し細かいルール指定をしています。
具体的には次のようなルールです。
- 22/tcp宛(
tcp dport 22
)の新規のコネクション(ct state new
)が来たとき - 送信元IPアドレス毎(
ip saddr
)に1分あたり1回以内(limit rate 1/minute
)であれば sshv4-meter
というset
に追加(add @sshv4-meter
)して- 通信を許可(
accept
)する
この状態で条件にマッチする通信を発生させ、sshv4-meter
を表示させると以下の様になります。
# nft list set inet filter sshv4-meter table inet filter { set sshv4-meter { type ipv4_addr size 65535 flags dynamic,timeout elements = { 192.168.0.1 expires 9m53s208ms limit rate 1/minute, 192.168.0.2 expires 2m5s501ms limit rate 1/minute } } }
elements
が表示され、IPアドレスと状態(limit rate
が1/minute
)が紐付いて、リストになっていることが分かります。
ちなみに、もし1分あたり1回を超えるような通信があった場合はこのルールにマッチしないため、ポリシーdropすることで結果的に通信が遮断されることになるわけですね。
setのパラメータ
add set
で指定できる主要なパラメータを紹介しておきます。
パラメータ | 値のタイプ | 説明 |
---|---|---|
type | ipv4_addr , ipv6_addr ,ether_addr , inet_proto ,inet_service , mark |
要素のデータ種別 |
flags | constant , dynamic ,interval , timeout |
フラグdynamic は、ルールで動的に追加する際に指定timeout は、タイムアウトを設定する際に指定パラメータの timeout を指定した場合は、flags に自動的にtimeout が付く |
timeout | string (利用可能な単位: d , h , m , s ) |
要素の保持時間 例:1h30m(1時間30分) |
size | unsigned integer (64bit) | 要素の保持できる最大数 |
設定方法(meter)
ちょっと古いnftables、Linux kernelを使っている方は、meter
コマンドを使う必要があります。
meter
の使い方についても説明するより実際のルールを見た方が分かりやすいです。
以下のルールを見てみましょう。
# nft add rule inet filter iifname "ens3" tcp dport 22 ct state new meter sshv4-meter { ip saddr limit timeout 10m rate 1/minute } accept # nft list ruleset chain input { type filter hook input priority filter; policy drop; iifname "ens3" ct state { established, related } accept iifname "ens3" tcp dport 22 ct state new meter sshv4-meter { ip saddr timeout 10m limit rate 1/minute } accept }
meterの場合は1行だけでルールが完結します。
1行目は、具体的には次のようなルールです。
- 22/tcp宛(
tcp dport 22
)の新規のコネクション(ct state new
)が来たとき - 送信元IPアドレス毎(
ip saddr
)に1分あたり1回以内(limit rate 1/minute
)であれば sshv4-meter
というmeter
に追加(meter sshv4-meter
)して- 通信を許可(
accept
)する
この状態で条件にマッチする通信を発生させ、sshv4-meter
を表示させると以下の様になります。
なお、meter
は明示的に作成せずにlist meter
することができます。
# nft list meter inet filter sshv4-meter table inet filter { meter sshv4-meter { type ipv4_addr flags timeout elements = { 192.168.0.1 expires 9m53s208ms : limit rate 1/minute, 192.168.0.2 expires 2m5s501ms : limit rate 1/minute } } }
elements
が表示され、IPアドレスと状態(limit rate
が1/minute
)が紐付いて、リストになっていることが分かります。
set
もmeter
もほとんど変わらない上に若干meter
の方がシンプルな気がしますね。
おまけ
ちなみに、ポリシーをaccept
にしている場合は追加するルールをlimit rate over
の表現にしつつdrop
するルールにすれば、一定の通信量を超えたものを破棄するように書くこともできます。
最近のコメント