それマグで!

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

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

nftablesのv4-v4のsnat/masqueradeとiptablesの比較

nftables

linuxiptablesはそのままのコマンドで動作するのですが。

iptablesはnftablesのサブセットのような扱いになっています。

iptablesと比較しながら、超基本的な書式を学ぶことにした。

nftables で masquerade する。

iptables では一行でかけたものが3行に分かれました。

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

え?三行?ですか。とおもったので、nftablesにチャレンジすることにした。

ただし、ゼロからの初期状態から追加するには、prerouting でnftにパケットを食わせる必要があります。(合計4行になります。)

nft add chain nat prerouting { type nat hook prerouting priority -100 \; }

最初にテーブル追加

最初にテーブルを追加します。

nft add table my_nat

iptablesのときは nat というテーブルは用意がありましたが、nftablesでは空っぽからスタートするので、自分で作ります。名前は自由に決められるが自由すぎて迷う。名前空間とか用意してくれればいいのに。

テーブルにフィルタチェーンを追加

次に、作ったテーブルにチェインを追加します。

nft add chain my_nat postrouting { type nat hook postrouting priority 100 \; }

masquerade する

チェインに入ったものをマスカレードするように書きます。

nft add rule my_nat postrouting oifname "eth1" masquerade

nftablesの特徴

テーブルにチェインを作って、rule書く

table -> chain -> rule

実際にやってみる。

クライアントがRouterを経由してマスカレードで出ていく構成を考えます。

home-lan - router  - lxdbr - client

次のような構成をLXDで作ります。

router としてのマシン作る

lxc launch ubuntu: router01

2本目のNICを挿し込む

lan 側のnic をmacvlanとして、router01 に挿し込む

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

nftables を確認する

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

2つとも、iptablesはnftables提供のものを使っていることを確認。

client01 の通信を router 経由にする

変更前の確認

root@client01:~# ip route
default via 10.17.238.1 dev eth0 proto dhcp src 10.17.238.67 metric 100
10.17.238.1 dev eth0 proto dhcp scope link src 10.17.238.67 metric 100

変更

ROUTER01_IP=10.17.238.10
CLIENT01_IP=10.17.238.202
ip route del default 
ip route add default via $ROUTER01_IP dev eth0  src $CLIENT01_IP metric 100

変更後の確認

root@client01:~# ip route show
default via 10.17.238.127 dev eth0 src 10.17.238.67 metric 100
10.17.238.0/24 dev eth0 proto kernel scope link src 10.17.238.67 metric 100
10.17.238.1 dev eth0 proto dhcp scope link src 10.17.238.67 metric 100

router01で転送を有効にする

パケット転送を有効にする。

sysctl -a | grep -F all.forward
sysctl  -w net.ipv4.conf.all.forwarding=1

準備は完了です。

以上で準備は完了です。

最初にiptables(legacy nft)を試す

iptablesはnft です。

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

nft でもiptablesコマンドはそのまま使えるので、よく知られたiptablesのコマンドでマスカレードを作ってみます。

iptables ( legacy) router01 でNAT/MASQUERADEする

LAN_DEV=eth0
LAN_NET=10.17.238.0/24
WAN_DEV=eth1
WAN_NET=0.0.0.0/0
iptables -t nat -A POSTROUTING -s $LAN_NET -d $WAN_NET -o $WAN_DEV -j MASQUERADE

iptables (legacy)として互換レイヤーで扱う。とiptablesと同じ書き方で同じ動作がするようになってるので、最初に互換レイヤーで試す。

疎通確認する

router01 でtcpdumo を見ながら、client01 からping を送信してみます。 ping が応答してきます。

root@client01:~# ping 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=8.67 ms
64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=8.51 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=8.70 ms

NATで正しく通信できることがわかります。

iptablesを消す

疎通が確認できたので、iptablesのテーブルをflush して何もない状態に戻します。

iptables -t nat -F
iptables -F

iptables をリセットして 初期状態に戻した。

nftables で masquerade する。

今度は、nft コマンド(nftables)を使ってマスカレードを試みる。

## テーブル追加
nft add table nat
## テーブルにフィルタチェーンを追加
nft add chain nat postrouting { type nat hook postrouting priority 100 \; }

masquerade する

nft add rule nat postrouting oifname "eth1" masquerade

iptablesとnftablesは同じだけど、iptablesは初期テーブルを持ってるが、nftablesはすべてを自分で書く必要がある。

初期テーブルnatくらい持っておけよとおもうが、natテーブルが大量になったり依存が増えて管理が大変になるだろうことを思うと、ルールセットごとに個別に指定するためには名前で分けるほうが、依存関係をなくせてスッキリするんだろうな。でも、単純なルールで使うカジュアル用途だと不便な気もする。

疎通確認

root@client01:~# ping 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=8.72 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=9.42 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=55 time=9.03 ms

iptablesの状態を確認

nftが有効な状態でiptablesの状態を確認すると・・・

root@router01:~# iptables -L -t nat
free(): double free detected in tcache 2
Aborted (core dumped)

コアダンプになった・・・ nat いう名前だとcore dump になった。なぜだ。

別の名前にすると動く、どうやらnatというテーブル名を使うべきではないようだ。

nft テーブルを確認

全ルールを見てみる。

nft list ruleset | less

全テーブルを見る。

nft list tables

テーブルを指定して中身を見る

nft list table nat

テーブル名を指定するとき、自分で決めた名前を使う。

nft テーブルを削除

作ったテーブルを削除してもとに戻します。

nft delete table nat

テーブルに名前をつける。

先程のnat作成は、テーブル名がnat なので、具体的な名前をつける。

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

ip はアドレスファミリipv4を示す。省略可能

削除のときも名前を使う。

nft delete table ip my_nat

名前をnat 以外にすると、コアダンプもなくなる。

root@router01:~# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
(略
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
root@router01:~# iptables -L -t nat
Chain PREROUTING (policy ACCEPT)
(略

nftables で作った my_nat テーブルは、iptablesから見えない。期待通りの動作ですね。

root@router01:~# iptables -L -t my_nat
iptables v1.8.7 (nf_tables): table 'my_nat' does not exist
Perhaps iptables or your kernel needs to be upgraded.

natという名前以外を使うとコアダンプしなくなったので、名前には注意したほうがいいかもしれない。

名前にドット( . ) が使えるようなので、簡易的な名前空間っぽいものが作れるかもしれない。大文字小文字は区別しない(はず)なのでいい感じに記号を使う必要がある。

SNATする。

SNAT も iptables で旧来スタイルは、次のようになる。

## 外向きのIPでSNATする
ROUTER01_IP=192.168.2.183
iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to-source $ROUTER01_IP
## 削除する
iptables -t nat -D POSTROUTING -o eth1 -j SNAT --to-source $ROUTER01_IP

同じことを nftables で書くには、次のようになる。

ROUTER01_IP=192.168.2.183
nft add table ip my_snat
nft add chain ip my_snat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip my_snat postrouting oifname "eth1" snat to $ROUTER01_IP

削除する。 ipipv4の意味で、省略可能。

nft delete table ip my_snat

iptables-translate

iptables-translate をつかうと、iptablesの書式をnftablesに変換できる。

最初に書いた iptables の書き方をtranslate してみる。

LAN_DEV=eth0
LAN_NET=10.17.238.0/24
WAN_DEV=eth1
WAN_NET=192.168.2.0/24

iptables-translate -t nat -A POSTROUTING -s $LAN_NET -d $WAN_NET -o $WAN_DEV -j MASQUERADE
nft add rule ip nat POSTROUTING oifname "eth1" ip saddr 10.17.238.0/24 counter masquerade

変換できるが、そのままコピペでは動かない。上記をよく見ると、iptables-d $WANが欠損している。tableはnatであるが、ruleは大文字POSTROUTINGである。chainが無いので動かない。nftablesに必要な、table->rule->chain の包含関係が無い。上記のpostrouting を動かすためには、prerouting でpostrouting にjumpしなくちゃいけない。そのためiptablesルールをnftablesでゼロから書くと3行必要なのだが、translateでは1行になっている。なので、このままで動かない。

このように、nftを少しでも知らないと変換結果を判断できない。またテーブル名称がnatになってしまうのでnft の良さを活かせないと思う。 「iptablesのあれはnftablesではどう書くんだろう。」ってときにサンプルが出てくる。translateの結果は参考程度にとどめたほうがい無難なのではないか。

同じことをnftablesで書くと次のようになる。

WAN_NET=eth1
nft add table nat
nft add chain nat postrouting { type nat hook postrouting priority 100\; }
nft add rule nat postrouting ip saddr $LAN_NET  ip daddr $WAN_NET oifname eth1 masquerade
## 削除
nft delete table ip nat

nat は自分で決める名前、oifoifnameの略、

iptables-translate は、「ルール」を変換するのであって、ルールの入れ物である、チェインやテーブルは自分で作る必要がある。

-s -d のようにSRC・DSTのアドレスを指定する場合

次のように、SRC・DSTのアドレスを指定する場合はよくある。

iptables -t nat -A POSTROUTING -s 1.1.1.1 -d 192.168.2.1 -o eth1 -j MASQUERADE

これをtranslate すると次のようになる。

$ iptables-translate -t nat -A POSTROUTING -s 1.1.1.1 -d 192.168.2.1 -o eth1 -j MASQUERADE

translate 結果

nft add rule ip nat POSTROUTING oifname "eth1" ip saddr 1.1.1.1 ip daddr 192.168.2.1 counter masquerade

先程までのテーブルとルールの話を踏まえると、translateの結果をそのまま使えない。POSTROUTINGが大文字なのも慣習的すぎて理解しにくい。counter も初見ですよね。

nftables で記述するとtanslate の結果を次のように書き換えれば、同じように動くはずである。

WAN_DEV=eth1
LAN_IP=10.17.238.0/24
WAN_IP=1.1.1.1

nft add table my_nat
nft add chain my_nat postrouting { type nat hook postrouting priority 100\; }
nft add rule my_nat postrouting oif $WAN_DEV ip saddr $LAN_IP ip daddr $WAN_IP masquerade

oifnameoif と略称にできる。oifnameoutput interface name の略だと思われる。 ipIPv4 のことでip は省略可能。ただし、ip daddrip saddrにつくip 省略不可である。

このように、translate した結果は、そのままコピペしてもえらーになり、参考程度にするのがいいかと思っ割れる。

戻りパケットを識別するとき、次のように書く

nft add rule filter input ip daddr 192.168.2.1 ct state established,related accept

戻りパケットを受け入れるように設定する。

上記の例は、次のような構造になっていた。

ADDR=192.168.2.1
MATCH=ct state established,related 
ACTION=accept
nft add rule filter input ip daddr $ADDR $MATCH $ACTION

ACTIONの決め方 ACCEPT/DENY/REJECT

iptablesには、デフォルト・ポリシーがあったが、nftablesにはデフォルトが無い。iptablesでデフォルトDENYで、必要なもの通すといった書き方はなくなってた。

なので、ルールを順番に追加して最終チェックでDENYをアクション登録する

全部拒否して22(ssh)だけを許可する。

input の 22 をACCEPTする(デフォルトポリシーがACCEPT)

iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -j DROP

input の 22 をACCEPTする(デフォルトポリシーがDROP

iptables -A INPUT -p tcp --dport 22 -j ACCEPT

nftablesにはデフォルト・ポリシーはACCEPTなので、次のように書く。

nft add table filter
nft add chain filter input { type filter hook input priority 0 \; }
nft add rule filter input tcp dport 22 accept
nft add rule filter input drop

デフォルト・ポリシーは、チェインごとに存在するのでベースチェインに書く。

icmp を許可する

nft 書いてて躓いたのがICMPだった。

v4 icmp は echo-request を明示しないとだめだった。

nft add table filter
nft add chain filter input { type filter hook input priority 0 \; }
nft insert rule filter input icmp type echo-request accept

ちなみに、上記のinsertiptables -I に相当する。INSERTで先頭に追加する。addは末尾に追加。

ルールをすべて表示して、個別に削除する

いくつかルールを足して試していて、ミスったものだけを削除したいとき。

iptablesなら IをDに変えればよかった。

iptables -I xxxx
iptables -D xxxx

nftablesの場合は、iptablesのように単純でははい。

つぎのように、filter テーブルに chain input を作った場合

nft add table filter
nft add chain filter input { type filter hook input priority 0 \; }
nft add rule filter input tcp dport 22 accept
nft insert rule filter input icmp type echo-request accept
nft add rule filter input drop

チェインの中身を表示するとき、handleを明示する。

root@router01:~# nft --handle list chain filter input
table ip filter {
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                icmp type echo-request accept # handle 3
                tcp dport 22 accept # handle 2
                drop # handle 4
        }
}

この --handleが大事で、追加したルールを個別に削除するときは、ハンドルを使う

個別に、IDを指定で削除

root@router01:~# nft delete rule filter input handle 3
root@router01:~# nft --handle list chain filter input
table ip filter {
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                tcp dport 22 accept # handle 2
                drop # handle 4
        }
}

削除後の番号を見ると、欠番が生じているのがわかる。インデックスでありながらも欠番がでるので、採番は連番とは限らない。番号には注意を払うこと。

nft --handle list chain filter input
nft -a list chain filter input

記述するとhandle は長いので、--handle-a でも代用可能である。

## 次は、できない
## 中身を記述して削除は、将来的なサポート予定らしい
nft delete rule filter input tcp dport 22 accept 

add をdelete にかえただけの削除はサポートされる「予定」らしい。

参考資料

https://wiki.archlinux.jp/index.php/Nftables#.E3.83.86.E3.83.BC.E3.83.96.E3.83.AB.E3.81.AE.E4.BD.9C.E6.88.90

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/securing_networks/configuring-nat-using-nftables_getting-started-with-nftables

https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management#:~:text=0.0.8%20drop-,Removing%20rules,it%20uniquely%20identifies%20the%20rule.&text=but%20this%20is%20not%20yet,until%20that%20feature%20is%20implemented.

Google Chromeのタブ周りの配色を落ち着いた配色にする。(Chromeデフォルト設定)

chrome の タブ周りの配色が変わった

ボタンの色なども変わってしまった。

Chrome バージョン 113.0.5672.53以降で変わるようだ。

プロファイルごとの設定でタブ配色が変わる。

chrome://settings/manageProfile

落ち着いた配色にする

非アクティブのタブは背景色に溶け込むとスッキリする。

薄い色を選ぶといい感じに溶け込む

デフォルト設定の色はタブがチカチカして集中を妨げてる印象がある。

カラーテーマを使うほどではない。

拡張テーマやダークモードを使うほどでもなく、Chromeに標準搭載された設定項目であるプロファイルのカラーテーマでカスタマイズが完結するのは安心感があって良い。

gnu screen で表示がずれる問題に対応する。

GNU Screen でdpkg などncursesの表示がずれる問題

GNU Screen はapt インストール時にセッション切断などの事故に備えるために非常に有効なツールである。なのに、APTでdpkgが起動すると、ncursesで表示がずれる問題が発生し、選択が非常にやりにくくなり、aptのために入れるのにaptが面倒になっていた。だからずっと使わなかった。。tmux を使ったいたのは、このcurses 問題がある。

いますぐ対応する場合

問題の原因は、日本語などCJKの全角文字の幅を適切に取り扱えない問題なので、LANG=Cをいれてやれば、解決する。

LANG=C screen 

最新バージョンのSCREENなら

~/.screenrc に次の魔法の文字を書くことで解決する。

cjkwidth off

なんと解決する!

(あえて、screen コマンドがわかるように若干数行分を上スクロールしてある)

リロード

Ctrl-Aを押してコマンドモードにしてから、リロードする

:source ~/.screenrc

今回実験した環境

screen -v
Screen version 4.09.00 (GNU) 30-Jan-22

screen は ubuntu のapt からインストール

takuya@raspi-ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.2 LTS
Release:        22.04
Codename:       jammy

screen いいじゃん

dpkg の表示がずれる問題がイヤで仕方なかったけどズレが解決するのであればScreenを使います。

参考資料

https://www.hiroom2.com/2015/05/29/utf8%E7%92%B0%E5%A2%83%E3%81%AEgnu-screen-4-1-4-2%E3%81%A7ncurses%E3%81%AE%E8%A1%A8%E7%A4%BA%E3%81%8C%E3%81%8A%E3%81%8B%E3%81%97%E3%81%8F%E3%81%AA%E3%82%8B/

GNU screen でマウスホイールでスクロールしたい。履歴になるの不便。

GNU Screen でスクロールを有効にしたい。

~/.screenrc

## ターミナルでマウスホイールしてカレントのログを登る
termcapinfo xterm* ti@:te@
## xterm以外のクライアント設定になってるなら
termcapinfo term* ti@:te@

いまのGNU Screen のデフォルトではコマンドのログが表示されてしまう。

screen を単なるタブ程度に使おうと思うと、コマンド履歴より表示ログをスクロールするほうが便利。なので、gnu screen にスクロールを辿らせることにする。

ついでに設定すると嬉しい .screenrc

# スタートアップメッセージ
startup_message off

## 背景色を消去
defbce on

## カラーリング
term xterm-256color

## エスケープをCtrl+T / C-Aは衝突する。
escape ^Tt

## ビジュアルベルをオフ
vbell off

## ターミナルでマウスホイールしてカレントのログを登る
termcapinfo xterm* ti@:te@

## 自動デタッチ
autodetach on

## vim が表示に残るのが面倒
altscreen on

## 画面分割ショートカット追加
## C-T v で縦分割 C-T s で横分割
bind v split -v
bind s split


## ログ保持の行数
## ターミナル系appでログ保持があるが
## アプリと齟齬がでるのでScreen側で保持
defscrollback 5000

## 全画面表示がずれる問題への対応
cjkwidth off


ファイルを更新する

:source ~/.screenrc

GNU Screen で screenrc をリロードする方法 - それマグで!

参考資料

https://pyopyopyo.hatenablog.com/entry/2021/10/10/130537

losetup でパーティションが認識されない。

losetup でパーティションが認識されない。

ddイメージファイル(raw)をパーティション検出を有効にしてループバックする。

sudo losetup -P /dev/loop10 ubuntu.raw 

-P をつけたらうまく行った。

なるほどね。でもdebian man には書いてなかった・・・ --help なら見れた。

 -P, --partscan                create a partitioned loop device

resize2fs で進捗表示

resize2fs で進捗表示したい

resize2fs -p ...

man に載ってた.

  -p     resize2fs の各操作について、作業完了分のパーセントを表すバーを表示する。 これによりユーザーはプログラムの動作を追うことができる。

進捗表示

実際に 進捗を表示してみた例。プログレスバーが状況を表示してくれる。

resize2fs はExt4の状況教によって時間がかかる。とくにShrinkは時間がかかるのでありがたい。

qcow2 をリサイズ(縮小)しメンテナンスやバックアップを行いやすくする。

qcow2 をリサイズ(縮小)しメンテナンスやバックアップを行いやすくする。

qcow2 を必要以上にに巨大サイズで確保したので、とっても扱いにくい。qcow2 を小さくしたい。

今回やった手法は、ダイレクトにパーティションを書き換える方法だ。

ダイレクトにパーティションやイメージファイルを「縮小」(shrink)しようとするとLVMで引っかかってすごく大変だったのでメモとして残す。

前提

作業するファイル ubuntu.qcow2

ubuntu.qcow2はEFIパーティションを含むEFIブート用のGPTディスクである。

ubuntu.qcow2 はLVMで構成されている。

LVMは、ubuntu-vg/ubuntu-lv の名前で構成している。

LVのubuntu-lvには ubuntuがインストールされていて、4GBを消費している。LVのサイズは6GBで程度ある。

PVのサイズは8.5GBを確保している。

qcow2 のサイズは10GBである。

まとめると、10GBのディスクイメージに1.5Gのブート領域が確保され、残りの8.5GBでPVが構成され、PV内に6GB程度のLVが構成されている。LVはext4で4GBを消費している。つまり無駄が多い。適当なサイズで適当に作ったディスクイメージのため中途半端な容量になっていて、コピーやバックアップで無駄な時間を浪費するのが許せない。raspiのSDカード内部で使っているディスクイメージなので、少しでもサイズを減らせば作業時間が稼げる。

お急ぎの人は

LVMの構成があるので、ディスクイメージにをダイレクトに操作するのが、本当に面倒だった。お急ぎの人は、大小ディスクイメージを2つ接続してpvmoveするべき(記事末尾に記載)

実際の作業

## qcow2 を作業用に持ってくる。
pv ubuntu.qcow2 > work.qcow2
## ループ・バックで接続する。
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
## ext4 をリサイズ(縮小)
sudo fsck.ext4 -f  /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv 5G

リサイズ(resize2fs)は時間がかかるので、この状態を保存しておく

## mapper を確認
sudo dmsetup ls 
## lvm を取り外し
sudo dmsetup remove ubuntu--vg-ubuntu--lv
## nbd ループバックを取り外し
sudo qemu-nbd  --disconnect /dev/nbd0 
## ファイルをコピー
pv work.qcow2 > work.resized.qcow2
## 以降の作業でミスった場合はリサイズ後からやり直す
## やり直すばあい
## pv work.resized.qcow2 >  work.qcow2 

## 再接続 / ループ・バックで接続する。
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

LV を操作する

## LVS をリサイズ
sudo lvresize ubuntu-vg/ubuntu-lv  --resizefs  -L 5G

--resizefs は念の為。すでにresize2fsは済ませたので時間的ロスはないので、念の為に書いただけ。

VG/LVの状態を確認する。

## LVの状態
sudo lvs ubuntu-vg/ubuntu-lv
  LV        VG        Attr       LSize Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  ubuntu-lv ubuntu-vg -wi-a----- 5.00g
## VGの状態
sudo vgs ubuntu-vg
  VG        #PV #LV #SN Attr   VSize  VFree
  ubuntu-vg   1   1   0 wz--n- <8.50g <3.50g
## PVの状態
sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

PVのサイズを変更する

PVのサイズは、自動的に決まるはずなので、自動的に決まらないようにする必要がある。

pvの状況を確認する PEは4MiBである

sudo pvdisplay /dev/nbd0p3
  --- Physical volume ---
  PV Name               /dev/nbd0p3
  VG Name               ubuntu-vg
  PV Size               <8.50 GiB / not usable 2.00 MiB
  Allocatable           yes
  PE Size               4.00 MiB
  Total PE              2175
  Free PE               895
  Allocated PE          1280
  PV UUID               YDqRlT-iObR-A8qn-Jhd9-QHno-Ivlb-Fi1lte

メタデータの量を確認

sudo pvck /dev/nbd0p3
  Found label on /dev/nbd0p3, sector 1, type=LVM2 001
  Found text metadata area: offset=4096, size=1044480

これらから、次のことがわかる。

PEは2175PEを確保している。メタデータは1044480バイトを確保されている。

PEのサイズとバイト数

PE( Physical Extent ) は4Mib ( 4* 1024 * 1024 ) で定義されている

1PE=4Mib

確保されているPEは 2175PEなので

2175PE
⇔ 2175*4*1024**2 bytes 
⇔ 9122611200 bytes 
⇔ ≒ 8.5Gb 

これを5.4GBにリサイズしようと思うと1383PEが必要になる。

5.4GB
⇔ 5.4*1024**3 bytes
⇔ 1382.4 PE
⇔ ≒1383 PE

ただし、メタデータが含まれるため、メタデータ分だけ容量は減る。metadata の容量はpvck で確認した値である。

sudo pvck /dev/nbd0p3
  Found label on /dev/nbd0p3, sector 1, type=LVM2 001
  Found text metadata area: offset=4096, size=1044480

新しいPVのサイズを計算する。

PVサイズはディスクのパーティションのサイズですから、パーティションのサイズはセクタ単位で計算されるので、セクタ数を調べる。

sudo sgdisk -p /dev/nbd0

出力から、512 bytes=1セクタであるとわかる。

Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): BDA3DC0C-F094-455B-965F-6546444E422C
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 20971486
Partitions will be aligned on 2048-sector boundaries
Total free space is 4029 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        20969471   8.5 GiB     8300

5.4G程度確保しようと思うと次のような計算になった

⇔ 確保したい容量+メタデータ量
⇔ ((5*1024**3+410*1024**2)+1044480) bytes
⇔ 5799669760
⇔ 11327480 sector ( 1 sector = 512 bytes )

この計算に沿ってgdisk で容量を変更する

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo gdisk  /dev/nbd0

パーティション+11327480セクタで作り直し

Command (? for help): p
## 確認
Disk /dev/nbd0: 20971520 sectors, 10.0 GiB
  Number  Start (sector)    End (sector)  Size       Code  Name
  1            2048         1050623   512.0 MiB   EF00
  2         1050624         3147775   1024.0 MiB  8300
  3         3147776        20969471   8.5 GiB     8300
## 削除
Command (? for help): d
Partition number (1-3): 3
## 作り直し
Command (? for help): n
Partition number (3-128, default 3):
First sector (34-20971486, default = 3147776) or {+-}size{KMGTP}:
Last sector (3147776-20971486, default = 20971486) or {+-}size{KMGTP}: +11327480
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'
## 確認
Command (? for help): p

  Number  Start (sector)    End (sector)  Size       Code  Name
  1         2048          1050623   512.0 MiB   EF00
  2         1050624       3147775   1024.0 MiB  8300
  3         3147776      14475255   5.4 GiB     8300  Linux filesystem
## 書き込み
Command (? for help): w

pvの設定サイズより小さくなったので、PEを読めなくてエラーになる。

sudo pvs /dev/nbd0p3
  WARNING: Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors. Was device resized?
  WARNING: One or more devices used as PVs in VG ubuntu-vg have changed sizes.
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

LVM の構造はキャッシュされるので一旦削除

ubuntu-vg という名前で /etc/lvm/{backup,archive} されているので削除する。

## 切断する
sudo qemu-nbd  --disconnect /dev/nbd0
## LVMの既知レイアウトのキャッシュ削除
sudo grep ubuntu-vg /etc/lvm -rl | xargs sudo rm
### 再接続する
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

これでもまだエラーになる場合

sudo pvs  /dev/nbd0p3
  WARNING: Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors. Was device resized?
  WARNING: One or more devices used as PVs in VG ubuntu-vg have changed sizes.
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <8.50g <3.50g

PV のサイズを強制的に変更する

エラーメッセージから現在のPVサイズが分かる

Device /dev/nbd0p3 has size of 11327480 sectors which is smaller than corresponding PV size of 17821696 sectors.

現在のPVは11327480 sectors であることがわかる。

セクタをバイト数に変換する。

11327480 sectrors 
⇔ 11327480*512 bytes
⇔ 5799669760 bytes

バイト数で PVの設定を作り直し

sudo pvresize --setphysicalvolumesize=5799669760b  /dev/nbd0p3

作り直したら読み込みし直す

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2

pv を確認する。

sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <5.40g 408.00m

細かい計算ミスをresize max で補う

LVとext4 は目標とするpvサイズより少量で確保したので、PVサイズに合わせてリサイズしておく。 現在のLVとPV

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0    10G  0 disk
├─nbd0p1                   43:1    0   512M  0 part
├─nbd0p2                   43:2    0     1G  0 part
└─nbd0p3                   43:3    0   5.4G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0     5G  0 lvm

最初に若干小さめにLVをリサイズしておいたので、PVよりLVが小さくなっており、500MB弱余っている。(512bytes=1sector 単位かつ、4Mib=1PEで整数倍のバイト数に揃える必要があり、計算が面倒になった。メタデータのこともあるので最初にLVを少なめにリサイズした。)

sudo lvresize -l +100%FREE ubuntu-vg/ubuntu-lv
sudo fsck.ext4 -f  /dev/mapper/ubuntu--vg-ubuntu--lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv

計算ミスを追いかけるのも面倒だし、メタデータのサイズも面倒なので、LV/ext4を小さくとって、PVを正しく設定した、PVはちゃんとLVを包含できたのを確認して、PVのサイズのMAXまでresize max して、無事に完了

PVチェック

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0   10G  0 disk
├─nbd0p1                   43:1    0  512M  0 part
├─nbd0p2                   43:2    0    1G  0 part
└─nbd0p3                   43:3    0  5.4G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0  5.4G  0 lvm

これで、PVは5.4Gに切りそろえることができた。

この状態を保存しておく

sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
pv work.qcow2 > work.PVresized.qcow2
## 戻すときは
# pv work.PVresized.qcow2 > work.qcow2 

qcow2 を縮小する

新しいディスクに必要なサイズを計算する

sgdisk -p /dev/nbd0

一番うしろのパーティションのセクタ位置を確認する

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        14475255   5.4 GiB     8300  Linux filesystem

最終セクタまでのバイト数を見る

14475255 セクタ
⇔ 14475255 * 512 バイト
⇔ 7411330560.0 bytes

つまり、7411330560バイトのサイズにqcow2をリサイズすればいいとわかる。 ただ、バックアップパーティションテーブルも必要なので少し余裕をもたせておく。 7411330560は6.9GBなので、7GB(=7516192768b)確保することにする。

qemu-img resize --shrink work.qcow2  7516192768b
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
sudo sgdisk -p /dev/nbd0
Warning! Disk size is smaller than the main header indicates! Loading
secondary header from the last sector of the disk! You should use 'v' to
verify disk integrity, and perhaps options on the experts' menu to repair
the disk.
Caution: invalid backup GPT header, but valid main header; regenerating
backup header from main header.

Warning! One or more CRCs don't match. You should repair the disk!
Main header: OK
Backup header: ERROR
Main partition table: OK
Backup partition table: ERROR

****************************************************************************
Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk
verification and recovery are STRONGLY recommended.
****************************************************************************
Disk /dev/nbd0: 14680064 sectors, 7.0 GiB

パーティションテーブルを書き換える

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/nbd0.
The operation has completed successfully.

書き換えたパーティションテーブルの値に沿ってPVを最終調整

sudo sgdisk -p /dev/nbd0
Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00
   2         1050624         3147775   1024.0 MiB  8300
   3         3147776        14680030   5.5 GiB     8300  Linux filesystem

パーティションのサイズからバイト数を計算

14680030-3147776
⇔ 11532254 セクタ
⇔ 11532254 *  512 バイト
⇔ 5904514048 バイト
### リサイズ
sudo pvresize --setphysicalvolumesize=5904514048b  /dev/nbd0p3
## 再接続
sudo dmsetup remove ubuntu--vg-ubuntu--lv
sudo qemu-nbd  --disconnect /dev/nbd0
sudo qemu-nbd  --connect=/dev/nbd0 ~/work.qcow2
## リサイズの結果を確認
sudo pvs

リサイズの結果、qemu-img resize で 100Mの余分が出ていた

  PV                       VG        Fmt  Attr PSize    PFree
  /dev/mapper/crypted-root vg.main   lvm2 a--  <111.27g <46.27g
  /dev/nbd0p3              ubuntu-vg lvm2 a--    <5.50g 100.00m

resize max して反映する

sudo lvresize -l +100%free ubuntu-vg/ubuntu-lv
sudo resize2fs -p /dev/ubuntu-vg/ubuntu-lv

最終チェック

ブロックデバイスの状況を見る。

lsblk /dev/nbd0
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
nbd0                       43:0    0    7G  0 disk
├─nbd0p1                   43:1    0  512M  0 part
├─nbd0p2                   43:2    0    1G  0 part
└─nbd0p3                   43:3    0  5.5G  0 part
  └─ubuntu--vg-ubuntu--lv 253:4    0  5.5G  0 lvm

LVMのPV / VG / LV を確認する。

$ sudo pvs /dev/nbd0p3
  PV          VG        Fmt  Attr PSize  PFree
  /dev/nbd0p3 ubuntu-vg lvm2 a--  <5.50g    0
$ sudo vgs ubuntu-vg
  VG        #PV #LV #SN Attr   VSize  VFree
  ubuntu-vg   1   1   0 wz--n- <5.50g    0
$ sudo lvs ubuntu-vg
  LV        VG        Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  ubuntu-lv ubuntu-vg -wi-a----- <5.50g

マウントしてみる。

$ sudo mount /dev/mapper/ubuntu--vg-ubuntu--lv /mnt
$ df -h /mnt
Filesystem                         Size  Used Avail Use% Mounted on
/dev/mapper/ubuntu--vg-ubuntu--lv  5.4G  4.0G  1.1G  79% /mnt
$ sudo umount /mnt

サイズもマウントも、OKだった。

やってみて

正確に計算すると、ちゃんと確実に終わるんだろうけど、PEサイズやセクタサイズなど気にするところがあって、正確な計算やりきりるのは面倒だった。やりながら考えていると、計算が合わなくなったり、メタデータサイズに気づかなかったりパーティションのサイズとかでミスが頻発して心が折れそうになった。

なれないことは、やるもんじゃない。

別解

pvmove という便利なコマンドがある。

2つのディスクにまたがったシンプルボリュームをLVMで構成することができる。

2つのディスクを接続し、VGを構成しておけば、あとは pvmove で古いディスクを取り外すことができる。

これをつかえば、バイトやセクタのサイズを気にすることなく、LVを別ディスクに移動することができる。PVのディスクの縮小ではpvmoveを使うべき。

pvcreate /dev/nbd1p3 #新しいディスクをPVへ追加
pvmove /dev/nbd1p3 # 新しいディスクへデータを移動
vgreduce vg-name /dev/nbd0p3 # 古いディスクを除去
pvremove /dev/nbd0p3 # 古いディスクを除去
resize2fs /dev/mapper/vg-name-lvname 

他に言えること。バックアップや移動がめんどくさいと思うようになったら、qcow2+kvm を諦めて、lxcに変えるべきなのかもしれない。lvmが面倒の原因なのでbtrfs にしてしまうのもありかもしれない。

apt のキャッシュはいつまで残るのか / APT::Archives MaxAge

apt のでーたはいつまでも残るのかなと思って調べた。

仮想マシンやSDカード起動などバックアップでは、容量を少なくするために、普段からなるべく clean をするようにしてるが、これいつまでも残るのか。 と疑問に思って調べた。

apt clean

APT::Archives

APT::Archives という設定項目があった。

takuya@ubuntu:~$ sudo grep Archive -R /etc/apt/
grep: /etc/apt/trusted.gpg.d/ubuntu-keyring-2018-archive.gpg: binary file matches
/etc/apt/apt.conf.d/20archive:APT::Archives::MaxAge "30";
/etc/apt/apt.conf.d/20archive:APT::Archives::MinAge "2";
/etc/apt/apt.conf.d/20archive:APT::Archives::MaxSize "500";

この値から想像するに、履歴は2世代前まで、30日間、最大500MB担っていると思われる。

では、このclean されるタイミングはいつなのかというと、apt install など apt コマンドが起動するときだと思われる。apt 系コマンドを定期的に実行しない場合は、削除チェックのタイミングが訪れず残り続けることになるのだろう。

作業用で頻繁にバックアップを取ってるマシンだと、ムダ時間を削減するため、apt clean は定期的に実行してもいいのじゃないかと思った。

使わないファイルをコピーしても無駄ですし。

画像が、白黒かカラーか判定する。(白黒に近いスキャン画像を判定する)

画像がカラーか、白黒かを判別する方法では、偶然にはてな質問の回答を見つけたので、コピペで行っていた。正直言って何がどうなってるのかよくわかってなかった。自宅の書籍や書類を整理するために大量にスキャンしていると、やっぱりこの判定がほしいなと思った。そのためもう一度見直すことにした。

画像がカラー・モノクロを判定する。

convert コマンドで画像を変換する、閾値より上下で2値にする、最後にmean(平均)を取り出す。

convert 001.jpg  \
  -colorspace HSB \ 
  -separate \
  -delete 0 \
  -fx "u*v" \
  -blur 2x2 \ 
  -threshold 30% \
  -format '%[fx:mean]\n' info:

もともとの回答者のコメントをみると、Convertで何をしているのかが見える。

colorspace HSB : 色空間をHSBに
separate : HSBになってる各チャンネルを分離して3枚のモノクロ画像に
delete 0 : 最初の0番の画像(色相)を削除
fx "u*v" : 1番(彩度)と2番(明度)の画像の各ピクセルを掛け合わせる。これで円柱型から円錐型のHSB空間における彩度になる
blur 2x2 : ノイズ除去用にぼかし
threshold 30% : 彩度30%を閾に二値化
format '%[fx:mean]' : 全体の平均値。30%で二値化しているので30%以上となったピクセルの割合になる。
書籍の自炊画像から、カラーページとモノクロページを判別するプ… - 人力検索はてな

サンプル画像を見ながら試す。

手順通りにやってみる。

元画像

HSBにする

RGBをHSBにする。(画像自体は変わらない。)

convert 001.jpg \
 -colorspace HSB \
 a-color.jpg

セパレートする

カラースペース(HSB:Hue色相・Saturation彩度・Brightness明度)に分割した状態で保存する。3枚になる。

convert 001.jpg \
 -colorspace HSB \
 -separate\
 b-color.jpg

b-color-0.jpg (Hue)

b-color-1.jpg (Sat)

b-color-1.jpg (Bri/V)

Hueを削除

上記のところから、インデックス=0であるHueを削除したもの

convert 001.jpg \
 -colorspace HSB \
 -separate \
 -delete 0 \
 c-color.jpg

2枚できる。出力に変化がないので、サンプル省略。

2枚を乗算

S,B の2枚を乗算する。オーバーレイ。彩度を外したので、色がないものになる。

convert 001.jpg \
 -colorspace HSB \
 -separate \
 -delete 0 \
 -fx 'u*v'\
 d-color.jpg

ぼかす。

細かいところがノイズにならないようちょっとぼかす

convert 001.jpg \
 -colorspace HSB \
 -separate \
 -delete 0 \
 -fx 'u*v' \
 -blur 2x2 \
 e-color.jpg

( blur 2,2 程度ではあまり変化はない、逆に考えると、見た目には変わらない程度に、ゴミが除去されたということ)

30% で2値化

30% ( 色範囲 0-65535の30%なので、19660>x , 19660<x ) の2値に分割する。

convert 001.jpg \
 -colorspace HSB \
 -separate \
 -delete 0 \
 -fx 'u*v' \
 -blur 2x2 \
 -threshold 30% \
 f-color.jpg

指定した閾値より濃いところは黒に、薄いところは白に2値化されている。

全体の平均を取る。

この状態で、全体の平均(白黒のピクセル数を集計し平均を取る、ツイッタのサムネイルの背景色、dominant_colorsを計算するようなもの)

convert 001.jpg \
 -colorspace HSB \
 -separate \
 -delete 0 \
 -fx 'u*v' \
 -blur 2x2 \
 -threshold 30% \
 -format '%[fx:mean]\n'\
 info:

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

0.216293

これは、0-65535の20(=14174)である。

この結果から、2値化して、色数が多い事がわかるので、カラーであるとわかる。

ぎゃくに、書籍のように白背景に黒文字、など色数が少ないものは、もっと小さな値になる。

書籍の例

たとえば、次のような画像で計算すると

convert dora.jpg  -colorspace HSB  -separate  -delete 0  -fx 'u*v'  -blur 2x2  -threshold 30%  -format '%[fx:mean]\n' info:

結果は、ゼロである。

0

次の画像は、0.21916 である。

このことから、画像が白黒に近いほど数字は0にちかづき、30%(0.3)を閾値にしているので、0.3より小さければ、ほぼ線画であるようなきがする。

ruby でやってみる。

このヒューリスティックアルゴリズムで本当に大丈夫なのか。画像を変えて調べたい。

大量に調べると画像の変換や一時ファイルへの書き出しがめんどくさいので、プログラムからオンメモリで処理していきたい。

とりあえず、手元ですぐ使えそうなruby を使ってみる。

mkdir work 
cd work 
bundle init 
sudo apt install libmagickwand-dev 
bundle add pry 
bundle add rmagick

コードを書いてみる。

#!/usr/bin/env ruby

require 'rmagick'
require 'pry'

def mean(file)
  blob = File.open(file).read
  i = Magick::Image.from_blob(blob).first
  i.resize_to_fit!(600)
  i.colorspace=Magick::HSBColorspace
  h = i.channel(Magick::HueChannel)
  v = i.channel(Magick::LuminosityChannel)
  u = i.channel(Magick::SaturationChannel)
  h.write('x-b-color-0.jpg')
  u.write('x-b-color-1.jpg')
  v.write('x-b-color-2.jpg')
  i = u.composite(v,0,0,Magick::MultiplyCompositeOp)
  i.write('x-d-color.jpg')
  i.blur_image(2,2)
  i.write('x-e-color.jpg')
  f = i.dup.bilevel_channel( Magick::QuantumRange * 0.3 )
  f.write('x-f-color.jpg')
  r = f.channel_mean(Magick::GrayChannel).map { |x| x / Magick::QuantumRange }[0]
end
(1..30).each{|i|
  f =  sprintf('%03d.jpg',i)
  r = mean(f)

  puts sprintf("%5s: %05f", f, r )
}

スキャン・スナップで取り込んだ書籍で試してみる。 実行してみる

bundle exec ruby rmagick.rb
001.jpg: 0.114141
002.jpg: 0.000000
003.jpg: 0.000000
004.jpg: 0.000000
005.jpg: 0.000000
006.jpg: 0.000000
007.jpg: 0.000000
008.jpg: 0.000000
009.jpg: 0.000000
010.jpg: 0.000000
011.jpg: 0.000000
012.jpg: 0.000000
013.jpg: 0.000000
014.jpg: 0.000000
015.jpg: 0.000000
016.jpg: 0.000000
017.jpg: 0.000000
018.jpg: 0.000000
019.jpg: 0.000000
020.jpg: 0.000000
021.jpg: 0.000000
022.jpg: 0.000000
023.jpg: 0.000000
024.jpg: 0.000000
025.jpg: 0.000000
026.jpg: 0.000000
027.jpg: 0.000000
028.jpg: 0.000000
029.jpg: 0.000000
030.jpg: 0.019163

あー良いなこれ。

ruby rmagick と convert のちがい

チャンネルやカラースペースを入れると、ImageListになる。

thredshold の箇所が、2値化を示す bi-level-channel という、わかりやすい名前になっている(しかしその分調べにくい。)

i.dup.bilevel_channel( Magick::QuantumRange * 0.3 )

なるほど、ね。

MagickよりOpenCVで調べたほうが楽だったか。

rmagick で画像処理してる人いなくて調べるのめんどくさかった。OpenCVPythonにすればよかった。

OpenCV使うともっと楽な方法が見つかった。

rgb でグレースケールを判定

そもそも、画像がグレースケール(2値化)に処理されている、処理済みの画像であれば、もっと簡単なのですよね。

#!/usr/bin/env ruby

require 'rmagick'
require 'pry'

def rgb(file)

  blob = File.open(file).read
  i = Magick::Image.from_blob(blob).first
  i.resize_to_fit!(600)
  r = i.channel(Magick::RedChannel)
  g = i.channel(Magick::GreenChannel)
  b = i.channel(Magick::BlueChannel)

  return r == g && g == b
end


(1..30).each{|i|
  f =  sprintf('%03d.jpg',i)
  rr = rgb(f) ? 'gray': 'color'

  puts sprintf("%5s: %5s", f, rr )
}

たしかに、もともとの画像がすでにグレースケールであれば、ピクセル計算でチェックせずに簡単に判定が可能ですね。

参考資料

https://stackoverflow.com/questions/21294036/

https://teratail.com/questions/372846

https://chantastu.hatenablog.com/entry/2022/09/07/233026

https://sabopy.com/py/scikit-image-4/

raspi に繋いだ USB-SSDのS.M.A.R.T値を取得する

raspi に繋いだSSD(USB)へsmartctl すると

sudo smartctl -a /dev/sda

次のようになり、値が取得できない。

smartctl 7.2 2020-12-30 r5155 [aarch64-linux-5.15.0-1026-raspi] (local build)
Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org

/dev/sda: Unknown USB bridge [0x152d:0x0580 (0x201)]
Please specify device type with the -d option.

オプションつけて値を取得する。

sudo smartctl  -a -d sat /dev/sda
sudo smartctl -a  -d sat,auto /dev/sda
sudo smartctl  -a -d scsi /dev/sda

オプションについて

VALID ARGUMENTS ARE: 
ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], 
usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntjmicron[,NSID], sntrealtek,
 intelliprop,N[+TYPE], jmb39x[-q],N[,sLBA][,force][+TYPE], jms56x,N[,sLBA][,force][+TYPE],
 marvell, areca,N/E, 3ware,N, hpt,L/M/N, megaraid,N, aacraid,H,L,ID, cciss,N, auto, test

-d のオプションはいくつかある。私は、sat と scsi でそこそこの情報が取れた。

参考資料

https://sourceforge.net/p/smartmontools/mailman/smartmontools-support/thread/4900DCE4.3020901@rtr.ca/

openwrt の firewall ipset 記述を入れた例

openwrt の firewall ipset 記述を入れたがうまく動かない。

firewall ipset を記述するときの注意。

fw4 / 2203以降

ipset ではなく、nft list setsを使ってる。

fw3 / 2203 未満

ipset rule を使っている。

rule が動かないとき

ipset を使った rule が動かない。(21.02.1、21.03.3 で確認)

protocol tcp を指定すると動く。

list proto 'tcp'

設定例 / マッチしたものを拒否

config ipset
  option name 'public_dns'
  option match 'dest_ip'
  option family 'ipv4'
  option loadfile '/etc/config/custom/public-dns/ip-list.txt'
  option enabled '1'

config rule
  option src 'lan'
  option dest 'wan'
  option ipset 'public_dns'
  option target 'REJECT'
  option device 'pppoe-mopera'
  option direction 'out'
  option family 'ipv4'
  option name 'reject public dns:ipset'
  list proto 'icmp'
  list proto 'any'

設定例 / マッチしないものを拒否

config ipset
  option name 'jp_addr'
  option match 'dest_ip'
  option family 'ipv4'
  option loadfile '/etc/config/custom/ipset/jp_addr.txt'
  option enabled '1'
config rule
  option name 'sslh-443 /ipset:cloudflare'
  option direction 'in'
  option device 'pppoe-ybb'
  option ipset '!jp_addr'
  option family 'ipv4'
  list proto 'tcp'
  list proto 'udp'
  option src 'wan'
  option dest_port '443'
  option target 'DROP'

rule 記述の注意。

わざわざ記事に書いているのは、トリッキー記述が必要だったから。プロトコル指定が必要。

ipsetを使うときluci/uci ではプロトコルを指定しないと動かなかった。プロトコルを指定すると動く。

以下に例を書く。

これは動く

#動く
  list proto 'tcp'
  list proto 'udp'
  list proto 'icmp'

プロトコルを省略したら動かない

## 動かない
config rule
  option name 'sslh-443 /ipset:cloudflare'
  option direction 'in'
  option ipset '!jp_addr'
  option family 'ipv4'
  option src 'wan'
  option dest_port '443'
  option target 'DROP'
## list proto 省略

プロトコルをANYにしても動かない。

## 動かない
  list proto 'any'

プロトコルを指定すると動く

## 動く
  list proto 'icmp'
  list proto 'tcp'
  list proto 'udp'

プロトコルをICMP+ANYにすると、ANYでマッチする(謎)

## 動く
  list proto 'icmp'
  list proto 'any'

理由はよくわからない。

openwrtのアップグレード手順(見直しの見直し

openwrt のアップグレード

アップグレードは、バックアップを取って新しいOSを書き込んで再起動する。バックアップを書き戻せばば良い。

ただし、以下の点に注意する。

  • /etc/config 以下がバックアップされる
  • opkg は再現されない。
  • /etc/passedはバックアップされる。
  • /bin/bashは喪失する

事前準備(シェル)

rootのログインシェルを/bin/ash以外のbashなどへ書き換えていると、SSHログインが再起不能になるので注意する。

chsh -s /bin/ash

事前準備(DNS・FW)

adguard などのDNS設定が入ってるとき、DNSサーバーをひけなくなる。wrt自身がどこを参照しているか確認。またwrt自体でFWを組んでいる場合はFW通信の疎通をちゃんと確認する。

cat /etc/resolv.conf
cat /etc/config/dhcp

手順1 opkgの保存

opkg 状態を再現するスクリプトがあるので、これを使ってopkg状態を保存する

curl -LJO https://raw.githubusercontent.com/richb-hanover/OpenWrtScripts/main/opkgscript.sh
chmod +x opkgscript.sh
./opkgscript.sh write /etc/config/opkg.installed

手順2 バックアップ確認・作成

ssh経由で、以下のコマンドで、バックアップ対象のファイルが確認できる。

sysupgrade -l

バックアップ対象を追加するには、コマンドで追加できる。

## 保存するファイルの追加
echo path/to/config >> /etc/sysupgrade.conf
## 保存するファイルの確認
sysupgrade -l 

必要に応じて、hotplugなどに配置したファイルもバックアップ対象に含める。

## 追加例
echo /root/opkgscript.sh >> /etc/sysupgrade.conf 
echo /etc/init.d/sslh >>  /etc/sysupgrade.conf
## 私はsymbolic linkで配備しているので、findコマンドから
find  /etc/hotplug.d/ -type l  >> sysupgrade.conf
find  /etc/init.d/ -type l  >> sysupgrade.conf

バックアップの作成

sysupgrade --create-backup $HOSTNAME-$( date -I ).tgz

バックアップ保存(scpやrsync、またはusbをマウントして)

scp $HOSTNAME-$( date -I ).tgz takuya@myserver:~/

バックアップ作成だけはWEB画面(luci)の専用ページ画面からもできる。コマンド経由(sysupgrade) よりも WEB-UI(luci)からやるのが楽ちん。ただし、バックアップ対象カスタマイズはコマンド経由で行う。

コマンド経由で行うと、次手順のアップグレードもコマンド経由で行うことができて便利。

手順3 アップグレードの開始。(OpenWrt内部で作業)

2023-10-17 メモ : 仮想マシン使ってる場合は、ディスクイメージファイルを直接操作したほうが楽だった。(末尾に追記)

事前に現在のディスク使用率やディスク容量を確認しておく。

/tmp/sysupgrade.img に展開されるのでtmp のディスク容量。またext4ディスク容量はddで初期化されるので現在使ってる容量を記憶しておく。

イメージ配布サイトから、自分のCPUモデルにマッチするイメージのURLを探し出して、アップグレードを開始する。

URL=https://downloads.openwrt.org/releases/22.03.3/targets/x86/64/openwrt-22.03.3-x86-64-generic-ext4-combined.img.gz
sysupgrade  --test -f $HOSTNAME-$( date -I ).tgz -v  $URL
## The point of NO return 
sysupgrade  -f $HOSTNAME-$( date -I ).tgz-v  $URL
## wait for reboot

私は、ext4MBRを使っているので、ext4-combined(bootとext4)を選んだ。最近だとEFIだと思う。squashfs はopenwrtの非x86でよく使うファイルシステムで、squashfs版は、再起動すると設定以外は全部消える。つまりramfsに載せるタイプである。これを選ぶと容量使わないしSSDも消費しないので便利だと思う。ただ私は、x86_64仮想マシンで動かしているしディスク容量も潤沢にあるのでext4でいいかと思ってる。

手順4 容量の確認とopkg の再インストール

opkg の再インストールをする。ただし焦ってインストールしてはいけない。

アップグレードは、imgをddされるだけなので、領域容量がデフォルト・サイズに書換わってる。この状態でインストールするとopkg install が容量不足になり、中途半端なインストール状態となり再起不能になる可能性がある。

現在のパーティションサイズを確認。

mount 
df 
lsblk 

パーティションサイズが100M前後では、かなり心許ない。ip-fullやvim-fullerやiptablesを使ってるなら200Mはほしいところ。

仮想マシンを停止してイメージを操作しディスク容量を増量する。

無事に起動したことを確認後、シャットダウン

virsh shutdown OpenWrt
sleep 20
virsh list --all 

ディスク・イメージの場所を確認

## ストレージを確認
virsh  domblklist OpenWrt

ディスク・イメージをブロックデバイスとして接続する。

## qcow2であればnbdを経由する
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 /var/lib/libvirt/images/openwrt-x86-64-combined-ext4.qcow2
## 使い終わったら
# sudo qemu-nbd -d /dev/nbd0 
# sudo rmmod nbd

## raw img であれば losetup だけで行ける。
sudo losetup -P -f /var/lib/libvirt/images/openwrt-x86-64-combined-ext4.img 

ディスクイメージを操作

sudo fdisk /dev/loop22
sudo resize2fs /dev/loop22p2
sudo losetup  -d /dev/loop22

ディスク容量を確保したら、opkgの復元

./opkgscript.sh install /etc/config/opkg.installed

手順3(方法2)アップグレードーchrootする

仮想マシンらしく、ディスクイメージの操作をしたほうが楽だと思う。

ext4を選んでいると仮想マシンだ。しかもsysupgrade -fddをしてるに過ぎない。OpenWrtの特性上、アップグレードといっても、クリーンインストールにsysupgrade がデータをコピーするだけである。

ディスクサイズが100M以下なら、公式配布ディスクを起動したら終わりな、アップグレードはとても楽である。 Used>100Mならアップグレード後のopkg install中に容量不足になるため、事前にディスクサイズ拡大を行う。

ディスクサイズの確認。

root@OpenWrt:~# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root               147.2M    111.3M     32.8M  77% /
tmpfs                     7.5G     11.0M      7.5G   0% /tmp
/dev/vda1                15.7M      5.2M     10.2M  34% /boot
/dev/vda1                15.7M      5.2M     10.2M  34% /boot
tmpfs                   512.0K         0    512.0K   0% /dev

私は、100M以上使っているので、サイズ拡大が必要だった。

ディスクイメージのファイルの情報などを見ておく。

## ワーキングコピー
cp openwrt-22.03-x86-64-combined-ext4.qcow2 WRT-current.qcow2
disk=WRT-current.qcow2
## 接続する。
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 /var/lib/libvirt/images/$disk
sudo mount /dev/nbd0p2 /mnt
## ディスクの情報を見る。
df -h /mnt
lsblk /dev/nbd0
qcow-img info /var/lib/libvirt/images/$disk
## 取り外し
sudo umount /mnt 
sudo qemu-nbd -d /dev/nbd0
sudo rmmod nbd

アップグレード先のディスクを用意する。

まっさらなディスクイメージをダウンロードしてくる。

URL=https://downloads.openwrt.org/releases/23.05.0/targets/x86/64/openwrt-23.05.0-x86-64-generic-ext4-combined.img.gz
wget $URL
pigz -d openwrt-23.05.0-x86-64-generic-ext4-combined.img.gz
## 作業用コピー作成
cp openwrt-23.05.0-x86-64-generic-ext4-combined.img work.img
## マウントする。
sudo losetup  -P -f /var/lib/libvirt/images/work.img
sudo losetup  
sudo lsblk ## パーティションを確認
sudo mount /dev/loop10p2  /mnt

sysupgrade 用のバックアップ・データを書き込む

## バックアップを書き込む
sudo cp ~/OpenWrt-2023-10-17.tgz  /mnt/root/
## chroot してデータ展開
sudo chroot /mnt '/bin/ash'
sysupgrade -r /root/OpenWrt-2023-10-17.tgz

ディスクの容量を調整

## ディスク・サイズを調整
sudo truncate -s +60M work.img
sudo losetup  -P -f /var/lib/libvirt/images/work.img
sudo losetup -c /dev/loop10
sudo sfdisk /dev/loop10 ## パーティションを編集
sudo e2fsck -fy /dev/loop10p2
sudo resize2fs /dev/loop10p2
## 結果の確認
sudo mount /dev/loop10p2  /mnt
sudo umount /mnt 
sudo losetup -d /dev/loop10

容量大きくしたので。ついでに、qcow2にしておく

cd /var/lib/libvirt/images/
sudo qemu-img convert -O qcow2 work.img work.qcow2
sudo virt-sparsify work.qcow2 work.qcow2.sparse
sudo cp work.qcow2.sparse work.qcow2
## マウントして確認
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 /var/lib/libvirt/images/work.qcow2
## 容量とか見る。
sudo mount /dev/nbd0p2 /mnt
sudo df -h /mnt
## 取り外し
sudo qemu-nbd -d /dev/nbd0
sudo rmmod nbd

どうせなら、opkg installchrootで済ませたかったが、、、今後の課題とする。

仮想マシンを編集する。

virsh shutdown OpenWrt
virsh edit OpenWrt

ディスクを取り替える。

 <disk>
    <driver name='qemu' type='qcow2'/>
    ## <source file='/var/lib/libvirt/images/openwrt-current.qcow2'/>
    <source file='/var/lib/libvirt/images/work.qcow2'/>
    <target dev='vda' bus='virtio'/>
(略

起動する

virsh start OpenWrt

これで、起動するはずである。opkg の追加パッケージが皆無なため、あれこれ設定がおかしくなってるかもしれないので注意は必要である。

22.03 にアップグレードしたときに、発生した問題。

  • iptablesのsocket モジュールが行方不明になった。
  • opkg install 時に、unboundが未インストールのために、名前解決ができなかった
  • /etc/passwd で /bin/bashに変えていたのでログイン不可になった
  • ddns関連の設定が飛んでいた。
  • ipset 関連がopkg未インストールのためにエラーで死んでiptablesがエラーになっていた。
  • fw3 → fw4 に変わったので/etc/config/firewall に一部互換性がなくなった。

unboundが未インストールでunboundをインストールできない問題、気づかずに焦った。PPPoEが接続されてないのかネットワークのトラブルなのか、すごく悩んだけど、/etc/resolve.confを書き換えで解決した。

2204 にアップグレードしたときに発生した問題点

ddns の設定が変わっていて dynamic dns が全部死んだ。

ddnsスクリプトがopkg管理外になったらしく、update scripts のボタンが設置され、定義ファイルが更新されていた。また設定項目が一部更新されており、互換性がなくなったため再設定することになった。

  • openwrt が AAAA をフィルタしない。

dnsmasq をversion 2.87 以降にすればいい。DNSMasqの更新履歴から読み取れる。 22.03.05 のDNSMasqがこれ (2.86)、また、23.05.0 の DNSMasq がこれ(2.89) 。なので、v2.87以降を使うなら23.x にアップグレードする。

2305 にアップグレードしたときに発生した問題点

  • dnsmasq が unboundと連携しててunboundが消えたのでdnsmasqがDNSを引けなくなってた。
    • dns forwardings を'127.0.0.1#5353'としdnsmasq が port 5353に聴くようにしてたのが、unboundがいなくなってエラー
  • fgrep: warning: fgrep is obsolescent; using grep -F がでる。
    • vi /usr/libexec/fgrep-gnu で編集すると消せる
  • vim のバージョンが上がって各種設定が動かなかったり。
  • ipsec/strongswan/swanctl 周りのパッケージ名が変わってインストール移行出来てなかった。

以前ほどトラブルは起きてない。

2023-10-30 unbound が問い合わせの応答に失敗する。

初回だけ、SOAを返してしまう。

unbound のquery_minimize と Strict Minimizeがチェックが増えて動作が変わってるようです。

query_minimize時にStrict Minimizeにチェックがあると初回応答はSOAだけです。A/CNAMEが返されませんでした。そのため、AレコードがないNO EXISTS DOMAIN(NXDOMAIN)となり、変な値がキャッシュされてしまいいました。サーバが見つからないエラーが頻発しました。

option query_minimize '1' 
option query_min_strict '0'

この方が良さそうです。

予備のルータを用意する

仮想マシンなんだし、予備のルータを用意するほうがアップグレードは楽だと思った。

気づいたこと

アップグレードとはいえ、どうせddされるだけなんだよね。バックアップを取ってext4-combinedイメージを取得して新規の仮想マシンで起動しちゃったほうが楽だよね絶対。

2023-10-17 追記。ためした。chroot仮想マシンのDISK編集するほうが私にはわかりやすくて楽だった。

以前の資料

以前はこのようにやっていた。 https://takuya-1st.hatenablog.jp/entry/2021/12/20/041955

vim でターミナルを表示する

vim の画面分割でターミナルを使う。

quickrun が思うように動かなくて代替案を探していた。

知らなかった。Vimの画面分割でターミナルを表示することができる。

:terminal

vs code でターミナルを表示できてIDEって便利だなと思ってたら、vim を使えば同じことができるんですね。便利だなと。

ターミナルの出力結果をコピペ

ctrl-w N

N(n は大文字)を入れれば、ノーマルモードになって、コピペが可能になる。

ターミナル・ノーマルモードからの脱出

i

a / i を入力すれば、ノーマルモードからターミナルの入力に戻れる。なんて便利なんだ。

i/a でインサートモードに戻れば、ターミナルとして再度使える。

実行シェル(REPL)を指定する。

:terminal irb
:terminal php -a 
:terminal node

phpirb や node などのREPLを起動すると楽しい。

参考資料

https://knowledge.sakura.ad.jp/23018/

vim quickrun を設定する

久しぶりに、SSH経由で、シェルスクリプトを書いていると、vim quickrun などvim関連の設定が壊れていることに気づいた。

quickrun があると、さくっとあれこれできるので気に入っていたが、どこかのタイミングで動かなくなってて、vs code などもあるし、良いかなと思って放置してたけど。やっぱりないと不便だよね。

quickrun の設定

[[plugins]]
repo = 'thinca/vim-quickrun'
hook_add = '''
  nnoremap <leader>r :QuickRun<CR>
  let g:quickrun_config={}
  let g:quickrun_config._={
  \  "outputter" : "buffer",
  \  "outputter/buffer/opener": "5new",
  \  'outputter/buffer/close_on_empty': 1,
  \  'hook/animation/enable': 1,
  \ }
'''

インストールには、deinを使う。

変更点

以前使っていた、横への分割は、ディスコンされていて、buffer_legacyになっていた。いまは、bufferを使う。 splitbelowでnew したときに下に開くようにした。5new で5行で開く

nnoremap <leader>r :QuickRun<CR> で leader から\r を押せばQuickRunが起動するようになっていたが、デフォルトのキーマップは削除されて明示する必要ができてた

toml と vimrc で重複登録してないかとかもチェック。

参考資料

dmsetup で lvm を取り外す

dmsetup コマンドを使って、lvm 関連のデバイスを除去する。

sudo dmsetup remove vg.main-root
sudo dmsetup remove vg.main-swap

これは、次のコマンドでInActiveにするのと同じ作用を齎すらしい。

sudo vgchange -a n vg.main

再起動まえにリロードするなら、activate しないといけない。

sudo vgchange -a y vg.main

USBデバイスを抜き差しするときに、LVMが表示されなかったりロードがおかしくなったときに使える。

また、LUKSのデバイスをCloseする代わりにも使える。

sudo dmsetup root-crypted
sudo cryptsetup close root-crypted

dmsetup は device mapper の略だと思う。ブロックデバイス再帰的に扱うLVMやLUKSで使える。

btrfs が暗号化に対応してくれたらdm 使わなくて済みそうなんだけど。いつになるのやら。

現在のDMを一覧する。

sudo dmsetup  -C  info
sudo dmsetup  -C  table
sudo dmsetup  -C  ls

ブロックデバイスを扱えるのは便利。