それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

nftablesのfowardが記述が大量になる問題。

nftablesのテーブルのルールがあふれる

どんなに整理しても、既存のforward テーブルがあふれかえることは避けられない。

細かく条件を入れればいれるほど、forward テーブルに処理が集中してしまい、結局のところiptables時代と何も変わらない気がします。

table inet fw4 {

  chain forward {
    # policy DROPなのでACCPETを列挙するのだが
    type filter hook forward priority filter; policy drop;
    $RULE_001 jump_to_accept 
    $RULE_002 jump_to_accept 
    $RULE_003 jump_to_accept 
    $RULE_004 jump_to_accept 
    $RULE_005 jump_to_accept 
    # ... あふれかえる Accept 
    $RULE_256 accept 
    jump handle_reject
  }
  chain handle_reject {
    reject comment "!fw4: Reject any other traffic"
  }
}

jump 先にjumpがあったりすると、追いかけるのが面倒になり嫌になる。

既存テーブルと干渉しやすいから躊躇する

iptablesのときもそうだったけど、ルールが増えてくると追いかけるのが面倒になる。新規でルールを突っ込むのを躊躇します。

OpenWrtのようなnftableを自動生成するソフトウェアが入ってると尚更です。

initスクリプトで追加したのか、GUIからの自動生成かが全くわからん。誰がいつどこでなんのために入れたのか。メモが残せない。

解決策1 commentを使う。

いちばん簡単な解決策はコメントを使う。

table inet fw4 {

  chain forward {
    type filter hook forward priority filter; policy drop;
    $RULE_001 jump_to_accept comment "# by takuya for Router A "
    $RULE_002 jump_to_accept comment "#!fw4 handle ..."
    $RULE_003 jump_to_accept comment "# by takuya for Router Be "
    $RULE_004 jump_to_accept comment "# org.example.www  "
    $RULE_005 jump_to_accept 
    # ... コメントにも限界が。。。
    $RULE_256 accept 
    jump handle_reject
  }
}

コメントに、識別IDや名前空間を編み出してルール化することになる。のだけど、nftablesってjump / goto で関数のようにルールを使い回せるのが魅力じゃん。

だったら一つのforwardテーブルに集中するのって、本末転倒な気がしなくもない。

解決策2 ポリシールーティングをする

コメントでもあふれることは避けられないのだから、指定パケットは、別にルーティングを用意して、メインのnftablesのテーブルとは全く別の許可設定やとルーティングするようにしたほうがすっきるするかもしれない。

ポリシールーティングの例

マークの有無でパケットをフィルタリングする。

マークしたパケットを許可すれば良い

nft insert rule $TABLE forward mark $MARK counter accept

なので、マークを付けるまでの箇所でルールをいっぱい作れる。

nft_table_name=takuya_mangle
iif=eth1
oif=wan2
GW2=10.0.0.2
mark_num=1111
to_addr='1.1.1.1/32'
MAIN_TABLE='inet fw4'
# -- ルーティングテーブルを追加
ip rule add fwmark $mark_num table $mark_num
ip route add $to_addr dev $OIF via $GW table $mark_num
# -- nftables でごちゃごちゃとマーク
nft create table ip $nft_table_name 
nft create chain ip $nft_table_name prerouting
nft insert rule  ip $nft_table_name prerouting \
   iifname $iif ip daddr $to_addr \
   counter meta mark set $mark_num \
   comment \"takuya_mangle_sample\"
## マークしたパケットを許可
nft insert rule $MAIN_TABLE forward mark $mark_num counter accept

このように書いておけば、マーク済パケットをFoward許可なので、prerouting でマークするときに条件を書くので、forwardにはマークだけになり、set mark している箇所を探せば良くなり、forwardにルールが増えないのでとても便利になる。jump 記述を捨てられる。

上記の例のように、マーク済のパケットのDefaultルートを変更できるので、特定PCからの特定宛先だけは別経路を通るように設定できるし、Fowardにはマークだけになってルールはシンプルになる。

ip route add src xxxで ソースのアドレスも指定できるのだけどテーブルを分けたほうが楽だよね。

これにより、fowardのAccept、DROPの順番などを意識せずに、マークを付けたか付いてないかで判断すれば良くなる。つまり条件でマッチするしないをmark unset / mark set の問題に置き換えることができて、複雑化する条件を管理することができる。

解決策3

まとめられるものはまとめる。

nftables は ipset が使えるので、@name= { .... } のリストを使ってできる限りまとめる。

nft insert rule inet fw4 prerouting iifname $IIF ip saddr @ipset_01 counter 

また、nftables は { "A" , "B" } の列挙ができるので、まとめられるものはできる限りまとめる。

nft insert rule inet fw4 prerouting iifname { "wan1", "wan2" } ip saddr @ipset_01 counter 

ipset の代わりにグルーピングする追加例

nft add table my_table
nft add set my_table my_pc { type ipv4_addr \; flags interval \; }
nft add element inet my_table my_pc { "192.168.1.100" }
nft add element inet my_table my_pc { "192.168.1.101" }

追加結果は次のようになる。

table my_table {
  set my_pc {
    type ipv4_addr
    flags interval
    elements = { 192.168.2.100, 192.168.2.101 }
  }
}

上記のようにすると、my_table 中のルールで@my_pc という配列が使える。配列が@マークなのは、なんかBashっぽいけど我慢

ただ、まとめすぎると、なぜ「グルーピング」したのかわからなくなるので、これも辛いところ。

なやましい

とりあえず、先頭にINSERTしておけばなんとかできたiptablesが愛おしい。

仮想マシン上にnftablesをいっぱい用意して用途別にnftablesをべつマシンで処理したほうが良くねーかもう。

昔ながらのFWに万能を求めアレコレと処理するというアプローチが間違ってる気がする。

仮想ネットワークと仮想マシンで用途別にパケットをさばくルーティングをするルーティングPC群として設計したほうが影響範囲を絞って耐障害性も誤操作寛容性も上がるんじゃないのかなぁ。

小さな物を組み合わせて大きなものを作るというアプローチで、小さなFWを仮想ネットワークで組み合わせて巨大なFWを構成するというアプローチが現代的なんだろうなぁ。

そうなるとnftablesっている?