それマグで!

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

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

OpenWRTでパケットをマークして許可する(ポリシールーティング)

WEB-UI ( Luci )を使った場合

Luci で ip rule を作って ip route テーブルを作ることはできる。

前回やったコマンドからのポリシールーティングをLuCI(WEB)経由でやる話です。

やることは次の通り。

nftables でマークを扱う。

マーク済パケットをAcceptする。prerouting で マークする。ip rule でルーティングテーブルを分ける。

nftables のprerouting(マーク条件)を別テーブルにする

ポリシールーティング用のルーティングテーブル

ip rule 設定で、マーク済パケット専用のルーティングテーブルを作る。

設定の Neworking -> routing -> { Static IPv6 route , IPv6 Rule }を編集して次のような設定を作る。

IPv6 Routing の設定

IPv6 Rulesの設定

この結果、次のような設定が作られる。

config route6
  option interface 'wg0'
  option target '2000::/3'
  option gateway 'fd00:aaa:afac:1919::1'
  option table '666'

config rule6
  option lookup '666'
  option mark '0x29a'

これで専用のルーティングテーブルを作ることができるし、MARKに一致したルールを作ることができる。

この結果はコマンドから確認できる。

ルールが作成される。

> ip -6 rule list
1:      from all fwmark 0x29a lookup 666

ルーティングが設定される。

> ip -6 route show table 666
2000::/3 via fd00:aaa:afac:1919::1 dev wg0 proto static metric 1024 pref medium

ルーティングをテストする。(google.comのAAAA経路)

> GGv6=2404:6800:400a:80b::200e
> ip -6 route get fibmatch $GGv6 mark 666
2000::/3 via fd00:aaa:afac:1919::1 dev wg0 table 666 proto static metric 1024 pref medium

マーク済パケットの経路が作られたことがわかった。

ファイアウォール設定で、マークを付ける。

config rule
  option name 'Add Mark'
  option family 'ipv6'
  list proto 'all'
  list src_ip 'fd03:3304:1128:3939::3'
  option target 'MARK'
  option set_mark '666'
  option src 'lan6'
  option dest '*'

ここで、ミスしちゃいけないのが、source zone をちゃんと入れること。 発信 zoneを入れると、 inet fw4 のmangle_preroutingにルールが作られる。

src zone を適当にAnyとかすると mangle_forwardmangle_output に入ってしまう。悩ましい

この設定で、nft に次のルールが追加された

> nft list chain inet fw4  mangle_prerouting

table inet fw4 {
  chain mangle_prerouting {
    type filter hook prerouting priority mangle; policy accept;
    iifname "br-lan" ip6 saddr fd03:3304:1128:3939::3 counter meta mark set 0x0000029a comment "!fw4: Add Mark"
  }
}

指定したSRC Addr のパケットをマークしている。

マークしたパケットを許可する

マークをつけたパケットを、Forward許可する。

次のような設定ができる。

config rule
  option name 'Accept Mark'
  option family 'ipv6'
  option src '*'
  option target 'ACCEPT'
  option mark '666'
  list proto 'all'
  option dest '*'

この設定の結果として、nft に次のルールが追加される

> nft  list  chain inet fw4  forward
table inet fw4 {
  chain forward { 
    type filter hook forward priority filter; policy drop;
    meta nfproto ipv6 meta mark 0x0000029a counter accept comment "!fw4: Accept Mark" # これ
    ## 略
    jump handle_reject
  }
}

以上によって、マークしてパケットを流せる

OpenWrtLuCIの画面から、ファイアウォールでマークしたパケットを、転送することが出来た。

OpenWRT はいいおもちゃ。

設定がミスっていても、確認できるし。コマンドから強引にパケット通せるし。

やり方がわからないときは、コマンドで作ってから、同じ設定をLuCIで試せばいいし。

メーカ製品を使うと手当たり次第に試す羽目になり、どうしてもストレスを感じる。 マニュアルがあっても正解がわからないので難しい。

その点では、OpenWrtを使えばLinuxだしコマンドで行けるし、WRT限定事象かどうかもUbuntuやRaspiでも試したりでミスの発見を着実に歩みを進められるので本当に嬉しい。

OpenWrtにfw4でポリシールーティングを入れる

OpenWrtでポリシールーティングを入れる

ポリシールーティングを入れると何が嬉しいのか。

OpenWrtの nftables (nft) では inet fw4 のテーブルに全部入っている。

しかしLuciのZONE転送の許可拒否とTraffic Acceptをうまく両立するのが大変だった。

Zone 転送とNAT許可設定でforwardがゴチャゴチャするからシンプルにしたい。

OpenWrtのZONE転送設定はdefault が reject である。

nft で別テーブルを作ってacceptを追加しても、デフォルトのテーブル inet fw4 でZONE転送にマッチしてreject される。コマンドでinet fw4 の forward チェインに許可する設定を追加して forward を acceptする。

しかし、nft table inet fw4 テーブルのforward チェイン記述が大量になる。追いかけるのが面倒になり諦めた。 特に、forwardチェインにIPアドレスを直接記述ルールを大量に書いて全体を見失い、パケット疎通ミスを引き起こしまくった。

そこで、forwardチェインをシンプル制御をうまくやる。このためにマークを使ったポリシールーティングを活用する。

全体の方針

  • nftables で パケットにマークを付ける
  • openwrt のfw4 にマーク済パケットのFowardをAcceptする設定をいれる。
  • ip rule でマーク済パケット用のルーティングテーブルを作る。

パケットをマークしなくても目的は達成できるものの、上記で書いた問題(fowardがあふれかえる)ということがあるので、実験用のネットワークはnftablesの別テーブルに切り出しを実現する。別テーブルでマークする設定を書いておけば、グチャグチャにしても影響範囲は抑えられる。

今回作るネットワーク

ルータ間の転送としてはありふれている

マークしたパケットを別のGWを流したい。

ただ、マークしたパケットを、別の経路で流そうということである。

nftables の処理。

このとき、FowardでAcceptするのはマーク済みのパケットだけとする。

nftables では、prerouting で条件マッチしたパケットにマークを付ける。fowardでマーク済パケットの転送をAcceptする。

ルーティングテーブルやFOWARDチェインに細かい条件を書いていくと数が増えてしまい煩雑になる。

その防止のため、prerouting で仕訳処理を先に済ませ、マークする。

nft のテーブルを分けて管理する。

nftables にはチェインを別テーブルに分割できるので、prerouting を別テーブルに分けてしまう。

パケットをマークする条件を別テーブルにして条件に特化したテーブルを用意して、そこだけ見て管理するようにする。

実際の処理

実際に作った作業は次のとおりになる

OpenWrt でforwardへマーク済みAcceptを入れる。

acceptするルールは、シンプルにする。MARK されたパケットを許可する。

MARK=666
FW4='inet fw4'
HANDLE_REJECT=$(nft -a list chain $FW4 forward | grep handle_reject | grep -oP '(?<=handle )+\d+' )
COMMENT=accept_sample
nft insert rule "$FW4" forward position $HANDLE_REJECT \
     mark $MARK counter accept comment "\"takuya: ${COMMENT} \""

結果を確認する。

> nft list chain inet fw4 forward
 table inet fw4 {
  chain forward {
    type filter hook forward priority filter; policy drop;
    ## Luci で入れたルール
    rule ...
    # 末尾に追加された
    meta mark 0x0000029a counter packets 250 bytes 21272 accept comment "takuya: accept_v6_sample "
    jump handle_reject
  }
}

末尾(handle_rejectの直前)に、ルールが追加された。

OpenWRT が作るfw4 のforwardテーブルは、default が DROP になっていて、且つ、未マッチはREJECTに送られる。そのためforward中にACCEPTを差込む必要があった。

パケットをマークするnftテーブルを作る

マーキングする条件を書くため専用テーブルを作る。

先ほどマークしたパケットを「Accept」するように書いた。ここでは、指定条件パケットをマーク済にするための新しく別テーブルを作り、その中にチェインを作ったうえで、ルールをいれる。

次の名前で、テーブルを作ってprerouting のチェインを作る。

TABLE='ip6 marking_sample'
nft create table $TABLE
nft create chain "$TBL" prerouting {\
  type filter hook prerouting priority filter \; policy accept \; \
}

prerouting で マーク条件を作る。

TABLE='ip6 marking_sample'
IIF=br-lan
MARK=666
DNET=2404:6800:400a:80b::/64
PC=fd01:b::3/64
nft insert rule $TABLE prerouting \
  iifname $IIF ip6 daddr $DNET \
  ip6 saddr $PC \
  counter meta mark set $MARK

今回書いたサンプル条件は、google.com AAAA 2404:6800:400a:80b::200e(2024-08-16現在)のパケットはマークとした。

この記述で、マッチしたパケットはマーキングされ、inet fw4 forwardを通過するときに、マーク済なのでforward ACCEPTされる。

masquerade の追加

また、マークされたパケットが出ていくときにmasquerade しておく

TABLE='ip6 marking_sample'
OIF=wg0
MARK=666
DNET=2404:6800:400a:80b::/64
nft create chain $TABLE postrouting { \
  type nat hook postrouting priority filter\; policy accept \; \
}
nft add rule $TABLE postrouting \
  oifname $OIF ip6 daddr $DNET counter masquerade

OpenWrtの場合は、ZONE設定でチェックをいれるとMASQURADEは記入しなくても大丈夫ですが、念の為。

ip rule を作る(ポリシールーティング)

マーク済パケット専用のルーティングが必要。

ここで専用のルーティングテーブルを作る。

MARK=666
GW2=fd00:aaa:afac:1919::1
ip -6 rule del table $MARK >& /dev/null
ip -6 rule add fwmark $MARK table $MARK
ip -6 route add $DNET dev $OIF via $GW2 table $MARK

ルーティングをテストする

MARK=666
GG=2404:6800:400a:80b::200e
ip -6 route get fibmatch $GG mark $MARK

上記のfibmatchを使うと、マッチしたrouting ルールをそのまま表示してくれて便利。add したルールがそのまま表示されればチェック・オッケ。

削除するときは、次のコマンドで

MARK=666
ip -6 rule del table $MARK >& /dev/null
ip -6 route flush table $MARK

ミスったらflushしたりdeleteすれば良い。

今回は、google.com AAAAのv6アドレスからサブネット指定した。この宛先アドレスをDNET='2000::/3' にすることで、v6のグローバルIP空間をすべてをルーティング対象にできるはず。2000::/3::/0default よりもわかりやすいかと思う。デフォルト使うとリンクローカルとかも混じって面倒かも。

mark をacceptするメリット。

フォワード・チェインが溢れずに済んだ。

nftablesは、jump で書いてもいい。しかし、jump jump であふれかえるとjumpのjumpで追跡が煩雑だった。そしてフォワードでジャンプすると、管理が OpenWrt のLuci生成のfw4 と混在してしまう。Openwrt のデフォルトであるfw4はluci の画面設定に依存する。fw4にコマンドで追記していると、どこで追記されたのか、ジャンプ追跡が煩雑すぎた。luci由来のfw4をあまりゴチャゴチャさせたくない。

また、ルートを変えたいときに、専用のテーブルを触るだけで済むし、一時的に止めたければ、マーク付与テーブルをdelete すれば済むのが嬉しい。

更にnft はhandleを使うのが面倒。jump でルールを書いたり、delete add したり、add flag dormant とかも、ちょっとした作業に毎回変わるHANDLEを探すのが手間で仕方ない。今回用にマークをつけるテーブル消す・テーブル作るで代用すると遊びやすくなった。

そういうことで、nft のHANDLEを使わずに、マークをつかうことにした。見るべき箇所がOpenWrtから切り離した別テーブル(マークを付けるテーブル)に限定できて管理がだいぶ楽になりました。

v6 NAT

今回の例は、v6 で NAT66 する例になるのだけど、v6 でNATをする意味ないって思うかもしれないけど、v6 NATも使えると割と便利なんですよね。

IPv6ではNATをしない(不要)」などと教科書には書いてるけど、NATあったほうが絶対便利。好きなIPoEの接続点を出口に据えて、任意ISPから出て回線テストもしやすい。やっぱりv6 NATはアリじゃないかな。

セキュリティについてもNATは防波堤になり得る気がする。グローバルIPv6で通信するv6は、OSのファイアウォール機能に依存するわけで。OSに脆弱性、つまりLinuxのnftablesやWindowsファイアウォール脆弱性があった場合は、大事故になると思うんですね。またユーザーがコピペで安易な許可設定をしてしまう可能性も怖い。

windowsTCP/IP v6の脆弱性

NAT66するべきだと思う理由にゼロデイ攻撃がある。

グローバルIPが割り当てられていると、OSの脆弱性の影響をもろに受ける。

OSのFWに脆弱性があると、グローバルIPが割あたっているすべての端末に影響が出る。

そう思っていたのだが、出てしまった。出てしまったのです。脆弱性

CVE-2024-38063WindowsのTCP/IPのスタック脆弱性でリモートコードの実行されるゼロデイ発覚していた。

パッチでるまでv6 オフにするレベル。ってやつです。

緊急パッチはでたものの・・・やっぱり、NAT66でNATもありなかって考えました。端末のIPを管理するにしてもULAのほうが何かと楽かも・・・

TCPなのでFWでCONNECTEDを見てれば大丈夫なはずではある。v6関連のチェックが甘くてstate new が通っちゃったりしてないですよね。

今回はv6 NAT とポリシールーティングで指定PCのGWを変えた。

今回は、OpenWrtのfw4 やLuciを使わずに、指定PCや指定サブネットだけは、v6 空間に出られるようにする設定を試してみた。

wireguardやStrongswanのようなVPNと組み合わせて、好きな箇所からv6アドレス出ていけるし設定をシンプルにする目処がたった

Linux箱をルータにしておくとちょっとした思いつきが試せて良いね。

nft で指定位置にinsert する

nft で指定位置にinsert する

既存のルールが次のようになっているとき

table inet fw4 {
  chain forward { # handle 2
    type filter hook forward priority filter; policy drop;
    $EXISTS_RULE # handle 2340
    $EXISTS_RULE # handle 2341
    $EXISTS_RULE # handle 2345
    $EXISTS_RULE # handle 2346
    jump handle_reject # handle 2352
  }
}

指定位置(jump handle_reject 2532 ) の直前にルールを入れたい

nft insert rule inet fw4 forward position 2352 mark 666 counter accept

結果は次のようになっている

table inet fw4 {
  chain forward { # handle 2
    type filter hook forward priority filter; policy drop;
    $EXISTS_RULE # handle 2340
    $EXISTS_RULE # handle 2341
    $EXISTS_RULE # handle 2345
    $EXISTS_RULE # handle 2346
    meta mark 0x0000029a accept # handle 2563 <=== 挿入
    jump handle_reject # handle 2352
  }
}

HANDLEを探す

末尾の一つ前に入れたいとき、特定のルールの直前に入れたいときなどあるだろうが、grep して探すしか無い。

GREPできるようなコメントがとても重要になってくる。

TBL='inet fw4'
CHAIN='foward'
UNIQUE='handle_reject'
nft -a list chain $TBL $CHAIN | grep handle_reject | grep -oP '(?<=handle )+\d+'

ユニークなコメントが有ると、ハンドル探索が楽。

TBL='my_table'
UNIQUE_COMMENT=':Quu9xeik'
nft -a list table $TBL | grep $UNIQUE_COMMENT | grep -oP '(?<=handle )+\d+'

直後はADD

insert の変わりに、ADDもできる

nft add rule $TBL $CHAIN position 2352 mark 666 counter accept

指定位置のinsertが便利

特定のルールの直前に挿入(INSERT)と特定のルールの直後に追加(ADD)をができるのは、nftの特徴で便利。

ファイアウォールは先頭の一つあとや末尾の1つまえをよく使うと思うので、よく使う挿入箇所はショートカット出来てほしいところ・・・悩ましい。

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っている?

nftablesでaccept後のdropにマッチを回避できない。

nftablesでacceptとdropにマッチするとdropになる問題

nftablesを使ってると、accept しても他のテーブルでreject(drop)されてしまうことがある。

複数テーブルでaccept と dropが混合しちゃう

例えば、tableAとtableBにforwardの許可設定を書くわけよ。tableAでAcceptしてるのに、tableBで reject されるから、結果として reject になっちゃう。

table ip tableA {
    chain forward {
        type filter hook forward priority filter; policy drop;
        iifname br0 oifname eth1 ip6 daddr 1.1.1.1 accept comment "# cf"
        jump handle_reject
    }
}
table inet tableB {
    chain forward {
        type filter hook forward priority filter; policy drop;
        $RULE_A accept 
        jump handle_reject
    }
}

8.8.8.8へのforwardを許可する例と拒否する例を同時に入れる。

例えば、8.8.8.8 との通信を許可(Accept)と拒否(Reject)を同時に入れるとどうなるかを見てみよう。

先に結果を述べておくと accept & rejectつまり、true & false となり結果はfalse(拒否・REJECT)になる

8.8.8.8 をforward accept する

nft create table ip allow8888
nft create chain ip allow8888 forward { type filter hook forward priority filter\; policy drop\; }
nft add rule  ip allow8888 forward iifname br0 oifname wan0 ip daddr 8.8.8.8 counter accept
nft list table ip allow8888

8.8.8.8 をforward reject する

nft create table ip deny8888
nft create chain ip deny8888 forward { type filter hook forward priority filter\; policy accept\; }
nft add rule  ip deny8888 forward iifname br0 oifname wan0 ip daddr 8.8.8.8 counter reject
nft list table ip deny8888

パケット送るとreject(unreachable)になる

PS C:\Users\takuya> ping 8.8.8.8

Pinging 8.8.8.8 with 32 bytes of data:
Reply from 192.168.1.1: Destination port unreachable.
Reply from 192.168.1.1: Destination port unreachable.
Reply from 192.168.1.1: Destination port unreachable.
Reply from 192.168.1.1: Destination port unreachable.

カウンタ結果は次のようになる。

table ip deny8888 {
  chain forward {
    type filter hook forward priority filter; policy drop;
    iifname "br0" oifname "wan0" ip daddr 8.8.8.8 counter packets 8 bytes 520 reject
  }
}
table ip allow8888 {
  chain forward {
    type filter hook forward priority filter; policy drop;
    iifname "br0" oifname "wan0" ip daddr 8.8.8.8 counter packets 8 bytes 520 accept
  }
}

カウンタ結果を見ると一目瞭然だが、両方のカウンタが回っている。すなわち、パケットは正しく処理されている。AcceptされたあとにRejectが評価されるのだ。

これはPriorityだろうと思うかもしれ無いが、優先度を変えても結果は同じである。accept が先に処理されてもあとからRejectされるし、Rejectを先にするとAcceptまで到達しない。

ということなので、ブロックされるルールの直前にルールを差し込む必要がある。

どうしたらいいんだ。nftables。accept箇所の先もあるんだ。Acceptでは移行をスキップできないんだ止まらいんだ。どうすれば良いのか。

ちゃんと、insert して指定場所に突っ込むしか無い。

めんどくさいことこの上ないが、ちゃんとルールをINSERTして指定場所に差し込む必要がある。

そうすると、こんどはforwardにルールが集中してしまう、そしてjump があるために、追いかけるのが更に面倒になる。

forwardを見てもjump 、jump jump jump の多段Jumpが大量に記載されることになり、iptablesより追いかけるのが面倒になり・・・

困っちゃうよ。nftables

jump 追いかけるのが面倒。

複数テーブルと複数のチェインがあるので、追いかけにくい。どこでDropされてるのかはcounterを入れて調べることができる。jump さきで jump されてしまうと脳内メモリが追いつかない。

しかし、drop されているルールを回避するのはめんどくさい。

nftables test dump みたいなコマンドがほしい。どのjumpを通ってどこにマッチしてdrop/rejectされてるのか追いかけるのが大変だよ。

nft monitor trace

一応、nft monitormeta nftrace set 1 で追いかけることができるのだが。

nft add chain inet fw4 filter  { type filter hook prerouting priority -301\; }
nft add rule  inet fw4 filter trace_chain meta nftrace set 1

nftace がなんか動かないんですよねぇ。

Error: syntax error, unexpected meta
add rule inet fw4 filter trace_chain meta nftrace set 1

もしかしたら、カーネルモジュール??

counterで地道に追いかけてるけど地獄。。。

nftables で、パケットが該当ルールに届いているかチェックする方法

nftables で、パケットが該当ルールに来ているかチェックする方法

パケットの行先が不明のとき、どこまでマッチしているか調べる必要がある。このときcounter を使うのが一番簡単。

例えば、Google(v6) 宛のパケットを探したい

次のように、ターゲットとなるチェインにカウンタをINSERTする。

GGv6='2404:6800:400a:80b::200e'
TBL='inet fw4'
CHN='handle_reject'
nft insert rule $TBL $CHN meta nfproto ipv6 ip6 daddr $GGv6 counter

このように、カウンタをいれる。実際にはもっと具体的な条件で挟む。( iifname / oifname / ip6 saddr / daddr など具体的に。)

カウンタが進むかチェックする

カウンタをいれたチェインを確認する

nft list chain $TBL $CHN

最初は、パケット数=0、バイト数=0である。

table inet fw4 {
  chain handle_reject {
    ip6 daddr 2404:6800:400a:80b::200e counter packets 0 bytes 0
    meta l4proto tcp reject with tcp reset comment "!fw4: Reject TCP traffic"
    reject comment "!fw4: Reject any other traffic"
  }
}

ping などでパケットを送ってみる。

ping 2404:6800:400a:80b::200e
Destination port unreachable.
Destination port unreachable.
Destination port unreachable.
Destination port unreachable.

パケット数を再確認します。

nft list chain $TBL $CHN

カウントが進んでいるのがわかります。パケット数=4、バイト数=320

table inet fw4 {
  chain handle_reject {
    ip6 daddr 2404:6800:400a:80b::200e counter packets 4 bytes 320
    meta l4proto tcp reject with tcp reset comment "!fw4: Reject TCP traffic"
    reject comment "!fw4: Reject any other traffic"
  }
}

このように、パケットの「カウント」を任意の場所に条件付きで挟み込んで、どこのチェインで処理されたかを知ることができる。

特に、reject / drop の手前に差し込むと効果がある。

nft がおかしいときは counter で探す。

accept -> accept -> reject のように accept後にどこかでreject されてパケットが捨てられる可能性が高いためである。

nftablesでは、複数テーブルで処理されて、chainA accept && chainB reject => reject のように複数チェインのどこかでDROPREJECTされてしまい、気付かないうちにDROPされて届かないということが頻発する。

そのため、カウンタでどこに来ているかを慎重にチェックしないと、思わぬところでリジェクトされて届かない。

hdparm で HGST だけがまともな値を返してくれる。

Seagate / WD / HGST の情報を見たとき

/dev/sdbHGSTなのですが。 APM に対応してて、Acousticに対応してるのはHGSTだった。

買うならHGSTのストレージですね。

ストレージも値段で選ぶなら、ちょっといいやつを買うほうが、細かい制御に対応してて良いのかもしれない。

cpu電源管理が面倒なので、powertop を自動的に実行する

CPUの電源管理設定を調べる。

cat でサクッと目見れる。

 cat /sys/class/scsi_host/host*/link_power_management_policy

https://wiki.archlinux.jp/index.php/%E9%9B%BB%E6%BA%90%E7%AE%A1%E7%90%86#SATA_Active_Link_Power_Management

## cpu の電源管理の設定を変える。

パワーマネジメントを変えることはそう難しくない

for i in  /sys/class/scsi_host/host* ; do 
    echo $i
    echo 'min_power' | sudo tee  $i/link_power_management_policy
done

ノートPCなら、min_powermed_power_with_dipm でいいかと思う。

面倒なので、powertop にやらせる。

このへんは powertop でチューニングされるはず

systemd サービスを作る

cat <<"EOS;" | sudo tee /etc//systemd/system/powertop.service

[Unit]
Description=Powertop tunings

[Service]
ExecStart=/usr/sbin/powertop --auto-tune
RemainAfterExit=false


[Install]
WantedBy=multi-user.target


EOS;

systemd のタイマーを作る。

cat << "EOS;" | sudo tee  /etc/./systemd/system/powertop.timer
[Unit]
Description=PowerTop auto tune

[Timer]
OnCalendar= daily
RandomizedDelaySec=3600
Unit = powertop.service

[Install]
WantedBy=default.target

EOS;

Serviceを有効にする。

sudo systemctl daemon-reload
sudo systemctl enable powertop.service
sudo systemctl start powertop.service

なぜこんなことをするのか

たまに、フルパワーでぶん回してベンチマークを取得たりするとき、元に戻し忘れる。

帰宅したときファンが轟音たててて、戻し忘れてたことを忘れるとかある。悲しい。

対応策は、あれこれあるけど、とりあえず、powertop にお任せ。

参考資料。

https://gist.github.com/shunirr/7510857

nginx の proxy_redirectでlocationヘッダを書き換える

nginx の proxy_redirectを知りました。

Locationを書き換えることができます。

シンタックス

proxy_redirect from to 

実例

proxy_redirect http://localhost:800 https://$server_name

効果

nginx のリバプロ先からLocation・Refreshが返されたとき、その転送先のアドレスを書き換える。

殆どの場合は、リダイレクトはnginxが書き換えてくれるが、websocketやポート番号が異なると書き換えてくれないので、sub_filterや proxy_redirectを使って書き換える必要がある。

正規表現も使える。基本的に proxy pass reverse 的な動きをする。ただし、いつもの通りコンテンツ(Body)は書き換えの対象外。

コンテンツ内部を書き換えるなら、別の方法(sub_filter, body_filter_by_lua_block)を取る。

ディレクトリがマウントされているか調べる

いまのマウントポイントは、proc ファイルシステムから取得できる。

cat /proc/mounts 

これをgrep すればマウント済みか判定できる

cat /proc/mounts  | grep dir 

また、grep には -qs で結果の true/false だけを取れるので。判定に使える。

ディレクトリ(PATH)がマウントされているか調べる。

dir=/path/to/check
grep -qs $dir /proc/mounts && echo $dir is  mounted 

コレを使うと、シェルスクリプトで便利になる。

dir=/path/to/check
if grep -qs $dir /proc/mounts ; do 
  echo $dir is  mounted 
else 
  mount $dir
if

curl でsocks プロキシを使ってssh先ネットワークから外部接続

curl でsocks プロキシを使って別ネットワークから外に出る。

curl でsocks プロキシを使う。

curl --socks4 127.0.0.1:1080 -v  g.co

socks オプションを使えば、HTTPプロキシではないにしろ、簡単にプロキシを挟める。

socks プロキシ作成(SSH

SOCKSプロ棋士の作成は ssh で瞬殺

 ssh -D 1080 remote-router.lan

これで、127.0.0.1:1080 にSOCKSプロキシが立ち上がるんで、あとはcurl で接続すればおっけ

ssh が通ればプロキシ経由できる。

SSHさえ通れば、プロキシを瞬間で作れてネットーワークを繋げれて外に出られる。

Grep に'\K' という便利な書式がある。

Grep に'\K' という便利な書式がある。

look behind を使って、必要な場所を取り出すことができる。

(<?=xxx) を使った場合

ip -o addr show  dev br0 | grep -oP '(?<=inet )[\d.]+'

\K を使った場合

ip -o addr show  dev br0 | grep -oP 'inet \K[\d.]+'

\K の書式について

\K より前はマッチ結果に含まれない(マッチはチェックされる)

echo abcdefg | grep -oP 'abcd\Kefg'

\Kをつけることで、マッチ試行が行われるが、マッチ文字列には取り込まれない。

-o はマッチ結果を表示するオプション

だから、何かの後ろ にあるものを取り出すので look behinde expression (後読み)と呼ばれる。

\Kを使えば、マッチグループを省略できる

\Kが使える環境であれば、後方参照を省略して、マッチ部を取り出せるので、便利。

grep はマッチ部しか取り出さないので、grep と組み合わせると特に有効に働く。

関連資料

https://takuya-1st.hatenablog.jp/entry/2017/11/03/000000

firefox のプロファイルをunionfs(+tmpfs)にして揮発性にして寿命を延ばし高速化する。

firefoxのプロファイルを揮発性(ramdisk)にする

USBメモリUbuntuをインストールし、LiveUSBのように使っているのですが、Firefoxを起動すると遅い。ファイルのキャッシュを作成するときに時間掛かっている模様。これはUSBの寿命にも良くない。

そこで、firefoxのキャッシュフォルダをtmpfs にした

tmpfs /home/takuya/.cache/mozilla tmpfs uid=1000,defaults,nodiratime,noatime,mode=0700 0 0

それでも、まだ遅い。書き込みを見ていると、プロファイルも大量に更新が入ってる。そうかクッキーやヒストリがプロファイル・フォルダに書き込まれるのか。

それなら、プロファイルもramdisk にしたいところ。

プロファイルのramdisk

プロファイルをramdisk 化すると、毎回初回案内が出てきてうざったいし、設定が残らないのも困るので、最低限設定しておいいて、初期設定済みのプロファイルにramdisk をかぶせ使う(unionfs)することにした。

次のようにフォルダを作って、unionfs で使うようにした

役割 変数名 パス
tmpfs volatile /home/$name/.mozilla/volatile
unionfs upper /home/$name/.mozilla/volatile/upper
unionfs work /home/$name/.mozilla/volatile/work
元プロファイル base /home/$name/.mozilla/firefox.base
unionfs merged /home/$name/.mozilla/firefox

最初に、tmpfs をつくって、そのなかにプロファイル格納用の揮発性マウントを作成。
unionfs はワーク領域が必要なので、最初に作ったtmpfs内部に作業エリアを設置。
揮発性マウントの下側に、baseとなるプロファイルをマウント。
揮発性マウントの中に、キャッシュを格納するマウントを追加。

まぁunionfs をためてしてみたかったというのがある。

これでメモリに書き込むfirefoxの設定とした。

ただ、とってもめんどくさいのでfstabに記載することを諦め、起動時に、自動的にマウントを作るようにした。

履歴を保存するときは、手作業でmerged の内容を base へcopy する。umount イベントで実行しようかと思ったけど、後回し。

/etc/systemd/scripts/firefox.mount.sh

#!/usr/bin/env bash

function mount_cache(){
  mount -t tmpfs tmpfs -o uid=$id,defaults,nodiratime,noatime,mode=0700 $cache_mozilla
}
function umount_cache(){
  grep -qs $cache_mozilla /proc/mounts && umount $cache_mozilla

}
function load_env(){
  name=$1
  id=$( id -u $name )

  cache_mozilla=/home/takuya/.cache/mozilla

  volatile=/home/$name/.mozilla/volatile
  upper=/home/$name/.mozilla/volatile/upper
  work=/home/$name/.mozilla/volatile/work
  base=/home/$name/.mozilla/firefox.base
  merged=/home/$name/.mozilla/firefox

  ibus=/home/$name/.cache/ibus
}

function mount_ibus(){
  mount -t tmpfs tmpfs -o uid=$id,defaults,mode=0700 $ibus
}
function umount_ibus(){
  grep -qs $ibus   /proc/mounts && umount $ibus

}

function mount_overlay_profile(){

  [ ! -e $volatile ]; mkdir -p $volatile
  mount -t tmpfs tmpfs -o uid=$id,defaults,nodiratime,noatime,mode=0700 $volatile
  [ ! -e $upper ]; mkdir -p $upper
  [ ! -e $work ]; mkdir -p $work
  mount -t overlay overlay -o lowerdir=$base,upperdir=$upper,workdir=$work $merged

}
function umount_overlay_profile(){
  grep -qs $merged   /proc/mounts && umount $merged
  grep -qs $volatile /proc/mounts && umount $volatile
}

function main(){
  name=$2
  [ -z $name ] && name=takuya
  load_env $name
  case $1 in
   mount)
     mount_cache
     mount_overlay_profile
     mount_ibus
     ;;
   umount)
     umount_cache
     umount_overlay_profile
     umount_ibus
     ;;
   *)
     echo "$0 [mount|umount]";
  esac
}


main $@

マウントを試してみる。

sudo /etc/systemd/scripts/firefox.mount.sh mount
findmnt 
sudo /etc/systemd/scripts/firefox.mount.sh umount
findmnt 

サービスを作って有効化

つくったスクリプトを呼び出すためにサービスとしてログイン時や起動時に実行することにした。

/etc/systemd/system/firefox-tmpfs@.service

[Unit]
Description=Mount %s firefox 
After=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/systemd/scripts/firefox.mount.sh mount %s
ExecStop=/etc/systemd/scripts/firefox.mount.sh umount %s


[Install]
WantedBy=multi-user.target

有効化

sudo systemctl daemon-reload
sudo systemctl enable firefox-tmpfs@takuya.service

履歴の保存

明示的に保存するときは lower にコピーすればいい

rsync -av ~/.mozilla/firefox/ ~/.mozilla/firefox.base/ 

メリット

爆速。本当に速い。キャッシュ作成がブラウザの遅延の原因だとよく分かる。

長寿命。USBメモリMicroSDカードだと書き込みが本当に減るので寿命が長くなる。

懸念点

終了時に保存する方法に、いい方法があればいいんだけど・・・・

unionfs は実装が古いので、aufs やfsprotecで書ければよかったけど詳しくないので。

今回は、apt の firefoxを使っている。ubuntuのsnapstore 版のmozilla はこの方法だとうまくいかないかもしれない。

overlay を作るときは、work は upper と同じフォルダ(マウント)内部にある必要がある。

overlay fs が作れないで苦労した

overlay でファイルシステムを保護しながら書き込みを可能にしようとして戸惑ったのでメモ

sudo mount -a 
sudo dmesg | grep overlay

エラーだった

[58517.488953] overlayfs: workdir and upperdir must reside under the same mount

overlay の作り方

volatile=/path/to/tmpfs
upper=/path/to/tmpfs/upper
work=/path/to/tmpfs/work
base=/path/base
merged=/path/meged

mount -t overlay overlay -o lowerdir=$base,upperdir=$upper,workdir=$work $merged

ここの/path/workdir and upperdir must reside と書いてあるとおり、おなじマウント中に作る必要があった。

たぶん、tmpfs を想定してるのだろうが、tmpfs 内部にフォルダを作ってマウントするというのがめんどくさかった。

nft のコマンドの使い方の例。NFTテーブル操作ガイドを作ろう

nft 使い方まとめ

nft の使い方は、本当にややこしい。覚えることが多くてる辛いので、調べたことをまとめ直す。

ルール閲覧

"コマンド" "役割"
nft list ruleset 全部見る
nft- a list ruleset 管理番号を含める

全体を見渡すコマンドがコレ。ruleではなくruleset

テーブル

"コマンド" "役割"
nft list tables テーブル一覧
nft add table TABLE テーブル追加
nft create table TABLE テーブル追加(既存チェック有り)
nft list table TABLE テーブル中身表示

TABLE<?family> <name> で定義される。

<?family> <name>inet mytableのように、アドレスファミリを付ける場合とつけない場合がある。作成時にアドレスファミリをつけたら以降は全部つけて識別する。作成時の省略なら以後は省略して作業する。

<?family> に使えるのは、inet | ip | ip6 | arp | netdev | bridgeであるが、パケット転送に限ればip, ip6, inet である。inetip,ip6の両方を含む。

add と create の違い

$ nft add    table ip mytable 
$ nft add    table ip mytable 
$ nft add    table ip mytable # 何度もaddでも既存があればスキップ
$ nft create table ip mytable # create は既存があればエラー

チェイン

"コマンド" "役割"
nft list chain TABLE CHAIN チェイン一覧
nft insert chain TABLE CHAIN '{ <rule> }' チェインの先頭にルールを追加
nft add chain TABLE CHAIN '{ <rule> }' チェインの末尾にルールを追加

nft -a list chain inet fw4  srcnat_VmNet

チェインにルールを追加する例

nft -a list chain inet fw4  srcnat_VmNet
nft add chain inet fw4 srcnat_VmNet { oifname "eth2" ip saddr 192.168.11.0/0 counter packets 0 bytes 0 masquerade comment "!fw4: my-sample" \; }

ルール

"コマンド" "役割"
nft insert rule TABLE CHAIN <rule> チェインの先頭にルールを追加
nft add rule TABLE CHAIN <rule> チェインの末尾にルールを追加
nft delete rule TABLE CHAIN handle <ID> ハンドルを指定して消す。
nft add table TABLE { flags dormant \; } ルールを一時休止

ルールの削除は、今のところ、ルールの削除は、Handle <ID>をつかって指定する。

指定したルールの後ろに追加する方法は現在発見されていない。ルールを作り直すほうが確実。

ルール削除はHandle ID

ルール削除は次の通り

nft delete rule TABLE CHAIN handle `<ID>` 

コメント活用したIDの探索

書いたルールがどのハンドルになっているか探すのが大変煩わしい、そこでコメントを使う方法を考えた。

コメントを利用した削除例

例えば、こんな感じに頑張る。

## 追加して
nft add rule inet fw4 srcnat oif eth0 accept comment mycomment      
## コメントを探す
nft -a list chain inet fw4 srcnat | grep mycomment
## コメントから handle を取り出し
HNDL_ID=$(nft -a list chain inet fw4 srcnat | grep mycomment | grep -oP '(?<=handle )\d+')
## ID指定で消す
[ -n $HNDL_ID ] && nft delete rule inet fw4 srcnat handle $HNDL_ID

いずれは、次のような記述も可能になると思うが、まだ無理っぽい。

nft delete rule inet fw4 srcnat oif eth0 accept comment mycomment      

コメントを用いたルールの削除と、JSON出力を組み合わせて頑張ろう

テーブルの追加削除

テーブル存在確認と作成と削除

## 作成  ( add または create )
nft add table ip mytable
## 確認
nft list table ip mytable
## 削除
nft delete table ip mytable

追加の失敗例 :既存がある。

nft create table ip mytable
Error: Could not process rule: File exists
create table ip mytable
                ^^^^^^^

追加の失敗例: 文法エラー

nft create table ip mytable chain
Error: syntax error, unexpected chain, expecting end of file or newline or semicolon
create table ip mytable chain
                        ^^^^^

チェインの追加と削除

## 作成 ( add または create )
nft add chain ip mytable srcnat
## 確認
nft list chain ip mytable srcnat
## 削除
nft delete table ip mytable

srcnat は、予約語なので注意

テーブル名は自由に決められるが、予約語がある。詳しくは、man nftCHAINSを参照すること

予約語については公式ページも参考になる。Priority_within_hook

MASQUERADE を作る例

テーブルにチェインを追加

nft add table ip mytable
nft add chain ip mytable srcnat 
nft add chain ip mytable srcnat_vpn
nft add rule ip mytable srcnat  oifname "eth2" jump srcnat_vpn comment jump-to-my-vpn
nft add rule ip mytable srcnat_vpn ip saddr 192.168.11.0/24  masquerade comment '"srcnat to my vpn';

いくつか、ポイントを解説。

meta nfproto ipv4 oifname "eth2" と書きたいところだが、ip mytable と、v4 限定のテーブルを書いているので、不要である。

コメントで、クォートしている。次のようにコメント記入しているcomment '"srcnat to my vpn'、コレは、bashにより、展開されるので、確実に文字列を引き渡す必要があるため。

確認

$ nft list table ip mytable
table ip mytable {
    chain srcnat {
      oifname "eth2" jump srcnat_vpn comment "jump-to-my-vpn"
  }
  chain srcnat_vpn {
    ip saddr 192.168.11.0/24 masquerade comment "srcnat to my vpn"
  }
}

動作チェック方法

counterを入れてマッチしたパケットの個数をカウントすればいい

nft add rule ip mytable srcnat  oifname "eth2" conter jump srcnat_vpn

counter を入れると、バイト数とパケット数がカウントされる。

丸暗記ポイント追加と削除

追加と削除が、ぱっと見てわかりにくい。

発展途上のコマンドのためサブコマンドが混乱を招きやすい体系である。留意しながら丸暗記しないと大変である。もしかしたら手作業による管理がnftに想定されてないのかもしれない。溜息が出るほどとっつきにくい。

追加と削除がわかりにくいので、とくに致命的じゃないかなって思う。

心配なのは、いま苦労して覚えても、数年後にはガラッとコマンド体系が改められそうと懸念している。懸念でるほど煩雑だ。

ルールの追加がわかりにくい

ルールの追加は add chain table chain { <rule> } 、ルールの削除は delete rule table chain handle <ID>

と思いきや、ルール追加は、add rule table chain <rule> でも行ける。

ルールの削除がわかりにくい

そして、削除はdel では無い。deleteである。add と来たら、ついついdelと打ちがち。しかしdelは存在しない。

愚痴をこぼすと、nftablesをnft って省略するのに、delete をdel って省略したらだめってのはひどい。nft は某用語なので検索しづらい。delを認めないのであれば、同じように nft コマンドではなく、nftables コマンドにしてほしい。

[add | insert] , [add | create] のペアがわかりにくい

テーブルとチェインは、add / create で、ルールはadd / insertになる。

初見殺しである。カジュアル利用の私達は、そんな違いをいちいち覚えてられない。めんどくさい。