それマグで!

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

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

ipコマンドでMAC アドレスを変更する

MAC Addressを変更したい

一時的にMacAddressを変更したいとき。

INF=enp1s0f0 
sudo ip link set dev $INF down
sudo ip link set dev $INF address 6c:4b:90:d8:c3:39
sudo ip link set dev $INF up

一時的に変更できたら問題解決ってことが多いと思う。

ip link set down / upでいったんデバイスをオフにしておくことが味噌だと思う。

永続化したいとき

debian の /etc/network/interfacesなら

auto eth0
iface eth0 inet dhcp
    hwaddress ether 00:11:22:33:44:55

ubuntu のnetplan なら

eth1:
  dhcp4: true
  macaddress: 00:00:00:00:02:00

systemdなら /etc/systemd/network99-my.linkを作る

[Match]
Name=eth2

[Link]
MACAddress=00:00:00:00:02:00

2023-05-20追記

ip コマンドではないが、変更を永続化したい方法を追記。

ubuntuから snapd(snap/snapcraft) を除去(アンインストール)て snapcraft 抜きのubuntuにする

ubuntu にある snapd と snap.lxd はすごく便利だからプリインストールは嬉しい。

でも、用途が限られているときには、snapd が要らない。

snapd の消し方

apt を使ってしまいますが、さきに snapd でsnap されているアプリを消す必要があります。

最初にsnap app を消す

初期導入されているSnapも含めて現在のSnapd 状況を見る。

sudo snap list 

全部消していく。初期導入されているのは core / snapd / lxd

sudo snap remove lxd
sudo snap remove coreXX # core20 core18とか
sudo snap remove snapd

消しても消えないものがあるので、forgetする

sudo snap saved
sudo snap forget 1 

apt でsnapd を消す。

## sudo apt autoremove --purge snapd で一発
sudo apt purge spand 
sudo apt autoremove 

ファイルを除去する

残存ファイルを除去する

sudo rm -rf /var/cache/snapd/
rm -rf ~/snap # 各ユーザーが持ってるsnap

これで無事に消えるが。再起動が必要

automount系を除去する。

systemd いくつか自動マウント系のジョブ・ユニットが残る。

sudo systemctl list-units snapd.*

必要に応じて除去したり。再起動して反映させる。

sudo systemctl disable snapd.*

めんどくさいのですが、無事に消えます。

aptを先にやってしまった場合

apt を先にやってしまった場合は、ずっとファイルが残り続けたり、起動時に systemd units がエラーになる。

apt install snapd 
## 
snap remove XXX
apt remove snapd

再度snapd をインストールして、snapdを除去していけば解決するはずである。

無事に除去された。

全部消したら、3GB未満になった。

takuya@spare:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda4       7.8G  2.6G  4.8G  36% /

snapd はいらないときもある

ubuntugrub メニューを隠したり、snapd を推してきたり。cloud-initを入れたりと、便利な反面ってところが見え隠れする。

消せるから特に気にしないけどね。

注意:

LXCのコンテナでこれをやると、LXCホスト側にも影響することがあるので注意。

lxc(snap版)は ホストーゲスト間でLXC(snap)を共有しているので、security.privileged: "true"の場合は、snap の操作が、lxc ホスト側に影響することがあるのでむやみに消すべきではないと思われる。

私はやらかしました snapd を消したら、ホスト側でも見えなくなって背筋が凍った。

/snap/lxd/24918/commands/lxc: 5: cannot open /proc/self/attr/current: Permission denied
/snap/lxd/24918/commands/lxc: 6: exec: /usr/bin/aa-exec: Permission denied

などとなり、app armor exec (aa_exec)がエラーになり、lxc コマンド関連が全部動かなくなった。もう泣いた。

スタートメニューの検索が遅いと思ったら。WindowsがCloudドライブを検索に含めてる。

windows でMSアカウントでログインすると。

スタートメニューの検索から、クラウドドライブの検索やBingの設定や閲覧履歴から検索されていた。

デフォルトでONになるのはやめてほしい。

すごく気持ち悪い。

windowsが遅いときやCPUファンが回ってるときってだいたい検索インデックスだからなぁ。

MSアカウントをゴリ押しされるの、いい気分がしない。

sgdisk で末尾から指定サイズを確保する。

末尾から確保する

sgdisk で末尾から5Gを確保するには次のようにする。

sudo sgdisk -n ":0:-5G" /dev/sda      

先頭から確保する

sgdisk -n "0::+512M" /dev/sda

応用:末尾に5GBを残す

末尾に指定サイズを残すには。

sudo sgdisk -n "0::-5G" /dev/sda

比較

"0::5G" /dev/sda # 先頭から
":0:-5G" /dev/sda    # 末尾から

順番を変えれば良いことがわかった。単純ですね。

0がスタート地点なので先頭からってことですかね。

sgdisk コマンド

sgdisk コマンドはこの様になっている。

sudo sgdisk -n パーティション番号:開始セクタ番号:末尾セクタ番号 /dev/sda

パーティション番号を省略したり0にすると新規パーティションが自動採番かな

セクタ番号の代わりに、+5G-5G のように単位と数字を符号とともに使える。

セクタ数で指定する

64Mを確保する。

64 * 1024 * 1024 / 512 =131072 # セクタ数

セクタの数で指定。

先頭から、64Mをセクタ数で指定する。

sudo sgdisk -n '0::+131072' /dev/sda

別解:開始セクタが2048の場合

2048+133120 -1 = 133119

セクタの先頭(インデックス)を指定して数えるので、-1している。序数と個数は違うので注意。

sudo sgdisk -n '0:2048:133119' /dev/sda         

windows のphoto以外の画像ビューワ

windows10/11 のフォトが許せない。

左右に”<>”が表示され、画像を閲覧するのが苦痛だ。

上記の写真のようにオーバーレイ表示が入るので、非常に紛らわしい。

また、ちょっとマークアップしたいときにいちいち、他のアプリで開き直すのも不便だ。代替候補を探したい。

QuickLook

Windows Storeで配布されているQuickLookはいい感じ。

https://github.com/QL-Win/QuickLook

ただ、Spaceキーで表示するのでダブルクリックでプレビューできない

Snip Sketch

スクショ撮った後に出てくるアプリケーション

これは、非常によく使うので慣れたUIであるため、写真のプレビューには向いている。

mspaint.exe

昔からあるペイント。悪くはないが、ウインドウ幅に合わせた拡大縮小ができないので、プレビューには不向きかも

snip がベストかも

インストールをせずに使える snip sketch を画像ファイルに関連付けるのがベストかもしれない。

そもそも、拡張子pngをphotoに関連付けられてたのが異常なのかもしれない。

Raspiでx86のLinuxにChrootで入り編集できて驚いた(異なるCPUアーキテクチャへの chroot )

Raspberry Pix86_64の起動ディスクを修正・・・できる・・だと?

昨日、Raspberry Pi ( armvl64 / aarch64 ) なことを失念したまま、x86ubuntuのディスクにChrootしたんですよ。

x86_64のUSBディスクにchrootしても動くんですよ。えええ。

RaspberryPi(arm)から USB-ubuntu(x86)へchroot ができてる。

マウントしてchrootして・・・

takuya@raspi-ubuntu:~ $ sudo mount /dev/sda1 /mnt
takuya@raspi-ubuntu:~ $ sudo chroot /mnt
root@raspi-ubuntu:/#  
## 動いてる。。。

エラーにならないんですよ。CPUアーキテクチャが異なるので、エラーになるかと思いきや、動いてるんです。意味わかんないwwww。

x86chrootしたときのuname

慌てて、CPUアーキテクチャを調べました。

root@raspi-ubuntu:/# uname -a
Linux raspi-ubuntu 5.15.0-1027-raspi #29-Ubuntu SMP PREEMPT Mon Apr 3 10:12:21 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

CPUは ARM / Cortex-A72 なのに x86_64

CPUを見ると Architecture: x86_64 となっている。マジですか。

root@raspi-ubuntu:/# lscpu
Architecture:           x86_64
  CPU op-mode(s):       32-bit
  Byte Order:           Little Endian
CPU(s):                 4
  On-line CPU(s) list:  0-3
Vendor ID:              ARM
  Model name:           Cortex-A72
    Model:              3
    Thread(s) per core: 1
    Core(s) per socket: 4
    Socket(s):          1
    Stepping:           r0p3
    CPU max MHz:        1800.0000
    CPU min MHz:        600.0000
    BogoMIPS:           108.00
    Flags:              fp asimd evtstrm crc32 cpuid
Caches (sum of all):
  L1d:                  128 KiB (4 instances)
  L1i:                  192 KiB (4 instances)
  L2:                   1 MiB (1 instance)
Vulnerabilities:
  Itlb multihit:        Not affected
  L1tf:                 Not affected
  Mds:                  Not affected
  Meltdown:             Not affected
  Mmio stale data:      Not affected
  Retbleed:             Not affected
  Spec store bypass:    Vulnerable
  Spectre v1:           Mitigation; __user pointer sanitization
  Spectre v2:           Vulnerable
  Srbds:                Not affected
  Tsx async abort:      Not affected

どうやって動いているのか。

落ち着いて、プロセスを確認した。

/usr/libexec/qemu-binfmt/x86_64-binfmt-P というQEMU経由で動作していました。

そういえば、このRaspberryPi4は、以前にDockerでX86バイナリを動かすために、qemuをいれていた。

sudo apt-get install qemu binfmt-support qemu-user-static

chrootqemu が自動的に動くようです。

つまり、chrootするときに、/bin/bashアーキテクチャを見て、x86_64のバイナリなので、qemu経由で動作している。

再現方法

qemuと binfmt-supportを入れる。

sudo apt install qemu-system binfmt-support qemu-user-static qemu-user-static:i386

適当な、x86バイナリを持ってくる

scp ubunt-x86:/usr/bin/bash ./bash-x86

動かしてみる

./bash-x86

ファイルが見つからないエラー。

takuya@raspi-ubuntu:~$ ./bash-x86
x86_64-binfmt-P: Could not open '/lib64/ld-linux-x86-64.so.2': No such file or directory

動いてます。gccのlib ファイルが見つからないというだけです。CPUアーキテクチャ相違エラーが出ません。

バイナリのフォーマットを見て、自動的にqemuを使って起動してくれるんですね。

chroot で動かす /mnt/bin/bashqemu経由になる、

bashx86なので、armなRaspberry Piからchrootしてもbashにあわせて、binfmtが発動しQEMUが起動するというわけだ。

qemu-binfmt/x86_64-binfmt-P があるからだ。

なるほど、これで冒頭のなんで動くの?という謎がとけた

binfmt-supportがあれば、x86LinuxのHDDをRaspberry Piからでもメンテナンスできるんですね。おもしれぇ

動作の速度

ただし、動くけど恐ろしく遅い。crypttab/fstabの修正くらいなら待ち時間に耐えられる。update-initramfsや grub-installはかなり辛抱強く我慢が必要(通常の10倍位、1分で終わる処理が10分くらいかかった)。vim でファイルを修正や passwdを書き換えるくらいならchrootqemuで起動してることに気づかないくらいだ。

追記

chroot / binfmt / qemuで調べたら、次の記事が見つかった。

Raspberry Pi のセルフビルド環境を QEMU で作る

ここに詳しい動作機序が解説してあった。どうやら、chrootする先にもQEMUがインストールされている必要がある様だ。

いか抜粋(一部)

binfmt はqemu-arm-static を実行しようとするが、すでに chroot してしまっている。なのでchroot 先にも、binfmt 設定とおりに qemu-arm-static を配置しておく必要がある。

いか抜粋

binfmt-support

実行可能ファイルのヘッダを見て、必要なランタイムやインタプリタ等を準備する。 スクリプトの #! 行を見てインタプリタ経由で実行させたり、ELF バイナリのヘッダを見てダイナミックリンカ (ld-linux.so) 経由で実行させたりしているのはこの仕組み。このパッケージはこの仕組みを設定ファイルで拡張可能にする。

qemu-arm-static

ARM 機械語の部分を x86 機械語に翻訳して実行し、システムコールはホストのものを呼び出すようになっている。通常の QEMU と違ってハードウェアをエミュレートしなくて良い分、高速に動作する。 スタティックリンク版を使う理由は後述。

ELF バイナリのヘッダにはアーキテクチャを示す項目があるので、「ARM アーキテクチャな ELF ヘッダを見つけたら qemu-arm-static を使って実行する」ように binfmt に設定すれば、ARMバイナリを x86 でも透過的に実行できるようになるはずである。

Linux のダイナミックリンカは /lib/ld-linux.so という名前なのだが、ARM も x86 も同じ名前で参照しようとするところ、実際にこのパスにあるのは x86 版なわけなのでエラーになる。このファイルは x86 バイナリの実行に必須なため置き換えることもできない。同様の問題が libc (libc.so) にもあてはまる。 chroot 先のディレクトリに ARM バイナリ一式(ランタイムも)と qemu-arm-static を置くことで、以下の流れが成立する。

chroot

chroot がルートディレクトリを変更する(元のディレクトリにはもう戻れない)
  ↓
(たとえば)chroot 先の /bin/bash を実行する
  ↓
ARM バイナリなので binfmt が /usr/bin/qemu-arm-static 経由で実行しようとする
  ↓
chroot 先の /usr/bin/qemu-arm-static を実行(これは x86 バイナリ)
  ↓
ARM 版のダイナミックリンカやランタイムを chroot 先から探すようになる
  ↓
bash 起動。この bash から起動したコマンドはすべて chroot の下で動作する。

binfmt は元々のルートディレクトリのつもりで qemu-arm-static を実行しようとするが、この場合すでに chroot してしまっているので、chroot 先のディレクトリにも、binfmt の設定に書かれているパスのとおりに qemu-arm-static を配置しておく必要がある。

atコマンドでタイマーを実験する(date コマンドと組み合わせ)

atq コマンドで、スケジュール・アラーム(目覚まし)機能が作れる。

指定時間のコマンド実行予約が、at コマンドを使って作れる。

 at "'19:45 03.05.23'" -f '/usr/bin/whoami'

書式は次のとおりである。

 at "時:分 日.月.年" -f コマンド

日付の指定がめんどくさい。

date コマンド at 用の日付を作る

date コマンドを使って、「もうすこし柔軟に」実行時分を指定する。

1 分後の時間

date -d ' +1 min' +'%H:%M %d.%m.%y'

3時間後の時間

date -d ' +3 hours' +'%H:%M %d.%m.%y'

at / date を組み合わせる

1分後に実行する予約する。

 at $(date -d ' 1 min' +'%H:%M %d.%m.%y') -f '/usr/bin/whoami'

ユーザーごとのキューを使う。 atコマンド(atq) は atq の名前付きキューをユーザごとに持つ。

 sudo -u www-data at $(date -d ' 2 min' +'%H:%M %d.%m.%y') -f '/usr/bin/whoami'

これで、ユーザーも指定できる。

関連資料

過去の資料。

atコマンドの使い方(指定時間にコマンドを実行する)でスケジュール実行。 - それマグで!

dateコマンドを見なおしてみる。dateコマンドオプションは使える - それマグで!

413 File too Largeになる。nginx+php-fpmの見落としがちな設定

nginx + php-fpmで構成するときに「アップロードサイズ」に注意する。

php-fpm 側のphp.iniの設定でアップロード上限がある、

nginx 側にも、アップロード上限がある。

後者を見落としがちなので注意する。わたしもたまにやらかす。

NextcloudのデータをVPS間で移動したときに、nginx側の設定を忘れて、写真のバックアップが取れなくなっていた。

php側の設定

/etc/php/{8.0,8.1}/fpm/pool.d/nextcloud.conf

;; php 全般
php_flag[display_errors] = off
php_admin_value[memory_limit] = 512M
php_value[upload_max_filesize]=1G
php_value[post_max_size]=1G
php_value[max_input_time]=360

nginx側の設定

/etc/nginx/sites-enabled/nextcloud.conf

server {

  client_max_body_size 1024M;

nginxのclient_max_body_size ( client max body size =HTTPのペイロードのサイズ上限)が抜けていると、デフォルト値=1Mが適用される。

忘れないように注意が必要。

3年くらい前にもいちどやらかしたのだけど、またやらかした。

パスワードを確認する。(今のパスワード合ってるか

linuxの「パスワード」がわからないとき

パスワードの変更コマンドで確認する。

~$  passwd

生成してみる。

sudo cat /etc/shadow | grep takuya
takuya:$y$j9T$txxxxxPxxx.xkxx:19249:0:99999:7:::

パスワードのハッシュ値をみて、同じ値を生成してもいいけど、絶対にめんどくさい。

SSHで鍵認証

SSH鍵認証で、パスワードを普段から使わないので、ふとした瞬間にパスワードがわからなくなるので、どこかにメモっておきOpenSSLでAES暗号化するとか、パスワード保存ツールに保存しておくほうが良い。

CA管理機能使わずにCSR署名する。コピペでつくる自己署名な証明書。

CA機能使わずにCSR署名する。

openssl の証明書の署名を見ていると、openssl ca を使わずに、CSRにサインする方法を知った。

openssl x509 -req -in my_cert_req.pem \
   -days 365 -CA ca_cert.pem \ 
  -CAkey ca_private_key.pem \
  -CAcreateserial -out my_signed_cert.pem

使っているコマンド

openssl x509 -req

openssl x509 で リクエストを扱う。え?これは知らなかった。

-req
By default a certificate is expected on input. With this option 
a PKCS#10 certificate request is expected instead,
 which must be correctly self-signed.

X.509 extensions included in the request are
 not copied by default. X.509 extensions to be 
added can be specified using the -extfile option.

なるほど、openssl caのCA機能が内部的に使ってるものっぽい、これを使って署名リクエストへ自分の証明書で検証可能か署名を作るわけか。

試してみる。CSRに署名する

自己署名な証明書を作って、次にCSRに署名する。

最初に秘密鍵自己署名証明書を作る。

CAとして振る舞うことができる自己署名証明書を作る。

次のコマンドを貼り付けると、CA証明書ができる。

openssl genrsa -out myCA.key
openssl req -new -subj "/C=JP/ST=Kyoto/O=my/CN=MyCA" -key myCA.key -out myCA.csr
openssl req -text < myCA.csr 
openssl x509 -req  -in myCA.csr -signkey myCA.key  -out myCA.crt

上記の流れを1回のコマンド起動で終わらせることができる。

openssl req  -nodes -new  -x509\
 -days 365\
 -subj "/C=JP/ST=Kyoto/O=my/CN=MyCA" \
 -newkey rsa:4096\
 -keyout myCA.key \
 -out myCA.crt

( -nodes は no des の意味。パスフレーズによる暗号化をスキップする)

秘密鍵作成・リクエスト・自己署名を1行で終わらせられた。便利。

自己署名証明書を確認

openssl x509 -text -noout < myCA.crt

コピペで実験用の自己署名証明書が作成できて便利ですよね。

CSRに署名する(自己署名CAから証明書を発行)

次に、証明書を発行する。さきほど作った自己署名証明書を用いて、別の鍵の署名リクエスト(CSR)にサインする。

openssl req -new -nodes  -x509 \
  -subj "/C=JP/ST=Kyoto/L=Kyoto City/O=acid/CN=raspi3.lan" \
  -newkey rsa:4096\
  -keyout server.priv.key \
  -days 365 \
  -CA myCA.crt \
  -CAkey myCA.key \
  -CAcreateserial \
  -out server.crt

これは、以下の手順と同じである。 任意の公開鍵を作る。次に署名リクエストを作る。

openssl genrsa -out  server.priv.key

署名リクエストを作る。

openssl req -new \
  -subj "/C=JP/ST=Kyoto/L=Kyoto City/O=acid/CN=raspi3.lan" \
  -key server.priv.key \
  -out server.csr

ファイルができた

ls server.*
server.csr  server.priv.key

作った署名リクエストに、自己署名証明書で署名する。

openssl x509 -req\
 -in server.csr \
 -days 365 \
 -CA myCA.crt \
 -CAkey myCA.key \
 -CAcreateserial \
 -out server.crt

ファイルができた

ls server.*
server.crt  server.csr  server.priv.key

署名した証明書を確認する。

openssl x509 -text -noout < server.crt

自己署名CAのシリアルが作られている。

 $ cat myCA.srl
 7C10C866EA0EFF73C4C957981DC32D9F2884F769

次のようなディレクトリ構造になった。

 $ ls -l
合計 28
-rw-rw-r-- 1 takuya takuya 1090  3月 27 14:15 myCA.crt
-rw-rw-r-- 1 takuya takuya  940  3月 27 14:15 myCA.csr
-rw-rw-r-- 1 takuya takuya 1679  3月 27 14:15 myCA.key
-rw-rw-r-- 1 takuya takuya   41  3月 27 14:18 myCA.srl
-rw-rw-r-- 1 takuya takuya 1131  3月 27 14:18 server.crt
-rw-rw-r-- 1 takuya takuya  980  3月 27 14:17 server.csr
-rw-rw-r-- 1 takuya takuya 1675  3月 27 14:17 server.priv.key

openssl ca によるCA機能だとわかりにくい部分がなくて良い。CAを使うと、証明書の発行と失効管理が煩わしい。

なので、実験用にCA署名が必要な証明書を作ならこの記事の方法が手軽でよい。署名作成する言う点もわかりやすい。

まとめ

2行あればCAとサーバー証明書を作れる

## myCA 作成
openssl req -nodes -new  -x509\
 -days 365\
 -subj "/C=JP/ST=Kyoto/O=my/CN=MyCA" \
 -newkey rsa:4096\
 -keyout myCA.key \
 -out myCA.crt
## myCAでserver.keyを作って署名
openssl req -new -nodes  -x509 \
  -subj "/C=JP/ST=Kyoto/L=Kyoto City/O=acid/CN=raspi3.lan" \
  -newkey rsa:4096\
  -keyout server.priv.key \
  -days 365 \
  -CA myCA.crt \
  -CAkey myCA.key \
  -out server.crt

CSRや鍵作成のあたりをすっとばせるのは、実験には便利だよね。

参考資料

2023-06-12

sudo つけやすいように、シェルのリダイレクト処理を out に書き直し。

冗長な言い回しを見直し。

nftables移行のためnftコマンドとnftablesを調べたときのメモ。

nftablesへの移行

openwrt をアップグレードしたら、fw3 が fw4 にアップグレードされて、nftablesがデフォルトになった。

そして、nftablesを使わないルールがあると、注意・警告が出るようになった。とても煩わしいし、ルールの確認のために見る箇所が増えて不便になった。残念だが、iptables利用をやめてnftablesに書き直す必要が出てしまった。

nftablesについて

nftables は テーブルー>チェーンー>ルールの順に管理されている。

テーブルを作ってその中にチェーンを作成、チェーンの中にルールを作成

チェーンは、Hookポイント(prerouting , forward , postrouting , input , output ) に登録して使う。登録しないときは、単なる関数のように振る舞うチェーン(Jump用)となる。テーブル内のチェーンは優先度に従って適用される。Dropされると終了。

iptables-translate で nftablesに変換できる。

iptables, ip6tables,ebtablesの3つの類似機能がnftablesに統合できるようになった。

追加と削除

テーブル追加・削除・無効

nft add table <?family> <name>

 nft add table ip my_ssh_mangle
 nft delete table ip my_ssh_mangle

add / create

nft add table ip my_ssh_mangle
nft add table ip my_ssh_mangle # エラーにならない
nft create table ip my_ssh_mangle # エラー

既存のテーブルのチェックしてから消す

nft delete table ip myA # 未存在でエラーに
## チェックしてから消す。
nft list tables | grep -wq "table ip myA " && nft delete table ip myA

テーブルを消すと、内包されるチェインも消える。チェインに内包されるルールも消える。

チェイン追加・削除(ベースチェインあり)

nft add chain ip sslh_mangle prerouting { type filter hook prerouting priority mangle \;}
nft delete chain ip sslh_mangle prerouting

hook はチェインが実行されるタイミング。type は何をするか。

ベースチェインに書いてるものは類似キーワードが多く初見殺しなので、以下をよく覚えておく。

type <type_name> hook <hook_name> priority <num|name>

type_name や hook_nameは固定(netfilter由来)である、これについては後述の図を参考にする。

チェイン追加・削除(ベースチェインなし、単なる関数)

nft add chain ip my_sslh_mangle my_chain

ルール追加・削除

nft add rule <?family> <chain> conditions statements;

nft add rule ip sslh_mangle output oifname eth0 tcp sport 443 meta mark set 1 counter accept comment '"takuya:fw1234"'

counter / accept/commet が 処理(statements)。その前につくのがマッチ条件

削除は、handle経由でやる

# handle IDを調べて消す
nft -a list ruleset  | grep 'takuya:fw1234' 
nft delete rule ip my_ssh_table my_chain handle 9999
# サクッとforループで
for i in $( nft -a list ruleset | grep takuya | grep -oE  '[0-9]+$'); do  
     nft delete rule inet my_table my_forward handle $i 
done

コメントを付けておくと、一つずつ消しやすい。ただ、テーブル全消しのほうが速いし楽。テーブル毎に分かれるのがnftablesなので。

コメントの付け方

コメント文字列は、bashスクリプトで解釈されないように適宜エスケープする必要がある。また空白があるコメントはクォートで囲む必要がある。

nft add rule ip tableX chainY oif eth0 accept comment mycomment
nft add rule ip tableX chainY oif eth0 accept comment '"my comment"'
nft add rule ip tableX chainY oif eth0 accept comment '"my: comment"'

これは、bashでクォートが処理後に。nftに引数を1つで渡すためでもある。融通が利かない。

comment my_comment
comment 'my_comment'
comment "'my comment'"
comment "my comment" # comment my commentに解釈されエラーに

一覧

うえから、全部表示・テーブル一覧・テーブル指定

nft list ruleset
nft list tables
nft list table mytable

チェインの中身を見る

nft list table mytable
nft list chain mytable mychain

ハンドルを付与してルールを確認する。nft -> nft -a にする

nft -a list ruleset
nft -a list chain mytable mychain

テーブルの休止

テーブルの休止・休眠状態を作る

使い所がないが、休止機能がある。

nft add table inet mytable { flags dormant \; }

dormant フラグを設定したら、休止状態になる。

flagを解除する方法が特に無いみたい。nft delete flag inet mytable dormant が使えればいいが、公式に見あたら無い。

フラグ解除ないので、今のところ使い所が無い。

次の用途だと活用できる。

  • シンタックスチェック的にテーブルへ突っ込む
  • 試行錯誤の途中で、あと保存することを前提に、いったん無効にする。

休止を解除する方法がサポートされてないので、用途が限られる。シンタックステスト目的以外で使わないほうがベター。個人的な見解でいえば、tcp flagと紛らわしいので、将来的に別の名前になるんじゃね。(ていうか別名にしてほしい)

dormantはITではあまり見ない単語。英語で、休眠・休止の意味。dormire 、睡眠薬ドリエルや、寮(ドーム・ドミトリ)や何かと同じ語幹で睡眠を表す。ドリーム(dream)も仲間か思いきやdreamは違うらしい。

nat するサンプル

単純にMaqueradeするサンプル

  nft add table $TABLE
  nft add chain $TABLE prerouting  { type nat hook prerouting priority 0 \; }
  nft add chain $TABLE postrouting { type nat hook postrouting priority srcnat \; }
  nft add rule  $TABLE postrouting oifname $VPNIF masquerade

注意点。preroutingとpostroutingは、ペアで記入する必要がある。(最初から入ってたりするので気づかない事がある。)

ただし、このマスカレードは、FORWARDがAcceptされていることが条件。

ディストリビューションのデフォルトでDropされているなら、Acceptを入れてあげる。たとえば、OpenWrtではデフォルトがDropなので、FORWARDできない。転送許可してあげる。

デフォルトDROP(OpenWrtのfw4)

# nft list chain  inet fw4 forward
table inet fw4 {
    chain forward {
        type filter hook forward priority filter; policy drop;

OpenWrtでの許可例

nft insert rule inet fw4 forward oifname $VPNIF accept 
nft insert rule inet fw4 forward iifname $VPNIF accept 

map/set を使う

iptablesみたいに、何行にも同じことを数字を変えて書く必要がない。

単純なSet

## 例1
meta nfproto ipv4 iifname { "wg0", "wg1", "wg3","tun1" }
## 例2
ip saddr { 172.16.3.0-172.16.4.255, 192.168.12.0/24 }
## 例3
ip daddr { 192.168.12.5, 192.168.12.21 }

巨大なセット

セットに名前をつけて参照できる。

set public_dns {
    type ipv4_addr
    elements = { 1.0.0.1, 1.0.0.2,
        1.0.0.3, 1.1.1.1, 8.8.8.8, ... }

@を使って使える。

oifname "wg1" ip daddr @public_dns drop 

map

同じことを3回ペアで書くときに、キーをつかったMapを書く

これが

iptables -t nat -A PREROUTING -p tcp --dport 1000 -j DNAT --to-destination 1.1.1.1:1234
iptables -t nat -A PREROUTING -p udp --dport 2000 -j DNAT --to-destination 2.2.2.2:2345
iptables -t nat -A PREROUTING -p tcp --dport 3000 -j DNAT --to-destination 3.3.3.3:3456

こう

nft add rule nat prerouting dnat to \
    tcp dport map { 1000 : 1.1.1.1, 2000 : 2.2.2.2, 3000 : 3.3.3.3} \
  : tcp dport map { 1000 :    1234, 2000 :    2345, 3000 :   3456 }

nftables/netfilterの処理図

次のような処理を順番にしている。

ここから、prerouting 、forward と input がわかる。またrouting が ip route を使ってることがよく分かる。

https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

nftables の処理の図。

nftables は次のような hook ポイントを使ってnetfilterに処理を入れているとよく分かる図。

https://people.netfilter.org/pablo/nf-hooks.png

たとえば、add rule {type nat hokk prerouting \;}とした場合、この図のprerouting hookにあたり、preroutingが行われていることがわかる。type natは前図(netfilter)に記載があり、netfilterの<nat>/preroutingに処理を入れていると読み取れる

デフォルトの優先度(priority)

hook ごとに、priority が数字が決まっていて、それぞれのデフォルト値になっていることがよく分かる。

この図から、priority filterpriority 0と同等であるとわかる。名前や数字が登場してわかりにくい。紛らわしい。

表のように、名前は数字の別名であると覚えておえけば良い。あとは名前から分かる。名前が同じなら同じ優先度・近い優先度である。

https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks

実際に使ってみて気づいたのだが、次のように、1や-1を設定しても実際に1になるわけでない

type filter hook forward priority 1;
type filter hook forward priority -1;

次のようになる。

type filter hook forward priority filter +1;
type filter hook forward priority filter -1;

つまり、優先度は type からの固定値があり固有値 ± 設定値であるようだ。上記の例だとfilter+1となっていた。同タイプのチェイン間で前後を調整する目的で使うのかもしれない。

nftables の優先度の大小のついて (2023-09-05 追記)

nftables の数字の大小はそのまま優先度である。小さい方が優先マイナスもある が、基本的には既定値±1で操作する。

https://stackoverflow.com/questions/38162173/how-is-the-order-of-tables-chains-in-nftables-arranged

Here higher priority value means lower priority and vice-a-versa.

数字が大きいほど優先度は低い。その逆も然り。

-1 と 0 だと、-1 が先に処理される。

nft add table ip filter2
nft add chain ip filter2 forward {type filter hook forward priority 0 \;}
nft add table inet filter
nft add chain inet filter '{type filter hook forward priority -1 }'

この場合は、forwardの既定値より±1されるだけであり、静的に-1 が入るわけではない。

テーブル・ルールが見つからない・指定できない(ip / inet 省略)

テーブルが見つからないというときに一番多いのが、 ip / inet の省略の有無。

次のように、テーブルを造ったとき。そのまま消せる

nft add table my_table
nft delete table my_table

IPを省略せずに作った場合

nft add table ip my_table
nft delete table my_table ## O.K. 省略形
nft delete table ip my_table ## O.K.

inetを指定した場合

nft add table  inet my_table
nft delete table my_table # エラー 省略形
nft delete table inet my_table # O.K.

ip6を指定した場合

nft add table ip6 my_table
nft delete table my_table # エラー省略形
nft delete table ip6 my_table # O.K.

このように、inet my_tableip6 my_tableとした場合は必ずinet/ip6を入れないとエラーになる。delete などするときに忘れがちなので注意。

openwrt のnftables

kmod-nft-socketがなかったりでsocketが使えなくてハマった。

kmod-nft-*で 検索して必要なモジュールを入れてあげないと動かないことがある。

openwrtでの例。

気づかずに、2時間使ってしまった。

nftablesのモジュール

コンパイル方法(公式)を参考にすると、モジュールはコンパイル・オプションで入ってるはず

% grep CONFIG_NFT_ /boot/config-4.2.0-1-amd64
CONFIG_NFT_EXTHDR=m
CONFIG_NFT_META=m
CONFIG_NFT_CT=m
CONFIG_NFT_RBTREE=m
CONFIG_NFT_HASH=m
CONFIG_NFT_COUNTER=m
CONFIG_NFT_LOG=m
CONFIG_NFT_LIMIT=m
CONFIG_NFT_MASQ=m
CONFIG_NFT_REDIR=m
CONFIG_NFT_NAT=m
CONFIG_NFT_QUEUE=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_REJECT_INET=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_CHAIN_ROUTE_IPV4=m
CONFIG_NFT_REJECT_IPV4=m
略

ただ、通常は、ディストリビューション提供のパッケージを使ってるはずなので。ディストリビューションのパッケージ管理をよく見ること。

openwrt の場合

opkg find kmod-nft*

debian / ubuntu

apt list *nft*

パッケージ管理で入ってるはずのモジュールは、一部がカーネル・モジュールなので、カーネルモジュールを確認する

## openWrt
lsmod | grep ^nf_tables | awk '{ print $3 }' | xargs echo  | tr ',' '\n' | sort
## ubuntu 
lsmod | grep ^nf_tables | awk '{ print $4 }' | xargs echo  | tr ',' '\n' | sort

意外なところでシンタックスエラー( unexpteced )が出るときは、モジュールを疑っても損しない。

iptablesで入れたルールの行き先。

nftablesが提供する iptablesコマンドがある。

追加したルールは暗黙的にnftablesに登録される

iptables(nftables提供)で条件を入れると、iptablesは、nftablesとして投入される。

iptables(オリジナル)は初期テーブルが存在した、iptables(nftables提供)でも同じように暗黙的に初期テーブル名が用意されている。初期テーブルは自動的に作られて名前も固定である。

iptablesで追加した場合

iptables -I FORWARD -i tun2  -j REJECT

一覧を見てみる。

iptables -L

iptables(オリジナル)と変わらない結果が得られるが

# Warning: iptables-legacy tables present, use iptables-legacy to see them
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

nftablesとしてルールは存在している。

table ip filter { # handle 6
chain FORWARD { # handle 1
        type filter hook forward priority filter; policy accept;
        oifname "tun2" counter packets 0 bytes 0 accept # handle 2
    }
}

上記のような nftablesのテーブル名とチェーンが作られている。

このことから、iptables(nftables提供)はnftablesのサブセットのような扱いを受ける。

iptables追加したでルールは、処理にnftablesが使われる。nftablesルールが適用されてから、iptables追加ルールが適用される、またはその逆が起きる。それゆえにiptablesだけでパケット処理完結しない。テーブルを見てわかる通りiptables処理後にnftablesルールが待ち構えてることがあるため、iptable/nftablesと混ぜて書くと思わぬ通信切断を招くかもしれない。

iptables-legacy と iptables(nftables)

少々ややこしいが、iptables(nftables)とiptables-legacy の2つのコマンドがある。

iptables-legacy コマンド

#  iptables-legacy --version
iptables v1.8.7 (legacy)

iptables(nf_tables)コマンド

# iptables --version
iptables v1.8.7 (nf_tables)

iptables-legacyは、従来のiptablesである。nftablesとは別物である。

iptables(nftables)で記載されたルールはnftables内にサブセットのように登録がされる。一方で iptables-legacyは、いままで通りiptablesのみに登録される。繰り返すと、nftablesモードではなない、従来モードのiptablesがほしいときはlegacyを使う。

同じコマンドが2つあるので、誤解して時間を浪費しないように。

  • iptablesのみで書く(legacy)
  • iptables(nftables)でnftablesを使う。
  • iptablesの記述をnftablesに書き換える。

iptablesからnftablesには、この3つの選択肢があり、組み合わせると6通りに移行状態が存在することになる。

コマンド iptablees-legacy iptables(nf_tables) nft
none
pure
mixed
mixed
mixed
mixed
mixed
pure

3種類(nft/iptable(nf)/iptables-legacy)のルールを同時に混ぜるとカオスだろうし、多分動かかない。

nft に全面移行するか、iptables-legacyで頑張る(nftを使わない)、nftのみ(iptables/nft+nftables)にする、この3つが現実的なんだろうね。

私は、ルールが30本程度だったので、全部をnft に書き換えた。

iptables-legacyは書き換え方法が見つからないときの最終手段なのかもしれない。

iptables-translate

iptables-translate を使うと、ntablesに変換できる。ただし、テーブル名とチェインは選べない。iptables(nf_tables)で追加したときとそのまま同じ暗黙的テーブル名が使われる。

translateの例

# iptables-translate -I FORWARD -i wg0  -j REJECT
nft insert rule ip filter FORWARD iifname "wg0" counter reject

コマンドiptablesは、nft_tablesを利用している。

# iptables --version
iptables v1.8.7 (nf_tables)

translate では iptablesコマンドでnftablesに登録されるルールが出力される

iptablesコマンドの実行

iptables-I FORWARD -i wg0  -j REJECT

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

# nft list table ip filter
table ip filter {
        chain FORWARD {
                type filter hook forward priority filter; policy accept;
                iifname "wg0" counter packets 16 bytes 8478 # xt_REJECT
        }
}

これは、transate で出力されるものと一致する(テーブル名・チェイン名)

# iptables-translate -I FORWARD -i wg0  -j REJECT
nft insert rule ip filter FORWARD iifname "wg0" counter reject

つまり、translate は iptables互換モードでnftablesに作成されるべきテーブルをコマンド形式で出力している、と考えられる。

iptables-translateの結果をそのまま使うと、iptablesコマンドを実行するのと同じ結果になる。ルールは何ら変わりがない。translate変換結果を参考に、自分で書く。そのために使うようにしておく。つまり、translateの結果を見ながら必要に応じてテーブル名・チェインを追加する。

検索キーワード nft

nftで検索すると、ゴミしか出ないので注意する。必ず netables nftで検索する。

NFTだと一攫千金を夢見たゴミ記事ばかりでちょっと困るよね。

紛らわしい単語

nftables だめなところ

set セット

いくつもある。とてもめんどくさい。

  • ipset 後継の 配列としての SET
  • パケットを更新するための set
  • mark をつけるための mark set(ct)

ct 系には、他にも label set event setとか無数にある。

紛らわしい statement と expression

nft は ルールを次のように書く

tcp dport 443 mark set 0x3

これは、次のようになっていて

<expression> <statement>
<式> <処理>

また、式と処理は並べて書くことができる。

<式>,<式>,<式>...  <処理>,<処理><処理>...

この並べて書くのが曲者なので、初見プレイでは迷路にハマることになる。

tcp dport 443 mark set 0x3 counter # マークを付けてカウントする
tcp dport 443 mark 0x3 counter # マークがあるものをカウント

nft はこのように、「条件式」と「実行処理内容」の記述に明確な区切りがないので、慌てていると勘違いを生み出しやすい。(処理と条件の間に do とかキーワードを入れればいいけど、入れられないので、実験中はcounter をよく入れる。)

counter を区切りに使う例。

tcp dport 443 counter mark set 0x3 # マークを付けてカウントする
tcp dport 443 mark 0x3 counter # マークがあるものをカウント

また、ルール実験しているとパケットがちゃんとマッチしているか見失いがちなので、counter が反応しているかを通してルールが効いているチェックになる。

2023/09/06 追記 ルールの削除について

nftables で追加・削除をスクリプト化するとき、コメントをつかうと便利。

将来的にはAddと同じ方法でrule の削除ができるらしいが、今はできない。

nft insert  rule inet fw4 forward iifname "br-lan" ip daddr 1.1.1.1 accept 
## これは出来ない
nft delete rule inet fw4 forward iifname "br-lan" ip daddr 1.1.1.1 accept 

今はできないので、代わりにどうするか。

削除するルールを入れたチェインまるごと消す。

nft insert  rule mychain myaccept iifname "br-lan" ip daddr 1.1.1.1 accept 
## チェインごと消す
nft delete chain inet mychain myaccept 

削除するルールを入れたチェインを入れたテーブルをまるごと消す。

nft insert  rule mychain myaccept iifname "br-lan" ip daddr 1.1.1.1 accept 
テーブルを消す
nft delete table mychain 

それでも駄目なときもある。既存のテーブルの既存のチェインにいれたAcceptを消したいときとか。

その場合は、ハンドルIDを使って消す。

nft delete rule inet fw4 forward handle $HANDLE_ID

しかし、ハンドルIDを探すのが煩雑である。

そこで、コメントを使ってユニーク・キーを入れておく。

UNIQUE_COMMENT='takuya:vpn accept'
nft insert rule inet fw4 forward iifname "br-lan" ip daddr 1.1.1.1 accept comment "\"{$UNIQUE_COMMENT}\""
## HANDLE IDを探して削除
HANDLE=$(nft -a list table inet fw4 | grep "{$UNIQUE_COMMENT}" | grep -oP '(?<=handle )+\d+')
nft delete rule inet fw4 forward handle $HANDLE

コメントを使ってユニークIDとして識別してしまえば、HADLEを探すのも簡単である。

参考にした資料

Linuxにおける新たなパケットフィルタリングツール「nftables」入門 Matcher Statements manpage/ubuntu Quick Reference Transparent proxy support Map Example コンパイル・オプション・モジュール

nftablesに変わった後、iptablesで- j ACCEPTが動かない。

nftablesに変わったらiptablesが動かなくなった。

nftablesに変わってもiptablesはそのまま動作する。と思ってたのですが。iptablesだけで書くとそのまま動作するが、nftablesとiptablesが混ざるとACCEPTが動かない。混ぜるな!と思うかもしれませんが、ディストリビューションがnftablesで書いてくると、私達がiptablesで書いたルールとバッティングするのです。

バッティングする例

ディストリビューションのルールを強引に上書きする。(INSERT)

iptables -I FORWARD -i eth0 -o eth1 -j ACCEPT

とりあえず動くだろうと、ルールを先頭へINSERTして強引に上書きしていた。

上記のように、先頭に追加していた場合に誤動作が起きる。

もともとは、デフォルト FORWARD がDROPなので、それより先処理をしようと、先頭にINSERTしていた。

しかし、nftablesでポリシーを書かれると、上書きができない。

accept したら打ち止めにする。->非常にめんどくさい

nftableの書式を考えると。次にようになっているTableA,Bがあるとき

table ip tableA {
  chain chainA1 {
    type filter hook forward priority filter + 1; policy drop;
  }
}
table ip tableB {
  chain chainB1 {
    type filter hook forward priority filter + 2; policy drop;
    oifname "eth1" accept
  }
}

tableBのほうが優先度が高いので、tableBでAcceptする。そのあと優先度の低い、TableAが評価されてDropされる。

なので、Dropがあるとめんどくさい。

iptablesのときは iptables -I で強引に上書きするようなルールがかけたのですが、nftablesで管理されている場合、nftablesでDROPされていると、iptables で上書きするようなルールを突っ込んでもnftablesを上書きできず2つのテーブルが順番に評価され iptables 側でAcceptしてもその後のnftablesでDROPされる。(nftablesにDROPがある場合、nftables->ipablesの順の場合は無条件にDROPされる。)これは非常にめんどくさい。

対応策:iptablesでは無理。

nftablesでdrop を書かれてしまうと、iptablesで上書きすることは上記の理由で非常にめんどくさい。

nftablesでコツコツと先頭に挿入するしか無い。

いまあるforwardチェーンを調べる。

nft list chain inet fw4  forward

たとえば、openwrtのnftableだと次のようにforwardが定義されていた。

table inet fw4 {
        chain forward {
                type filter hook forward priority filter; policy drop;
                ct state established,related accept comment "!fw4: Allow forwarded established and related flows"
                ct state invalid drop comment "!fw4: Drop flows with invalid conntrack state"
                iifname { "tun4", "ovpn2", "br-lan" } jump forward_lan comment "!fw4: Handle lan IPv4/IPv6 forward traffic"
                iifname "pppoe-mopera" jump forward_wan comment "!fw4: Handle wan IPv4/IPv6 forward traffic"
                jump handle_reject
        }
}

このforwardに追加する

nft insert rule inet fw4 forward oifname eth1 counter accept comment '"takuya: accept eth1"'

このように、nftで追加しないとiptablesでは既存のDROPを上書きできない。

これは、iptablesは固定の1つのFORWARDテーブル、nftablesでは複数のテーブルを優先度に従ってすべて処理し、DROPはそこで打ち止めに、ACCEPTは次のテーブル・ルールに処理を渡し継続するためだとおもう。iptablesでACCEPTしても次のnftでDROPされてしまう。困ったものだ。

どのパスにマウントしているか調べる findmnt

mount コマンドの結果がわかりにくい

lxd や snap にtmpfsがあって、物理的なブロックデバイスのマウント先がどこなのか調べるのが面倒。

findmnt コマンド

findmnt コマンドで調べると便利

fstabに記載されたもの一覧

findmnt -s

結果をJSONで取得

findmnt --json

このPATHはマウントされているのか

たとえば、/tmp がtmpfs などにマウントされているのか

findmnt /tmp
TARGET SOURCE FSTYPE OPTIONS
/tmp   tmpfs  tmpfs  rw,noatime,size=15728640k,inode64

このデバイスはマウントされているのか

バイスがマウントされているのか?

findmnt /dev/sda
findmnt /dev/sda1
findmnt /dev/mapper/vg.main-root
findmnt tmpfs

ext4 でマウントされている一覧

findmnt -t ext4

引数なしなら依存関係が見える。

便利かも。

mount -a コマンドでマウントされているものをしらべてgrep でフィルタするより楽かもしれない。

openwrtのLuci(WEB)でv6のNATを作る。

Zoneの設定で、v6 Masqueradeを有効にする

この設定は openwrtの最新版で登場した。

私の設定

私は、v4とv6を混ぜて管理せずに分けて管理することにした。

LAN6というゾーンを作った。

LANのv6アドレス(ユニークローカル )を割り当てて、v6専用のゾーンを作成

このゾーンではv6飲みを扱うようにした

WAN6のゾーンは最初からあるデバイスをラッピング

wan6というインターフェイスは、wrtに最初から準備されているので、これをWAN6として専用のゾーンを作った。名前がかぶるとややこしいのでNGNとした。

あわせて、ゾーン間の転送を許可した。

v6アドレスに限定したゾーンにした。

設定ができた。

NATマスカレードを有効にした。

WAN6でNATマスカレードを有効にし、

v6アドレスに対してもマスカレードを有効にした。

ルーティングを追加した

NATする先のルーティングを追加した。

ネットの疎通を確認した。

これで、v6 v6のマスカレードをWEB画面から作ることができた。

他に試したこと

ip6tables-zz-legacy というopkgを追加して、ip6tablesの互換モードで動かそうとしたけど、nftablesに変わった最新版ではv6のマスカレードはうまく動かなかった。

opkg でインストール

もしかしたら、次のパッケージが必要かもしれない。

opkg install kmod-ipt-nat6

IPv6間のMasquerade(NAT)nftablesでLinux v6 GWを作る方法。

ipv6 NATをnftables

v4 の masquerade は次のようになっていた。

nft を使ってv4 を作る単純な例は次のようになる。

# テーブル追加
nft add table my_nat44
# テーブルにフィルタチェーンを追加
nft add chain my_nat44 postrouting { type nat hook postrouting priority 100 \; }
# masquerade する
nft add rule my_nat44 postrouting oifname "eth1" masquerade

これは省略形が含まれる。familyを省略せずに書くと、次のようになっている。

nft add table ip my_nat44
nft add chain ip my_nat44 postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip my_nat44 postrouting oifname "eth1" masquerade

消す場合は

nft delete table ip my_nat44

ip は IPv4 のことで、IPv6 の場合は ip6と書くので ip -> ip6 に変える

nft add table ip6 my_nat66
nft add chain ip6 my_nat66 postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip6 my_nat66 postrouting oifname "eth1" masquerade

nft の一般例で「ipは省略される」を思い出すのが大事。ipはIPv4を表し、IPv6はip6と記述する。これを思い出しておく、neftablesを扱うときは常に省略に注意を払う。

実際にやってみる。

次のような構成を作る。

構成を作る(LXD)

router としてのマシン作る

lxc launch ubuntu: router01

lan 側のnicn をrouter に挿し込む

lxc config device add router01 mytap0 nic nictype=macvlan parent=eth0
lxc exec router01 dhclient -v eth1

クライアントを作成する

lxc launch ubuntu: client01

2台分をアップデートすると時間がもったいないので、apt-cacherを使う。

echo 'Acquire::HTTP::Proxy "http://apt-cacher.lan:3142";' |sudo tee  /etc/apt/apt.conf.d/01proxy
apt update 
apt upgrade -y

メイン ルーター

メインルータにアドレスを追加。

ip addr add fd11:0801::1/64 dev br-lan

router01にアドレスを追加

ip addr add fd11:0801::2/64 dev eth1
ip addr add fc33:3e1f::1/64 dev eth0

client01 にアドレスを追加

ip addr add fc33:3e1f::2/64 dev eth0

疎通チェック

ssh main-router   -- ping fd11:0801::1
lxc exec router01 -- ping fd11:0801::1
lxc exec router01 -- ping fc33:3e1f::2
lxc exec client01 -- ping fc33:3e1f::1

まだこの状態では、NATをしていないので、パケットが通らないこと粉を確認

lxc exec client01 -- ping fd11:0801::1

パケットを転送する

router01 がv6 をForwardするように構成。これで、v6パケットが転送される。

lxc exec router01 -- sysctl -w net.ipv6.conf.all.forwarding=1

eth0 から受けたパケットをeth1へ転送するようにする。

経路を変える。

デフォルトのルートを削除する。

lxc exec client01 -- ip -6 route del default

v6アドレスは、すべてrouter01 へ向けてやる

lxc exec client01 -- ip -6 route add default via fc33:3e1f::1

この状態では、メインルータにICMP6が届くが、アドレス変換が掛かってない。そのため、メインルータは戻りパケットの送信先がわからない。

lxc exec client01 -- ping fd11:0801::1

ルータはにはパケット届いているのがわかる。

root@# ssh main-router -- tcpdump -n  -i br-lan icmp6
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-lan, link-type EN10MB (Ethernet), capture size 262144 bytes
18:20:56.536041 IP6 fc33:3e1f::2 > fd11:801::1: ICMP6, echo request, seq 4, length 64

この状態で通信するためには、選択肢は2つある。1つは戻りパケット経路を入れる、もう一つはNATする。この2つである。今回は、マスカレードを調べているので、NATする。

NAT・Masqueradeする。

router01 でマスカレードを構成すれば、通信できるはず。

lxc shell router01

nft / nftables をする。マスカレードする。

nft add table ip6 my_nat66
nft add chain ip6 my_nat66 postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip6 my_nat66 postrouting oifname "eth1" masquerade

疎通を確認する

lxc exec client -- ping fd11:0801::1

ちゃんとパケットの応答が出てくる。

PING fd11:0801::1(fd11:801::1) 56 data bytes
64 bytes from fd11:801::1: icmp_seq=1 ttl=63 time=1.71 ms
64 bytes from fd11:801::1: icmp_seq=2 ttl=63 time=1.34 ms
64 bytes from fd11:801::1: icmp_seq=3 ttl=63 time=1.21 ms

念のためにtcpdump で、NATしていることを確認する

メイン・ルータで確認

root# ssh router 'tcpdump -n  -i br-lan icmp6'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on br-lan, link-type EN10MB (Ethernet), capture size 262144 bytes
18:27:27.517512 IP6 fd11:801::2 > fd11:801::1: ICMP6, echo request, seq 1, length 64

中間にいる router01で確認

lxc exec router01 -- tcpdump -n -i eth1 icmp6
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
09:28:53.183917 IP6 fd11:801::2 > fd11:801::1: ICMP6, echo request, id 62941, seq 1, length 64
09:28:53.184916 IP6 fd11:801::1 > fd11:801::2: ICMP6, echo reply, id 62941, seq 1, length 64

ちゃんと、NATしていることがわかる。

結論

nftablesでも3行あれば、IPv6 でもNAT変換して 'Unique Local Unicast' をグローバルユニキャストに変換してネットに出ていけることがわかる。

ポエム。nat66 による匿名性とセキュリティ

NATがないと、匿名性の確保が困難になる。また別の経路での通信(VPN)が困難である。すべてのIPv6端末にファイアウォール設定が必要になる。

ユーザ追跡が容易に可能になる。そのため、プライバシー面で問題だと思う。IPv6の使い捨てアドレスを追加で割り当てたとしてもプレフィックスは変わらない。なので、使い捨てアドレスがあってもNATしても、ユーザー追跡性は同じようなものである。NATを否定する理由にならない。

グローバルv6アドレスでグローバル通信を許可すると、機器はNAT制御の中にない。自宅のIP通信機器へ、世界中からアクセスすることが可能になる。このときに、保護はどうしたら良いのでしょうか。ネットに繋がる端末があふれる自宅になりつつあります。Google HomeやNestのような据え置き端末、食洗機や照明と言ったIoT機器がそれぞれが「機器自体でv6フィルタ機能を持つ」というセキュリティ機能が要求される。

しかし通信機器がフィルタ機能を持っていない。サポート面でも貧弱だ。さらに怖いことに、通信機器(IoT家電)のファイアウォール脆弱性を抱えていた場合に非常に面倒になる。アップデートが提供され続ける機器ならいいが、アップデートを怠るメーカやユーザーにより危険性が増す。脆弱性を突かれ、IoT機器は外部から操作されてしまう。そうならないよう、外部からの通信を遮断し、内部から外部への通信を許可し応答パケットだけを許可する必要がある。この設定を機器利用の全家庭に仕込まなくてはいけない。可能だろうか。

家庭用ルータでは機器から外部への通信(と応答パケット)を許可する設定を入れているとしても、その設定がルーターに入っているかどうか。安価なルータの説明書に書いてあるのでしょうか。アップデートは提供されるのでしょうか。そしてそれは一般人にわかるように説明書に記載ができるのでしょうか。

「内と外」という境界型のセキュリティは結局のところ必要になるし、境界型というのは「人間の肌感覚」にマッチしているので、SNATを使えば「アドレスが長くなっただけ」と認識できる。既存の知識の人たちには理解しやすいと思うのです。すべての機器にグローバルアドレスを割り当てる意味、懐疑的に見ています。一般市民にはGWもNATも無いグローバル接続は難しいのではないか。そう考えるとNAT-6-6でGWを作るのは悪くない選択肢だと思うのです。