それマグで!

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

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

Windowsで溜まり続けるキャッシュを消す。

Windows はアプリごとにキャッシュを持っていて、Tempなどにまとめて管理する文化がない。

まぁLinuxとかRuby Bundler なんかも同じなんですけど。

せめて、Windowsのディスクのクリーンアップが「$APPDATA/**/Cache」フォルダの中身は容赦なく消すぞ。みたいな仕様にしてくれたらなぁっと思う。

キャッシュ関連を削除

rm -Recurse -Force -Confirm:$false C:\Users\takuya\AppData\Local\NuGet\Cache\* 
rm -Recurse -Force -Confirm:$false C:\Users\takuya\scoop\cache\*
rm -Recurse -Force -Confirm:$false 'C:\ProgramData\Package Cache\*'
rm -Recurse -Force -Confirm:$false C:\ProgramData\chocolatey\logs\chocolatey.*.log
rm -Recurse -Force -Confirm:$false C:\ProgramData\chocolatey\logs\choco.summary.*.log
rm -Recurse -Force -Confirm:$false C:\ProgramData\ChocolateyHttpCache/*
rm -Recurse -Force -Confirm:$false C:\ProgramData\Emurasoft\EmEditor\updates\*
rm -Recurse -Force -Confirm:$false 'C:\ProgramData\Apple Computer\Installer Cache\*'


## ゴミ箱を空に
echo y | powershell Clear-RecycleBin

他にも、キャッシュを持ってるフォルダは見つかり次第に追加していこうと思う

タスクスケジューラに登録する。

こんな感じで、コマンドを登録して、定期的に実行することにした。

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2024-09-11T14:42:35.0215253</Date>
    <Author>takuya</Author>
    <URI>\takuya\Cleanup Caches</URI>
  </RegistrationInfo>
  <Triggers />
  <Principals />
  <Settings />
  <Actions Context="Author">
    <Exec>
      <Command>powershell.exe</Command>
      <Arguments>-WindowStyle Hidden  -ExecutionPolicy Bypass -File ""C:\Users\takuya\misc-app\maintenance\remove-cache.ps1"</Arguments>
    </Exec>
  </Actions>
</Task>

キャッシュは溜めないでほしい

キャッシュがSSD居座ることで、SSDの容量に居座る。そのためログとキャッシュある箇所は二度と書き込みされないのに、ずっとデータが有る。SSDはキャッシュの箇所など認識しないので、データ有りとなる。SSDは空き容量をやりくりながら書き込みを平準化する。SSDを容量ギリギリ迄使っていると、結果としていつも同じ箇所に書き込みが続いて、寿命を縮めることになる。

アプリつくる人はキャッシュ消せよ!1年以上も残してるんじゃないよ。っていうかボリューム・シャドウコピーがあるんだから、ボリューム・シャドウコピーを前提として上書きしてほしい。ボリューム・シャドウコピーならWindowsが管理してくれるんだから。

起動用のキャッシュならともかく、アップデート用のキャッシュは複数溜まってたりするので絶対に消してほしい。

WindowsでのiPhoneバックアップの場所

windowsiTunesに取得したバックアップの場所

Windows内部に作ったバックアップの場所。

C:\Users\takuya\AppData\Roaming\Apple Computer\MobileSync\Backup

バックアップの削除はここから行う。

うっかり、AppData内部のデータを消さないように

ローカルのバックアップって意味あるの?

「あります。」

  • iCloudバックアップは容量不足になりがちで、Appleに1ドル吸われる。
  • iCloudバックアップは全データを取り出さない(指定データのみ)
  • iCloudバックアップから取り出しは遅い。
  • iCloudバックアップでは一部アプリのセキュア・データが復元されない

あらゆるデータをバックアップ取るのであれば、ローカルが一番ですね。

機種間でのデータ移行も確実に行えます。

いくら高速インターネットだの、高速WiFiだといっても、iCloudからデータ取り出しで20MB/sくらいしか速度が出ないんですから。ローカルに持っておくのがストレス無いですね。

lxc の root ストレージを作り直す。( Failed getting root disk: No root device could be found)

LXC のstorage を作り直す

LXCを整理するのには、ストレージを消して作り直すと手っ取り早い

ストレージをすべて消して空っぽにした状態

takuya@:~$ lxc storage ls
+------+--------+--------+-------------+---------+-------+
| NAME | DRIVER | SOURCE | DESCRIPTION | USED BY | STATE |
+------+--------+--------+-------------+---------+-------+

lxc のストレージを消すには、used by で利用しているlxd インスタンスlxc delete $NAMEで消す必要もある。

lxc コマンドは、次のようにすると、sudo なし一般ユーザも利用可能。

sudo usermod -aG lxd takuya

ストレージを再生成する。

lxc storage create  default btrfs

root (イメージ保存先)を割り当てる。

path=/ を割り当てる。

lxc profile device add default root disk path=/ pool=default

この作業を忘れると、次のように root device (stoage ) がないと怒られる。

Failed getting root disk: No root device could be found

btrfs の圧縮オプションを入れておく。

btrfs は圧縮オプションを入れておくといいかもしれない。(おまじない)

lxc storage set default btrfs.mount_options compress=zstd

zstd 圧縮が効いていることを確認

lxc storage get default btrfs.mount_options

イメージをlaunchして確認

LXC_NAME=test-ubuntu
RELEASE=22.04
lxc launch ubuntu:$RELEASE $LXC_NAME

btrfs の圧縮オプションが効いていることも確認

lxc exec $LXC_NAME 'mount' | grep btrfs | grep zstd

/var/snap/lxd/common/lxd/disks/default.img 
on / type btrfs (rw,relatime,idmapped,compress=zstd:3,
ssd,space_cache=v2,user_subvol_rm_allowed,
subvolid=257,subvol=/containers/test)

btrfs なんで subvol=/containers/test でLXC_NAMEが入っていて、compress=zstd:3, になって圧縮オプションも継続していることが確認できる。

まとめ

lxc のストレージの作り直し。

lxc storage delete $NAME
lxc storage create $NAME
lxc profile device add default root disk path=/ pool=$NAME

lxd init をしてもいいけど、ストレージだけ作り直せる。使えると便利。

dockerfileでAPTプロキシとシェル変数を渡す例。

docker build で apt installを高速化

docker build で毎回ダウンロードが公式レポジトリから行われるのが、心苦しい。

無駄な通信が発生している。そして何より遅い

ビルド例

export APT_HTTP_PROXY=http://192.168.2.21:3142 
docker build --build-arg APT_HTTP_PROXY=${APT_HTTP_PROXY} -t sample .

Dockerfileの例

dockerfile で次のように記述すればいい。

FROM ubuntu:20.04
ENV DEBCONF_NOWARNINGS=yes
ARG APT_HTTP_PROXY
ENV APT_HTTP_PROXY=${APT_HTTP_PROXY}
RUN echo "Apt Config" && \
echo "APT::Install-Suggests 0;\nAPT::Install-Recommends 0;"  \
  |   tee /etc/apt/apt.conf.d/00-no-install-recommends && \
echo "path-exclude=/usr/share/locale/*\npath-exclude=/usr/share/man/*\npath-exclude=/usr/share/doc/*\n" \
    | tee  /etc/dpkg/dpkg.cfg.d/01-nodoc && \
echo "Acquire::HTTP::Proxy \"${APT_HTTP_PROXY}\";" >> /etc/apt/apt.conf.d/01proxy && \
echo 'Acquire::HTTPS::Proxy "false";' >> /etc/apt/apt.conf.d/01proxy && \
apt-get update && \
apt-get upgrade -y

とくにポイントになるのは、次の二行

echo "Acquire::HTTP::Proxy \"${APT_HTTP_PROXY}\";" >> /etc/apt/apt.conf.d/01proxy && \
echo 'Acquire::HTTPS::Proxy "false";' >> /etc/apt/apt.conf.d/01proxy && \

また、プロキシ設定をビルド時変数で渡す箇所を利用して、再利用性を高める。

ARG APT_HTTP_PROXY
ENV APT_HTTP_PROXY=${APT_HTTP_PROXY}

dockerfile のコツ

docker build を実行するシェルから変数を渡すのに、--build-arg が使える。

Dockerfileでは、ビルド変数を受け取るARG $NAMEがある。 ARG $NAME で受け取った変数をビルド用変数としてENV NAME=$NAMEで展開する。 RUN cmdENV 変数を使う。

このように書いておけば、Dockerfile にビルド時の変数を使える。

そして、ビルド時に変数を渡す

docker build --build-arg APT_HTTP_PROXY=${APT_HTTP_PROXY}

APTプロキシを使うコツ

プロキシ自体はdocker run sameersbn/apt-cacher-ng で起動できる。

https:// のレポジトリは、httpを強制すれば簡単にプロキシが可能

sed -e 's|https:|http:|' -i /etc/apt/sources.list.d/docker.list

apt update は http でプロキシつながるのは次のような動作背景にある。

  • apt update で、http://apt.example.tld/ へ プロキシ経由でアクセスする。
    • プロキシはhttpで取得を試行する。
    • オリジンサーバーがhttp->https へアップグレードをかける。
    • プロキシがhttps でコンテンツを取得
    • プロキシは apt 結果として httpsの取得結果を返す
  • apt update の結果が取得される。

過去の資料

過去の関連資料です。

apt インストールは「何度も実行する」ために存在するわけではない。通信はプロキシでキャッシュするしか無い。

 またaptのRUNより手前を書換えるとdockerfileのキャッシュがうまく効かないんですよね。とくにRUNを1回で済ませようとすると、毎回aptが実行されて、数秒の待ち時間が必要。その待ち時間が10秒でも回数が30回を超えてくると流石に面倒なです。

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されて届かないということが頻発する。

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

Gitlab をAPIで使ってレポジトリ(プロジェクト)を管理。レポジトリのコピーと移動

Gitlab を整理したんですが。

レポジトリの取捨選択と移動が手間なので、APIでコマンドから行いました。

レポジトリを整理し選抜して新サーバーに移動するかどうか決めるときに、100件くらいと削除を繰り返すのが面倒だったので、インポートを使わずに必要なレポジトリだけをclone して push することにした。

Gitlab のAPIで解決させる

APIはパーソナルアクセストークンを使って、プロジェクトの一覧、グループの一覧、グループのプロジェクトの一覧から、プロジェクトのレポジトリを一覧したり削除したり、新規作成できる。

APIを使って面倒な削除処理とClone処理を少しでも改善することにする。

GitLabのAPIを使う。

APIを使えば、アクセス可能なプロジェクトの名前やURLを取得し、git clone や git push を連続的に行うことができる。

APIキーの作成

パーソナルトークンを発行する。

ここで発行したアクセス・トークンを使う。

APIキーのテスト(自分のユーザー情報を取得)

自分のユーザー情報

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
## 確認する。
curl -s -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/user"  | jq  -r .id
## 保存する。
USERID=$(curl -s -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/user"  | jq  -r .id)

このように、TOKENを使えば簡単にアクセスできることがわかる。TOKENの確認に自分のユーザー情報取得し、動作チェックすとする。

プロジェクト一覧を取得する

自分がアクセス可能なプロジェクト一覧を取得する。

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
curl --header "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/projects"

自分のプロジェクト一覧

自分のパーソナルなプロジェクト(レポジトリ)を一覧する。

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
USERID=$(curl -s -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/user"  | jq  -r .id)
curl -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/users/$USERID/projects"  | jq .

一覧系は、all_available をつけないと、一部が欠損する。

グループ一覧

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
curl --header "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/groups?all_available=yes&top_level_only=yes"  

一覧系は、all_available をつけないと、一部が欠損する。

グループ内のプロジェクト一覧

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
curl --header "PRIVATE-TOKEN: $MY_TOKEN" \
  "https://$HOST/api/v4/groups/154/projects"

bashやjs で使いやすく可能する

プロジェクトを見やすく、整形して加工する。配列などにしておけば、シェルコマンドから使いやすい。

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
USERID=$(curl -s -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/user"  | jq  -r .id)

curl -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/users/$USERID/projects" > p.json
cat p.json | jq '[ .[] | { id:.id, name:.name, desc: .description , path: .path, ssh: .ssh_url_to_repo } ]'
cat p.json | jq .[] | jq ' "\(.name) \(.ssh_url_to_repo)" ' -r
cat p.json | jq .[] | jq ' "\"\(.path)\": \(.ssh_url_to_repo)" ' -r
cat p.json | jq .[] | jq ' "project=\"\(.name)\";project_path=\"\(.path)\"" ' -r

プロジェクト個別の情報を見る。

プロジェクトの設定情報や参加者などを個別に見る。

PRJID=55
curl -X GET -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/projects/$PRJID" 

プロジェクトを消す。

要らないな。と思ったプロジェクト消す。

PRJID=55
curl -X DELETE -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/projects/$PRJID" 

もっと簡単にまとめて消す

ARR='35 37 39'
for i in $ARR; do 
    echo $i
    curl -s -X DELETE -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/projects/${i}"  | jq . 
done

WEB画面から行うと確認が入ってめんどくさいので、APIから削除する。削除は即座に行われる。二度と復活できない。

プロジェクトをまとめてCloneする。

自分の個人プロジェクトのレポジトリをまとめてgit clone する。

MY_TOKEN=glpat-XXXXXXXXX-2g
HOST=git.example.com
USERID=$(curl -s -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/user"  | jq  -r .id)
curl -H "PRIVATE-TOKEN: $MY_TOKEN" "https://$HOST/api/v4/users/$USERID/projects" > p.json
cat ../p.json | jq .[] | jq '.ssh_url_to_repo' \ 
| xargs -I@ git clone @

プロジェクトを作成する。

プロジェクトの作成をする。

TOKEN2=glpat-TOKE2
HOST2=other.gitlab.example.tld
JSON='
{
  "name": "sample-webui",
  "path": "sample-webui",
  "initialize_with_readme": false
}
'
curl -X POST -H "PRIVATE-TOKEN: $TOKEN2 " \
 -H "Content-Type: application/json" \
 --data "$JSON" \
 --url "https://$HOST2/api/v4/projects/"

プロジェクトのレポジトリへまとめてPushする。

以上を踏まえて、一括してgit clone て、一括してgit push する。

ここまでの例を使って、すでに不要なプロジェクトを消したので、残るのは別サーバーに移動するものだけが残っています。

例えば、下記のようにgitlab間のレポジトリのコピーを行うことができる。

TOKEN01=glpat-TOKEN-11
HOST1=git.example.com
TOKEN02=glpat-TOKEN-22
HOST2=other.gitlab.example.tld
HOST2_SSH_PORT=23456

function list_ssh_url_to_repo(){
  USERID=$(curl -s -H "PRIVATE-TOKEN: $TOKEN01" "https://$HOST1/api/v4/user"  | jq  -r .id)
  curl -H "PRIVATE-TOKEN: $TOKEN01" "https://$HOST1/api/v4/users/$USERID/projects" \
  | jq .[] | jq ' "\"\(.path)\"  \"\(.ssh_url_to_repo)\"" ' -r
}
function create_project(){
  name=$1
    JSON="{ \"name\": \"${name}\", \"path\": \"${name}\",
      \"initialize_with_readme\": false }"
    curl -s -X POST -H "PRIVATE-TOKEN: $TOKEN02 " \
     -H "Content-Type: application/json" \
     --data "$JSON" \
     --url "https://$HOST2/api/v4/projects/"
}
function push_repository(){
  name=$1
  cd $name
  pwd
  git remote rename origin old-origin
  ## とりあえず、自分のユーザーに固定する。
  git remote add origin ssh://git@${HOST2}:$HOST2_SSH_PORT/takuya/${name}.git
  git push --set-upstream origin --all
  git push --set-upstream origin --tags
  cd ..
}
## HOST1 のレポジトリを一旦手元にコピーして
mkdir work 
cd work
for $url in $( list_ssh_url_to_repo ) ; do 
git clone $url
done
# HOST2 へ全部pushする。
for  name in $( ls -l ); do 
    create_project $name
    push_repository $name   
done

cd ..
rm -r work

もちろん、プロジェクトのTODOや作業ログを含めて移動するとなると、凝ったスクリプトにするとか、バックアップを使う必要があるけど、プロジェクトのレポジトリを移動するだけなら、APIでURLを出して、git clone して、新しいGitLabにプロジェクト作成してgit push すれば良いわけです。

gitlabやgithubを使わずに、ベアレポジトリを扱ってたら、「プロジェクト」の概念がなかったのでもっと簡単だったのですが。

レポジトリを保存するだけに、プロジェクト単位になるのはちょっとした作業になりますね。

vscodium - VSCode の自由ソフトウェア版に移行する。

vs code は MSなので将来が不安

MSはいまは、とてもフレンドリーに見えるとはいえ、過去の行動を考えると不安だ。

また、Googleが自由ソフトウェアを食い物にしている現状を考えてもVS Codeは今後に不安を覚える

VS Codium に移行する

そんなに気にすることでもないかもしれないが。

MS VSCode に利用状況といえど、蒐集されるのは、気分がいいものではない。

インストール

chco install vscodium

起動など

ロゴに著作権があるので、ロゴは変わるが、その他で特筆すべき変化はない。

何ら通常と変わらない。

しばらく使ってみることにする。

エディタのタブサイズを設定した。

PlainTextファイルはタブサイズを「別途」で指定しないと上書きできないようになってた。TAB=8はデカすぎるねん。

Windowsの右クリックメニューを占拠された

VSCodiumでもVs Code と同じようにすべてのファイルに対する右クリックメニューが作られた。こういうところは真似しなくて良いんですよ。。。

## VS Code 同様にCodiumがうるさいので消す。
New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCodium' -Name 'LegacyDisable' -PropertyType 'String' -Value ''
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Drive\shell\VSCodium'  -Destination 'HKLM:\SOFTWARE\Classes\Drive\shellex\VSCodium'

New-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\VSCodium' -Name 'LegacyDisable' -PropertyType 'String' -Value ''
Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\Directory\shell\VSCodium'  -Destination 'HKLM:\SOFTWARE\Classes\Directory\shellex\VSCodium'

Move-Item -LiteralPath 'HKLM:\SOFTWARE\Classes\*\shell\VSCodium'  -Destination 'HKLM:\SOFTWARE\Classes\*\shellex\VSCodium'

VSCodeと同じように消すことになった

.bat ファイルのように、Openを上書きできないものは、Shellメニューに追加する。

## vs code 開くに追加
## .bat
New-Item -Path 'HKCU:\SOFTWARE\Classes\batfile' -ItemType Directory 
New-Item -Path 'HKCU:\SOFTWARE\Classes\batfile\shell' -ItemType Directory 
New-Item -Path 'HKCU:\SOFTWARE\Classes\batfile\shell\VSCodium' -ItemType Directory 
New-Item -Path 'HKCU:\SOFTWARE\Classes\batfile\shell\VSCodium\command' -ItemType Directory 
New-ItemProperty -Path 'HKCU:\SOFTWARE\Classes\batfile\shell\VSCodium' -Name 'Icon' -Value "C:\Program Files\VSCodium\VSCodium.exe"
New-ItemProperty -Path 'HKCU:\SOFTWARE\Classes\batfile\shell\VSCodium\command' -Name '(Default)' -Value '"C:\Program Files\VSCodium\VSCodium.exe" "%1"'

wsl で xming(vcxsrv) 経由で mpv を起動する。

wsl からXwindowのウインドウアプリを起動するには

WSLでウインドウを使うアプリ起動するには、WSLからX Window サーバーに接続すれば良い。

https://takuya-1st.hatenablog.jp/entry/2020/11/08/135059

殆どの場合は、これで動く

mpv は動かない

mpvopenGLが必要なのでそのままだとエラーになる。

大きく分けて2つのエラーが出てくる

error: XDG_RUNTIME_DIR is invalid or not set in the environment.
X Error of failed request:  GLXUnsupportedPrivateRequest
  • XDG_RUNTIME_DIR がない
  • GLX が使えない

いかにエラーメッセージを残しておく。

[vo/gpu/x11] X11 error: BadRequest (invalid request code or no such operation)
[vo/gpu/x11] Type: 0, display: 0x7fe33c003f80, resourceid: 5a, serial: 10
[vo/gpu/x11] Error code: 1, request code: 92, minor code: 19
[vo/gpu/opengl] Suspected software renderer or indirect context.
[vo/gpu] VT_GETMODE failed: Inappropriate ioctl for device
[vo/gpu/opengl] Failed to set up VT switcher. Terminal switching will be unavailable.
[vo/gpu/opengl] Listing DRM devices with drmGetDevices failed! (No such file or directory)
[vo/gpu/opengl] Failed to find a usable DRM primary node!
[vo/gpu/opengl] Failed to create KMS.
[vo/gpu-next/x11] X11 error: BadRequest (invalid request code or no such operation)
[vo/gpu-next/x11] Type: 0, display: 0x7fe33c0d0820, resourceid: 5a, serial: 10
[vo/gpu-next/x11] Error code: 1, request code: 92, minor code: 19
[vo/gpu-next/opengl] Suspected software renderer or indirect context.
[vo/gpu-next] Can't handle VT release - signal already used
[vo/gpu-next/opengl] Failed to set up VT switcher. Terminal switching will be unavailable.
[vo/gpu-next/opengl] Listing DRM devices with drmGetDevices failed! (No such file or directory)
[vo/gpu-next/opengl] Failed to find a usable DRM primary node!
[vo/gpu-next/opengl] Failed to create KMS.
[vo/vdpau/x11] X11 error: BadRequest (invalid request code or no such operation)
[vo/vdpau/x11] Type: 0, display: 0x7fe33c0d0820, resourceid: 5a, serial: 10
[vo/vdpau/x11] Error code: 1, request code: 92, minor code: 19
Failed to open VDPAU backend libvdpau_nvidia.so: cannot open shared object file: No such file or directory
[vo/vdpau] Error when calling vdp_device_create_x11: 1
[vo/xv/x11] X11 error: BadRequest (invalid request code or no such operation)
[vo/xv/x11] Type: 0, display: 0x7fe33c0d0820, resourceid: 5a, serial: 10
[vo/xv/x11] Error code: 1, request code: 92, minor code: 19
Xlib:  extension "XVideo" missing on display "127.0.0.1:0.0".
[vo/xv] Xv not supported by this X11 version/driver
X Error of failed request:  GLXUnsupportedPrivateRequest
  Major opcode of failed request:  143 (GLX)
  Minor opcode of failed request:  17 (X_GLXVendorPrivateWithReply)
  Serial number of failed request:  124
  Current serial number in output stream:  124

対応 XDG_RUNTIME_DIR

XDG_RUNTIME_DIR が無いのであれば作れば良い。

export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$USER
mkdir -p $XDG_RUNTIME_DIR
chmod 700 $XDG_RUNTIME_DIR

対応 OpenGL (GLX) をつけて起動

VcXsrv を起動するときに、WindowsOpenGLを使うように指定しておく

"C:\Program Files\VcXsrv\vcxsrv.exe" :0 -multiwindow -clipboard -wgl

注意事項として、VcXsrv を使うこと。Xmingの場合は GL 関連のライブラリが含まれてないことがるので、Xmingのビルドとインストールを見直す必要がある。( -wglオプションがなかった )めんどうだったんだよね。VcXsrvなら動いたのでこっちを使うことにした。

次のように起動した

タスクトレイに常駐している

WScript(JScript)で起動する場合は、スペースがうざいが、次のようにする。

var ws = WScript.Createobject("WScript.Shell")
ws.Run('\"C:/Program Files/VcXsrv/vcxsrv.exe\" :0 -multiwindow -clipboard -wgl',0)

mpv 起動

以上のことを終わらせると、mpv を起動することができた。

サウンド再生

ただ、まだサウンドがでてこない。

X Windowサーバーにあれこれ設定しても多分徒労なので、PulseAudio をネットワーク越しに使うことにする。

C:/Users/takuya/AppData/Roaming/PulseAudio/bin/pulseaudio.exe

以上で、MPVをWSLのコンソールから使うことができた。

まとめ

wsl で mpv をインストールして動画再生

## wsl 側
sudo apt update && sudo apt install mpv 
export DISPLAY=localhost:0.0
export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$USER
mkdir -p $XDG_RUNTIME_DIR
chmod 700 $XDG_RUNTIME_DIR

Windows

choco install vcxsrv
"C:\Program Files\VcXsrv\vcxsrv.exe" :0 -multiwindow -clipboard -wgl

再生開始

# wsl から
mpv http://127.0.0.1/example/sample.mp4

サウンドに関しては、PulseAudioのインストールを参考に設定する。

これで、mpv をコマンドから扱えるので、mpv に関する調査をするのに、Ubuntuマシンを用意せずに済む。Windowsから行える。便利。

2024-08-09

WScript で起動する方法を追加。

mpv でキー操作を書き換える。

mpv のスキップ機能が不便なので改善する。

mpvinput.conf で キー操作を上書き(変更)できるので、設定を変更して便利に使いたい。

設定ファイルの場所と・作成

touch  ~/.config/mpv/input.conf
cat  ~/.config/mpv/input.conf

設定ファイルはGlobalでもUserでもどちらでもいい

~/.config/mpv/input.conf

UP    osd-msg seek  300 exact
DOWN  osd-msg seek -300 exact
RIGHT osd-msg seek  20 exact
LEFT  osd-msg seek -20 exact

キーと、操作内容を書いた.

osd-msg は画面に現在の再生位置を表示する。osdon screen display とかでよく使われる用語。ここの s は status の s かもれしれない。設定自体は--osd-on-seek=<no,bar,msg,msg-bar> に相当する。

再生スキップ時に何分スキップしたか見えないと不便なので、をon にしてる。

seek で 指定秒数をスキップする。書式は、seek <target> [<flags>]

この設定を応用すれば、スクショを撮ってその場でアップロードとかもできるかもしれない。

参考資料

mpv マニュアル

mpv の設定を書いて、起動オプションを省略する

mpv の設定は以下の場所に設置できる。

  • /etc/mpv/
  • /usr/local/etc/mpv/
  • ~/.config/mpv/

上記に設定ファイル(mpv.conf)を設置すると、起動時に読み込まれる。

ファイル名も固定で指定できて/etc/mpv/{mpv.conf,input.conf} のファイルを指定できる。input.conf はキー操作に関する設定。mpv.conf は起動オプションに関する設定である。ファイル名も設定で変えられるはずだが、変えないほうが無難だと思う。

設定例

たとえば、コマンドのオプションの次の場合

mpv --fullscreen 

設定ファイルは次のようになる。

touch  ~/.config/mpv/{mpv.conf,input.conf}
## 設定例
fullscreen=yes

引数で=XX と変数を与えない場合、つまり引数の有無で判断する場合は、YESを書いてあげる。

つぎに、コマンドのオプションで値を与える場合

mpv --audio-device=alsa/sysdefault

値がある場合は次のようになる。

## 設定例
audio-device=alsa/sysdefault

この場合は、設定ファイルにそのまま書けばいい

起動オプションと設定の対応

設定自体は、公式サイトに解説がある。

公式サイトの解説によれば、次のように対応してる。

Option Configuration file entry
--flag flag
-opt val opt=val
--opt=val opt=val
-opt "has spaces" opt=has spaces

設定例

設定書式は INI 形式である。

# normal top-level option
fullscreen=yes
audio-device=alsa/sysdefault

hr-seek=yes
#hr-seek-demuxer-offset=20
#hr-seek-framedrop=no

# a profile that can be enabled with --profile=big-cache
[big-cache]
cache=yes
demuxer-max-bytes=512MiB
demuxer-readahead-secs=20

[network]
profile-desc="profile for content over network"
force-window=immediate
# you can also include other profiles
profile=big-cache

[reduce-judder]
video-sync=display-resample
interpolation=yes

# using a profile again extends it
[network]
demuxer-max-back-bytes=512MiB
# reference a builtin profile
profile=fast

こんな感じに色々と設定できる。

参考資料

https://mpv.io/manual/stable/