それマグで!

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

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

Rejected request from RFC1918 IP to public server addressに対応する。

OpenWrt で自分自身のグローバルIP(PPPoE)へHTTPすると、RFC1918

例えば次のように、グローバルIPへ内部から接続すると

curl -v  http://{$my-global-ip}

RFC1918のエラーになる。

Rejected request from RFC1918 IP to public server address

これは、OpenWrtの管理画面UIであるLuciから出ている。

Luciのuhttpd が 0.0.0.0/0 をリッスンしていて、PPPoEのIPで応答するため、上記のエラー(Reject RFC1918) が発生する。

対応

対応は「何をしたいか」によって分かれる。

Luciを見たい、見せたいのであれば、対応策1を使う。

Luciを応答させずに、別のApache/nginxなどWEBサーバーを応答させたいときは、対応策2を使う。

私が、この事象に遭遇したのは次の理由による。

443ポート(https)を転送するけど、80(http)はDNAT(リダイレクト)を未記入で使っていた。

最近は80を使うことが減ったのでポート転送を書いてなかった。

しかし、HTTP->HTTPSのリダイレクトを行うには、いったん80で応答しなくてはいけなかった。

対応策1

rfc1918_filter をオフにする。

/etc/config/uhttpd:  option rfc1918_filter '1'

対応策2

ファイアウォールでリダイレクトを書く。

/etc/config/firewall

config redirect
  option target 'DNAT'
  option name 'wan-http-to-local'
  option family 'ipv4'
  list proto 'tcp'
  option src 'wan'
  option src_dport '80'
  option dest_ip '192.168.1.xxx'
  option dest_port '80'

ファイアウォールの転送は、uhttpd応答より前段階である。FWが転送してuhttpdにパケットが届く前に処理されるように設定している。

参考資料

https://forum.openwrt.org/t/hosting-website-rejected-request-from-rfc1918-ip-to-public-server-address/158971

lxc の storage / btrfs で 圧縮を有効にする

lxc ディスクの圧縮

以前も試したのだが、Raspiを構成し直すにあたり、もう一度やってみた。

lxd のストレージ・プールbtrfsに透過圧縮(zstd)を有効にする。 - それマグで!

圧縮を有効にする

lxc storage set default btrfs.mount_options compress=zstd

または、lxc storage edit で編集する

lxc storage edit default 

例えば次のように編集

config:
  btrfs.mount_options: user_subvol_rm_allowed,compress=zstd
 source:xxxxxxxxxxxxx
 volatile.initial_source: /dev/vg.main/lxd      

実際に試してみる

インスタンスを作る

lxc launch ubuntu:22.04 test        

最初は、compressがないことを確認

lxc storage get default btrfs.mount_options #=> 空文字が戻る

インスタンスから確認

lxc  exec test  -- mount | grep btrfs
/dev/disk/by-uuid/xxxx on / type btrfs (rw,relatime,idmapped,ssd,space_cache=v2,user_subvol_rm_allowed,subvolid=257,subvol=/containers/test)

有効にする

lxc storage set default btrfs.mount_options user_subvol_rm_allowed,compress=zstd

再起動して

lxc stop test
lxc start test

compressが有効になったことを確認

lxc  exec test  -- mount | grep btrfs
/dev/disk/by-uuid/xxxx on / type btrfs (rw,relatime,idmapped,compress=zstd:3,ssd,space_cache=v2,user_subvol_rm_allowed,subvolid=257,subvol=/containers/test)

前回との違い

lxc / lxd 自体の再起動が不要であるとわかった。

関連資料

lxd のストレージ・プールbtrfsに透過圧縮(zstd)を有効にする。 - それマグで!

ip route のOn-Linkがよくわからないので、ちょっと調べた。

on-linkしてるのにルートが作られない。、なぜだろう。

onlink は on-link (強制的にルートを入れる)と意味です。

似たようなものに、scope link があって勘違いしてました。

いくつかの記事から見てみます。

https://forum.openwrt.org/t/routes-onlink-missing-doc/114941

On-Link route means one that the addresses that belong to it can be resolved locally and gateway is not needed. For example a route to 127.0.0.0/8 is on-link via interface lo0. In your example the use of on-link is wrong. on-link ルートは指定したアドレス範囲に属するアドレスが、ローカルのルーティングテーブルで解決されゲートウェイを必要としない、ということを意味する。 たとえば、127.0.0.0/8 なら via interface lo0 でon-linkになるのである。これを踏まえると君の例のon-linkは設定ミスになる。

The onlink option is used in very rare cases. When you create a static route, the next hop address must be directly reachable by the specified device, meaning it must be from the same IP subnet. Otherwise you'll get an error response.

onlinkオプションは稀有事例で使われるようです。静的ルートを作ったとして、ネクスト・ホップのアドレスはデバイス到達圏内(同じサブネットなど)

Using this option, we tell the kernel that the next hop is located in the same Layer 2 segment.

onlink means that the routing should "pretend that the nexthop is directly attached to this link, even if it does not match any interface prefix". So we can make a fake one of those in the table too: https://unix.stackexchange.com/questions/579087/whats-the-meaning-of-linkdown-and-onlink-in-linux-route

onlink はルーティング で次の意味である「Next Hop が直接疎通可能として振る舞う。サブネットマスクが不一致であろうがなかろうが無関係にそう扱う。」つまり、ルーティング・テーブルに追加して擬似的に振る舞う。

When enabled, gateway is on-link even if the gateway does not match any interface prefix 有効時はGWはonlink になる。GWに指定したIPプレフィックス(ネットワークアドレス)が不合致でもGWに登録される。

次のようなネットワークがあるときに、

onlink は次のような例になるようです、

192.168.9.2/32 宛は、192.168.9.2 に eth0 から投げる / onlink

192.168.9.2/32 dev eth0 proto static onlink

192.168.7.0/24 宛は、192.168.9.1 に eth0 から投げる / onlink

192.168.7.0/24 via 192.168.9.1 dev eth0 proto static onlink

192.168.1.0 宛は、eth0から流す。 / scope link

192.168.1.0/24 dev eth0 proto static scope link

デフォルト / gw / static

default via 192.168.1.1/24 dev eth0 proto static 

scope link は、リンクローカルなスコープを示す属性である。

この属性は、特定インターフェースに直接接続されたサブネットを示す。192.168.1.0/24 が、eth0 直接接続であり、192.168.1.0/24宛の通信はローカルリンク完結であると読み取る。

もっと短く言えば、"scope link" 属性は、リンク上の通信で使う。

ルーティングテーブルが特定ネットワークへ直接的な経路を持つ、という意味です。

192.168.7.0/24 のネットワークへは、192.168.9.1 を経由するようにeth0送出される。

onlink は静的ルート属性であり、直接接続されたeth-に対してルートが有効を示す。

実際の例

実際の例を見てみると、少し想像がつくかもしれない。

デフォルトルートが構成されている例。

default via 192.168.1.1 dev eth0 proto static src 192.168.1.2 metric 240 onlink

特定のネットワークアドレス宛の例

192.168.7.0/24 via 192.168.9.1 dev eth0 proto static src 192.168.1.2 metric 240 onlink

このような感じになると思うが、特定ネットワークアドレス向けでは onlink はなくても動くので用途がないと感じる。

ややこしい

on-link だから 「リンクしたときに使う」と考えて、「 ip link set dev eth0 up 」した時にデバイスが有効化されるのとおなじように、ip addr が割り当てたら使うのかと思ったら、全然そんなことはなかった。私が混乱した理由は、on の意味の誤解だった。onlink のon は「〇〇のとき」ではなく、「〇〇の上に」の意味なんだと気づいた。

自分で使うことはあまりない。

通常の経路設定で使うことは無いと思うので。気にしない方がいい。

openvpn access server の管理画面をcurlで書き換える。

OpenVPNAccess Server のAPIを使って設定を書き換えようと思ったが、APIを探し出したり、管理コマンドをSSH経由で実行するのもめんどくさい。

curl でwebUIを叩いてみることにした。

WEB画面のSSL証明書を更新する例。

DOMAIN=ovpn.example.tld
CERT_PATH=/tmp/${DOMAIN}.crt.pem 
PKEY_PATH=/tmp/${DOMAIN}.privkey.pem 
OVPN_AS_SITE=192.168.2.147:943
OVPN_AS_USER=takuya
OVPN_AS_PASS=takuya
COOKIE=/tmp/ovpn-as.cookie

function cleanup() {

  echo -n 'clean up ..'
  [ -e ${COOKIE}    ] && rm ${COOKIE};
  echo 'done.'

}

function update_cert_ovpn_as(){
  [ ! -e $CERT_PATH ] && exit 1 
  [ ! -e $PKEY_PATH ] && exit 1 

  ## ログインして
  echo -n 'login to openvpn as ..'
  curl -ksL -c ${COOKIE} -b ${COOKIE}  https://${OVPN_AS_SITE}/admin/ > /dev/null
  curl -ksL -c ${COOKIE} -b ${COOKIE} \
   -d username=${OVPN_AS_USER} -d password=${OVPN_AS_PASS} \
    https://${OVPN_AS_SITE}/admin/__login__ > /dev/null
  echo 'done'


  ## フォームIDを取り出して
  echo -n 'get form key  ..'
  POST_ID=$(curl -ksL -c ${COOKIE} -b ${COOKIE}  \
     https://${OVPN_AS_SITE}/admin/web_server |  \
     grep -oP '(?<=name="post_id" type="hidden" value=").+(?=" />)' )
  echo 'done'
  echo POST_ID=$POST_ID

  ## 証明書を更新して
  echo -n 'post cert pkey  ..'
  curl -kvL -c ${COOKIE} -b ${COOKIE} \
  -F cert_option=user_provided \
  -F domain='' \
  -F email='' \
  -F cs.ca_bundle='' \
  -F cs.cert=@${CERT_PATH}\
  -F cs.priv_key=@${PKEY_PATH} \
  -F button='Save Settings' \
  -F post_id="${POST_ID}" \
  https://${OVPN_AS_SITE}/admin/web_server > /dev/null
  echo 'done'

  ## 再起動
  echo -n 'request restart  ..'
  curl -ksL -c ${COOKIE} -b ${COOKIE} \
  -F button='Update Running Server' \
  -F cert_option=user_provided \
  -F domain='' \
  -F email='' \
  -F cs.ca_bundle='' \
  -F cs.cert='' \
  -F cs.priv_key='' \
  -F post_id="${POST_ID}" \
  https://${OVPN_AS_SITE}/admin/web_server  > /dev/null
  echo 'done'

}

update_cert_ovpn_as;
cleanup;

WEB画面があれば、APIが分からなくても、curl でサクッとスクレイピングできて便利ですね。

cookieとリダイレクトを扱う。

コツとしては、次の箇所。

curl -ksL -c ${COOKIE} -b ${COOKIE}  $URL

発行されるCookieを保存しつつ、リクエストにつかう。これでリクエストのセッションが維持できる

CSRF キーを取り出す

もう一つがCSRFのキーを取り出すgrepの箇所

POST_ID=$(curl -ksL -c ${COOKIE} -b ${COOKIE}  \
     https://${OVPN_AS_SITE}/admin/web_server |  \
     grep -oP '(?<=name="post_id" type="hidden" value=").+(?=" />)' )

WEBフォームにはCSRFトークンがついているので、CSRFトークンを取り出してリクエストに使う。

上記のコツを覚えておく

上記のコツを覚えておけば、あとはChromeの開発ツールでリクエストパラメタを見ていればWEB管理画面程度であればリクエストを発行して更新できて便利。

EFIでマルチブートを作ると区別できない。(全部Ubuntuで区別つかん)

EFI パーティションデュアルブートを作りました。

EFIパーティションには、OSごとにブートを設定を書くことができます。

EFIパーティションを使うように grub をインストールする。

sudo grub-install --target=x86_64-efi

細かく指定すると

grub-install --target=x86_64-efi --boot-directory=/boot --recheck /dev/sda

UbuntuUbuntuデュアルブートを作ると区別できない

頑張っても以下のように、同名で登録されてしまう。

takuya@spare:~$ sudo efibootmgr -v
BootCurrent: 0002
Timeout: 0 seconds
BootOrder: 0000,0002,0007,0008,0005,0006
Boot0000* ubuntu       HD(1,GPT,c80c6849-859c-4251-99d1-085d5b56fdf3,0x800,0x10000)/File(\EFI\UBUNTU-MAIN\SHIMX64.EFI)
Boot0002* ubuntu       HD(5,GPT,3fdd0e27-fad1-4afc-9534-5dfbab47c8d4,0x10800,0x10000)/File(\EFI\UBUNTU-SPARE\SHIMX64.EFI)
Boot0005* Generic Usb Device    VenHw(99e275e7-75a0-4b37-a2e6-c5385e6c00cb)
Boot0006* CD/DVD Device VenHw(99e275e7-75a0-4b37-a2e6-c5385e6c00cb)

ubuntu 多すぎて区別できねぇ

ESP を複数作って区別してみた。

ESPを2つ作ったら、行けた

ESP(EFI System Partition)を複数作って、Grubの設定で名前を変えた。

root@livaz:/home/takuya# efibootmgr -v
BootCurrent: 0003
Timeout: 0 seconds
BootOrder: 0000,0002
Boot0000* ubuntu-main   HD(1,GPT,c80c6849-859c-4251-99d1-085d5b56fdf3,0x800,0x10000)/File(\EFI\ubuntu-main\shimx64.efi)
Boot0002* ubuntu-spare  HD(5,GPT,3fdd0e27-fad1-4afc-9534-5dfbab47c8d4,0x10800,0x10000)/File(\EFI\UBUNTU-SPARE\SHIMX64.EFI)

再現方法

最初に次のような、パーティションを用意した

/dev/sda1 # ESP for ubuntu-main
/dev/sda2 # ESP for ubuntu-spare
/dev/sda3 # ubuntu-main root fs
/dev/sda4 # ubuntu-spare root fs

2つのシステムパーティションと2つのESPを用意した。Ubuntursyncでコピーした。

GRUBの設定をする。

sudo su
echo GRUB_DISTRIBUTOR="Ubuntu main" >> /etc/default/grub
update-grub
grub-install --target=x86_64-efi

もう一つのOSはchroot で入って。

## 
sudo mount /dev/sda4 /mnt
sudo mount /dev/sda2 /mnt/boot/efi
##
cd /mnt
sudo mount -t proc proc proc/
sudo mount -t sysfs sys sys/
sudo mount -o bind /dev dev/
sudo mount -t devpts pts dev/pts/
## 
sudo chroot /mnt

grub を設定

echo GRUB_DISTRIBUTOR="Ubuntu spare" >> /etc/default/grub
update-grub
grub-install --target=x86_64-efi
exit

efi を確認する。

EFIを確認する。

root@ubuntu-main:/home/takuya# efibootmgr -v
BootCurrent: 0003
Timeout: 0 seconds
BootOrder: 0000,0002
Boot0000* ubuntu-main   HD(1,GPT,c80c6849-859c-4251-99d1-085d5b56fdf3,0x800,0x10000)/File(\EFI\ubuntu-main\shimx64.efi)
Boot0002* ubuntu-spare  HD(5,GPT,3fdd0e27-fad1-4afc-9534-5dfbab47c8d4,0x10800,0x10000)/File(\EFI\UBUNTU-SPARE\SHIMX64.EFI)

EFPが2つあれば、Ubuntuが2つあっても別々のEFIを使えることがわかった。

GRUB_DISTRIBUTOR

GRUB_DISTRIBUTOR を指定すると、Grubのメニューで設定される名前やEFIに書かれるファイルが異なるように指定できる。

ただし、'ubuntu/main'のようなスラッシュ区切りの名前を指定すると、EFIパーティションディレクトリ階層が深くなってしまい、起動できなくなる。

以下は、間違ってスラッシュを指定したときの例。

Boot0000* ubuntu-main   HD(1,GPT,xxx)/File(\EFI\UBUNTU\MAIN\SHIMX64.EFI)

階層が深くなりすぎている。

EFI の問題点(Ubuntu複数)

ESPが一つではだめな理由。

EFI は ESPをgrub を インストールにするのだが、Ubuntuが複数あると厄介になる。

Ubuntu2つ(便宜上 main / spare とする) が、それぞれを同じEFIの名前の箇所に設定を書いてしまう。

ubuntu-main も ubuntu-spareも「同じファイル」を書き込みに行く。

\EFI\UBUNTU\SHIMX64.EFI
\EFI\UBUNTU\GRUB.CFG

このため、Ubuntuデュアルブートするときは、EFIを固定ができない。

EFIから起動するGrubデュアルブートを担当することになる。

GRUB設定でOS_PROBEを有効にしておくとデュアルブートEFIではなくGrubで実行できる。

GRUBでは厄介な問題が出てくる。

grub を使う場合、grububuntu-main の /boot なのか、ubuntu-spare の /boot なのか全くわからない。

またgrub はUSBシリアルコンソールを使えないので、リモートからどちらをブートさせるかを明示的に選ぶことが不可能だった。

grub にはデフォルトでどのメニュー項目を選ぶ設定がある

grub デフォルト選択を指定して再起動

たとえば、メニュー項目4番で起動するように設定してみると

sudo grub-reboot 4

この設定を永続化するとか

sudo grub-set-default 0

最後に選択したGrub項目を常にデフォルトにするとか

GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true

これらは一見すると便利そうに見えるが、grub が複数あるとすげえ不便である。

ubuntu-spare で起動して、apt upgrade をかけたときに grub が更新されてしまったり。

これを固定するには、OS_PROBEを設定した上で、GRUB_DEFAUTを固定する

GRUB_DEFAULT="ubuntu-main"

さらに、/bootをルートファイルシステムから切り離して共有してしまえばスッキリするのだが。

cryptsetup で LUKSしているとOS_PROBEする前にかならず cryptsetup open をしておかないといけないのでとても面倒である。

efiの場合はすごく簡単

EFIの場合は、次に起動するOSを選ぶのが簡単。

ブートの番号を指定して next boot に指定するだけ。

efibootmgr --bootnext 0002

ブートの番号は、次のように簡単に確認できる。

efibootmgr
BootCurrent: 0004
Timeout: 1 seconds
BootOrder: 0004,0005,0001,0002,0003
Boot0001* UEFI:CD/DVD Drive
Boot0002* UEFI:Removable Device
Boot0003* UEFI:Network Device
Boot0004* Ubuntu
Boot0005* Debian

デフォルトのブート順を変更することも可能

efibootmgr -o 0005,0004,0001

順序を簡単に触れるし確実に変更ができる。

同じOSをデュアルブートするのが難しい。

ところが、同じ名前のOSがあるとこれが面倒になる。

またEFIパーティション(ESP)が一つだけだと、前述の通り上書きされてしまう。よってESPが2つ必要になるが、それでもやっぱりめんどくさい。

EFIでOS選択起動するのであれば、ESPを複数用意する。

grubからOS選択起動するのであれば、OS更新時に順番が変わってしまう。

EFIBIOS/UEFIからも閲覧できる

EFIを設定ミスって消しちゃったたりした場合。 ESPはBIOS/UEFIから、フォルダを探索して、指定することができる。

このとき、次のファイルがあるが shimx64.efi を選ぶのが一般的である。

BOOTX64.CSV  fbx64.efi  grub.cfg  grubx64.efi  mmx64.efi  shimx64.efi

shimx64.efi は署名チェックのブートなので今どきはこれを使う。

  • shimx64 はセキュアブート用。通常はこっちを使う
  • grubx64.efiは非セキュアブート用、CSM無効でセキュアブート無効ときに使う。
  • CSM有効にするとBIOS互換(Legacy)になるので、ESP(EFI system partition)が使われない。

起動したら、grub-install で再度作り直し。

grub.cfg は grub 起動時に、読み込まれる

takuya@debian:~$ sudo cat /boot/efi/EFI/debian/grub.cfg
search.fs_uuid daf1dc18-4abc-4d18-b2ee-6996b190e96e root hd0,gpt4
set prefix=($root)'/boot/grub'
configfile $prefix/grub.cfg

grubgrub を探す。EFIからGrubを起動してその設定に従って、/boot/grubをさがしそっちのGrubに処理を移す。再帰的なのでちょっとわかりにくい。

ESPを作るときの謎

これはESPを作っていて謎だったんだけど

以下のファイルが無いとUEFIが反応しなかった。

boot/efi/EFI/BOOT/
boot/efi/EFI/BOOT/BOOTX64.EFI
boot/efi/EFI/BOOT/fbx64.efi
boot/efi/EFI/BOOT/mmx64.efi

initramfsからブートするする

initramfsで止まったときも、自分で何とかすることができる。

(initramfs) cryptsetup luksOpen /dev/sda3 dm_crypt-0
(initramfs) mount /dev/mapper/vg-lv /root
(initramfs) exec swich_root /root /sbin/init

結論、OSを分ける。

電車での移動時などに、散々考えた結果、Ubuntuデュアルブートは非常にめんどくさい。DebianDebianでも同じ。同じディストリビューションデュアルブートは超めんどくさいという結論に達した。

そこで、ESPを1つににして、debian / ubuntuデュアルブートにした。

デュアルブートはちょっと便利。

デュアルブートを仕掛けておくと、任意のタイミングでOSを切り替えて起動ができる

これで、確実に指定したOSから起動するようになる。

efibootmgr --bootnext 0002

チェックディスクを掛けたいときや、ディスクの容量変更したいときに、メンテナンスにはデュアルブートを仕掛けておくと、リモートから行えるので便利だと思う。

remount で read only にしてもいいと思うんだけど、docker や lxc や systemdが複雑にマウントを使ってる現代のLinuxで read only で動かすのは超めんどくさい。

ちょっとディスクのリサイズをしたいときにUSBをぶっ刺しに行くのも面倒だし、リモートからSSHだけで操作が完結するようなメンテナンス方法を模索してるが、シリアルコンソールが使えない場合はデュアルブートがベターなんだろうなと思ってる。

btrfsを使う前に調査。雰囲気で使ってるbtrfsをよく知ろう。

btrfs の練習

僕も、雰囲気でbtrfsを使ってる。

  • サブボリューム
  • スナップショット
  • マウント
  • スクラブ ( scrub )
  • デフラグ
  • バックアップ(send/receive)
  • スパニング(LVM/raid)
  • リサイズ
  • lxc storage

btrfsって多機能なんだぜ?でも、LVM+ext4で事足りるからあまり真剣に向き合って無いので、いい機会なので、使ったことない機能などを試して、自分にとって不明点を明らかにして調べることにした。

btrfs の作成

## パーティション作成
sudo sgdisk -Z /dev/sda
sudo sgdisk -n '0::+1G' /dev/sda
sudo sgdisk -n '0::+10G' /dev/sda
## btrfs 作成
sudo mkfs.btrfs /dev/sda1
sudo mkfs.btrfs /dev/sda2

作成されたことをcheckサブコマンドで確認

sudo btrfs check /dev/sda1
sudo btrfs check /dev/sda2

問題なく作成された

found 147456 bytes used, no error found

実験用に作る

実験するだけなら、適当なファイルを作って、フォーマットしてマウントすれば良い。

fallocate -l 128M sample.btrfs.img
mkfs.btrfs sample.btrfs.img
sudo mount -t auto sample.btrfs.img mnt

またはLoopバックデバイスを経由する

fallocate -l 128M sample.btrfs.img
sudo losetup -f sample.btrfs.img
DEV=$(sudo losetup | grep sample | cut -d ' ' -f 1 )
echo $DEV # /dev/loop10
mkfs.btrfs ${DEV}
sudo mount ${DEV} mnt

マウント・マウント解除

btrfs をマウントしてみる。

sudo mount /dev/sda1 /mnt

マウントのチェック

findmnt /mnt

正常にマウントされている。

TARGET SOURCE    FSTYPE OPTIONS
/mnt   /dev/sda1 btrfs  rw,relatime,ssd,space_cache=v2,subvolid=5,subvol=/

中身を見てみる。ext4 みたいなlost foundはない。root権限になっている

# ls -l /mnt
total 0
# ls -ld /mnt
drwxr-xr-x 1 root root 0  5月 19 16:07 /mnt
# touch /mnt/hello

問題なく作成された。

マウントを解除

sudo umount /mnt

サブボリュームを使う

最初にマウントする。

sudo mount /dev/sda1 /mnt

サブボリュームを一覧する

sudo btrfs  subvolume  list /mnt

サブボリュームを作る。

sudo btrfs  subvolume  create /mnt/subA

サブボリュームを一覧する

sudo btrfs  subvolume  list /mnt

実行結果の例

ID 256 gen 15 top level 5 path subA

サブボリューム内部にファイルを作る

sudo touch /mnt/subA/in-sub-a.txt

サブボリュームをマウントする(名前で指定)

sudo mount -o subvol=/subA /dev/sda1 /mnt

サブボリュームをマウントする(IDで指定)

sudo mount -o subvolid=256 /dev/sda1 /mnt

マウント結果の例。サブボリューム内部だけが見える。

ls /mnt
in-sub-a.txt

サブボリュームを削除する

sudo btrfs  subvolume delete /mnt/subA/

サブボリュームは、単なるディレクトリ?

単なるディレクトリとして考えても問題ないと思う。サブボリュームはディレクトリのすごいヤツ

ディレクトリと同名のサブボリュームは作成できない。

例:ディレクトリが存在するとサブボリュームが作れない。

# ls /mnt
dirA  hello
# btrfs  subvolume  create /mnt/dirA
ERROR: target path already exists: /mnt/dirA

ディレクトリと競合するのがSubvolumeである。ディレクトリの一種と考えておくといいだろう。(我々利用者から見るとディレクトリにしかみえない)

ディレクトリをサブボリュームに変換する方法

サブボリュームがディレクトリと同じように見えるとしても、サブボリュームとディレクトリの相互変換はできない。かわりに、名前をつけてrsyncでデータを抜き出すしか無いかも。

サブボリュームを移動する(名前変更)

上記で述べたように、サブボリュームは単なるディレクトリとして扱える。

サブボリュームの名前の変更(移動)は単純にmvコマンドを使う。ディレクトリとして扱うだけ

sudo mv subA/ subAA

実行結果の例。

$ sudo btrfs subv list  /mnt/
ID 257 gen 24 top level 5 path subAA

上記のように、mv コマンドでディレクトリ名変更でサブボリューム名が変更されている。ディレクトリ操作である。「我々利用者から見るとディレクトリと思っておけば良い。」という理由である。

スナップショットを使う。

スナップショットは、サブボリューム。

btrfs subvolume snapshot /mnt/src_name/ /mnt/snap_name
sudo muont /dev/sda1 /mnt
sudo touch /mnt/s1/sodium.txt

動作サンプル

sudo btrfs subvolume snapshot /mnt/s1/ /mnt/s1.snap
Create a snapshot of '/mnt/s1/' in '/mnt/s1.snap'

スナップショットとの差分を作る

sudo touch /mnt/s1/sodium_hydroxide.txt

スナップショットとオリジナルの差分を見てみる。

sudo ls /mnt/s1*/s*.txt

スナップショット時点と違いが出ても保持されているとわかる。

sudo ls -l /mnt/s1*/s*.txt
-rw-r--r-- 1 root root 0  5月 19 21:21 /mnt/s1.snap/sodium.txt
-rw-r--r-- 1 root root 0  5月 19 21:21 /mnt/s1/sodium.txt
-rw-r--r-- 1 root root 0  5月 19 21:24 /mnt/s1/sodium_hydroxide.txt

スナップショットは単なるサブボリュームである。

読み取り専用モード

スナップショットをバックアップの目的で作るならread onlyで作る。

## read only
sudo btrfs subvolume snapshot -r /mnt/s1/ /mnt/s1.snap.ro
## read write
sudo btrfs subvolume snapshot -r /mnt/s1/ /mnt/s1.snap.rw

read only は次で使う。send / recieveで必要

send/recieve

send はbtrfsの中身を取り出してストリームにする。

receiveは ストリームを受け取って btrfs に書き込む

send / recieive の例

sudo btrfs send /mnt/s1.snap.ro/ | sudo btrfs receive /mnt/subB

実行後のスナップショット

sudo btrfs sub list /mnt
ID 256 gen 13 top level 5 path subA
ID 257 gen 20 top level 5 path subB
ID 258 gen 13 top level 5 path s1.snap.ro
ID 260 gen 21 top level 257 path subB/s1.snap.ro

サブボリュームの内部にファイルがコピーされた

send はファイルに書き出せる

sudo btrfs send /mnt/s1.snap.ro/ | cat - > out

書き出したファイルを見てみると。ストリームである。

hexdump -C out
00000000  62 74 72 66 73 2d 73 74  72 65 61 6d 00 01 00 00  |btrfs-stream....|
00000010  00 2e 00 00 00 01 00 a8  c3 e9 a1 0f 00 0a 00 73  |...............s|
00000020  31 2e 73 6e 61 70 2e 72  6f 01 00 10 00 93 7e dc  |1.snap.ro.....~.|
00000030  42 ca d0 f8 4f ae 65 e3  38 89 f1 69 9f 02 00 08  |B...O.e.8..i....|
00000040  00 0b 00 00 00 00 00 00  00 1c 00 00 00 13 00 02  |................|
00000050  37 2d 8c 0f 00 00 00 06  00 08 00 00 00 00 00 00  |7-..............|
00000060  00 00 00 07 00 08 00 00  00 00 00 00 00 00 00 10  |................|
00000070  00 00 00 12 00 59 55 75  35 0f 00 00 00 05 00 08  |.....YUu5.......|
00000080  00 ed 01 00 00 00 00 00  00 34 00 00 00 14 00 98  |.........4......|

このことから、tar などと同じく、パイプ転送ができることがわかる。

btrfs send /mnt/s1.snap.ro/ | ssh root@127.0.0.1 'btrfs receive /mnt/subC'

実行後のサブボリューム

sudo btrfs sub list /mnt
ID 256 gen 13 top level 5 path subA
ID 257 gen 20 top level 5 path subB
ID 258 gen 13 top level 5 path s1.snap.ro
ID 260 gen 23 top level 257 path subB/s1.snap.ro
ID 261 gen 25 top level 5 path subC
ID 262 gen 26 top level 261 path subC/s1.snap.ro

サブボリューム内部に、スナップショットが転送された。

別のパーティションでも試す。

別のディスクのパーティションへ転送してみたらどうなるのか。

sudo mount -t btrfs /dev/sdb1 /mnt2

別のパーティションへ転送する

btrfs send /mnt/s1.snap.ro/ | sudo btrfs receive /mnt2/an1/

転送結果から、無事に転送されたことがわかる。

sudo btrfs su list /mnt2/an1/
ID 256 gen 8 top level 5 path an1
ID 257 gen 11 top level 256 path s1.snap.ro

このことから、send/receiveでバックアップやdumpが取れる。

btrfs はサブボリュームをメインに使って、サブボリューム単位でスナップショットを取って使うのが良さそう。

ext4 のように、dump fs /restore fs が存在しないが、btrfs では send / reciveを使って同様っぽい

btrfs subvolume を省略する

subvolume は長いので、もう少し短くできる。

sudo btrfs su list  
sudo btrfs sub list 
sudo btrfs subv list
sudo btrfs subvol list 
sudo btrfs subvolu list
sudo btrfs subvolume list

scrub send subvolumeと3つあるので、btrfs s のように究極の省略はできない。

メンテナンス defrag / scrub / check

デフラグ(マウント中)

sudo btrfs filesystem defragment -r /mnt

チェック(アンマウント必須)

チェックコマンドは、ディスクの修正を兼ねている.

sudo umount /dev/sdb
sudo btrfs check /dev/sdb

ちょっとおかしい程度なら、 check して scan したら治ってることが多い。 repairしたこと無いし、使うことがない

スクラブ

スクラブ、時間がかかるので start -> status でfinishチェック

sudo btrfs scrub start /dev/sdb
sudo btrfs scrub status /dev/sdb

空き容量の確認 ( usage )

sudo btrfs filesystem usage /mnt

使用容量の確認

sudo btrfs filesystem df /mnt
sudo btrfs filesystem du /mnt

df / df より、usageをつかことが多くて詳しいことは知らない。

ssd trim

マウント時に、discardオプションを付けておくと、fstrimコマンドが使えるようになる。

mount -o discard 

discardには並列(非同期)があって、そちらのオプション( -o discard=async )も使えるらしい

LVM的に使う

btrfs では、lvmのように、複数のディスクにまたがったファイルシステムが作成可能です。

2つのディスクから1つのbtrfsを作る

2つのディスクを作る。

## 1 つめ
fallocate -l 128M sample.btrfs.img
sudo losetup -f sample.btrfs.img # /dev/loop10
## 2つ目
fallocate -l 128M sample2.btrfs.img
sudo losetup -f sample.btrfs.img # /dev/loop10

2つのディスクからBtrfsを作る。

mkfs.btrfs -m single /dev/loop10 /dev/loop11 

マウントすると、2倍の容量になってる。

$ sudo mount /dev/loop11 /mnt
$ df -h /mnt
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop10     256M  3.4M  242M   2% /mnt

マウントは、2つどちらをを指定しても同じようです。

ディスクの追加

LVMの特徴といえば、ディスクを柔軟に使えることだろう。btrfsでも同じようなことができる。

1本目のディスクを作成

sudo mkfs.btrfs -f /dev/loop10
sudo mount /dev/loop10 /mnt

2本目のディスクを追加

sudo btrfs device add /dev/loop11 /mnt

追加後の結果を表示(singleになっていて、容量が増えたことがわかる。)

$ sudo btrfs filesystem df /mnt
Data, single: total=8.00MiB, used=0.00B
$ sudo btrfs filesystem usage /mnt
Overall:
    Device size:                 256.00MiB

2本目のディスクを削除

sudo btrfs device remove /dev/loop11 /mnt

LVMはPVをremoveするときに、pvmove で別ディスクにデータを移して削除してくれるし、それはオンラインでしかもマウント継続中のまま行えたと思う。btrfsでも同じようだ。空き容量があれば、LVMみたいに自動的に移動してくれるっぽい。

ディスクを取り外す。(あまり使わない)

sudo umount /mnt
sudo btrfs device scan --forget /dev/loop11
sudo btrfs device scan
sudo mount /dev/loop10 /mnt

removeと間違えそうだ。使うときは、ディスクを取り出してミラーリングの別ディスクに付け替えるときだろうか。何も考えずに forgetしたら ディスクにアクセスできなくなった。scanをしたらディスクが再びマウント可能になった。

raid の場合

Raid1(ミラーリングで作る)

sudo mkfs.btrfs -d raid1 /dev/loop10 /dev/loop11
sudo mkfs.btrfs  /dev/loop10 /dev/loop11

Raid0(ストライピング)

sudo mkfs.btrfs -m raid0 /dev/loop10 /dev/loop11

シンプル(スパニング)

sudo mkfs.btrfs -m single /dev/loop10 /dev/loop11

ギョーカイでは常識なRAIDだけど、専用カードによるRAIDはよく壊れるので私は信用してない。ソフトウェアなら信じてもいいのだけど、Btrfsではどうなのでしょうね。Raid5/6には重大なバグがあるとかだから、raid0/1も過度に信用して良いものか迷う。

raidメタデータ

raidは、メタデータ-m と実データ-d について指定する。デフォルトはデータシングルメタデータdup

デフォルトの確認(オプションなし

$ sudo mkfs.btrfs /dev/loop10
$ sudo mount /dev/loop10 /mnt
$ sudo btrfs filesystem df /mnt
Data, single: total=8.00MiB, used=0.00B
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=32.00MiB, used=128.00KiB
GlobalReserve, single: total=3.25MiB, used=0.00B

デフォルトの確認( -m single )

$ sudo mkfs.btrfs -f -m single /dev/loop10
$ sudo btrfs filesystem df /mnt
Data, single: total=8.00MiB, used=0.00B
System, single: total=4.00MiB, used=16.00KiB
Metadata, single: total=8.00MiB, used=128.00KiB
GlobalReserve, single: total=3.25MiB, used=0.00B

デフォルトの確認-d single

$ sudo mkfs.btrfs -f -m single /dev/loop10
$ sudo mount /dev/loop10 /mnt
$ sudo btrfs filesystem df /mnt
Data, single: total=8.00MiB, used=0.00B
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=32.00MiB, used=128.00KiB
GlobalReserve, single: total=3.25MiB, used=0.00B

上記の結果からわかる通り、メタデータとデータがRaidになる。メタデータはデフォルトがDUPであり、実データは single であるとわかる。

このことは、mkfs.btrfsの実行結果にも出てくる。(たぶん最初のうちは読み飛ばしてると思う)

mkfs.btrfsの実行結果 の例

sudo mkfs.btrfs -f  /dev/loop10
btrfs-progs v5.16.2
See http://btrfs.wiki.kernel.org for more information.

Performing full device TRIM /dev/loop10 (128.00MiB) ...
NOTE: several default settings have changed in version 5.15, please make sure
      this does not affect your deployments:
      - DUP for metadata (-m dup)
      - enabled no-holes (-O no-holes)
      - enabled free-space-tree (-R free-space-tree)

-m dupの記述があるのがわかる。

  - DUP for metadata **(-m dup)**

RAIDは上記のことを踏まえた上で、指定して使う必要がある。

私はRAIDに愛情が全く無いのでこれ以上は調べてない。

btrfs の特徴

Btrfs には従来のExt2などと違う特徴を持ってる。

  • 遅延書き込み
  • 自動デフラグ
  • 自動圧縮
  • 重複排除

自動圧縮・自動デフラグ・遅延書込についてはマウントのオプションで指定する。

重複排除

重複排除 はbtrfs の特徴である。MacのAPFSも同じ特徴を備えている。

ただし、Btrfsではコピー時重複排除がメインで、CoW ( copy on write )の機能がある。

btrfsでは重複排除だからといって、同じファイルを別名で作成した場合は重複排除はされない。(この機能は、将来の夢であり、プラグインをインストールすると使えるらしい。)

cp --reflink

commit 指定(遅延書き込み)

遅延書き込みについては参考資料

これはext4 と同じ。

LABEL=my / ext4  defaults,discard,errors=remount-ro,commit=300 0 1
LABEL=s0 / btrfs defaults,discard,errors=remount-ro,commit=300 0 1

commit 間隔はデフォルト30秒なので、それ以上に間隔を開けておけば、小さなファイルの見かけ上の速度向上はみられる。

SDカードなどは600秒とかにしておけば、書き込みタイミングをずらせるので寿命に寄与するかもしれない(そもそも、書込総量を減らしたり、無駄データを捨てたほうが圧倒的に寿命を長くするのだが、commitで書き込みをズラすと、更新箇所の上書き更新といった不要な書込みを減らせて寿命に寄与するかと)

自動圧縮( compress )

btrfs ではファイルを透過的に圧縮できる。ext4 にはこの機能がない。デフォルトはOFFである。

compress=zstd

zlib, lzo, zstd の3つから選ぶ。

何でもかんでも圧縮するわけでもなく、ファイルの先頭だけで圧縮を試して、圧縮が効果が上がると判明したときだけ、圧縮が行われる。チェックを飛ばして全ファイルを圧縮するオプションcompress-forceもある。

compress-force=zstdの、forceを使うと、速くなることがあるらしい

NTFSはすべて圧縮するので、compress-force の動作イメージに似ているかもしれない。

SDカードの場合は、このCompressオプションで圧縮してやると、データ総量が抑えられるので、SD寿命の延命に寄与することだろう。

自動デフラグ (autodefrag)

defaults,autodefrag

ただし、自動デフラグ無限ループになる可能性があり非推奨らしい

リサイズ

XFSは、容量の縮小ができない、なので不採用です。btrfsではどうなのでしょうか。

btrfsでもリサイズはサポートされています。ext4同じように扱えます。

マウントした状態で扱います。

# 1G にする
sudo btrfs filesystem resize 1G /mnt
# +5M にする
sudo btrfs filesystem resize +5M /mnt
# -15Mする
sudo btrfs filesystem resize -15M /mnt

このようにファイルシステムの実サイズを扱えるため、別途容量が必要になっても対応できます。

冒頭でXFSをDisりましたが、縮小ってあんまり使わない。容量が小さい別ディスクにコピーするときで事前に容量を揃えられない程度の問題で、実は必要ないんですよね。XFSが割り切った縮小って、なくても問題ないんですよね。縮小ができないXFSで困るのは、小さいディスクにコピーしたときにUUIDが変わっちゃうことなんですよね。同じラベルをつけ直すのが手間というか。fstab書き換えが面倒というか。その点においてbtrfsでは困りません。良かったです。

btrfsでは制限があって。/dev/loopのリサイズができない。

$ sudo mount sample.btrfs.img  /mnt
$ sudo btrfs filesystem resize -5M /mnt
Resize device id 1 (/dev/loop10) from 128.00MiB to 123.00MiB
ERROR: unable to resize '/mnt': Invalid argument

上記のように、ループバックデバイス経由で、イメージファイルを扱ってる場合は、リサイズができない。nbdではどうなのでしょうか。試してない。

lxd で btrfs を使うと

ubuntu にはLXD/LXCのコンテナが標準で入ってる。そのLXDのストレージにbtrfs や zfs使う事ができる。これらは共通のの機能があって、スナップショットが自動的作られるそうだ。

lxd を btrfs で使うと、次の機能が使える

  • snapshots.expiry 全自動で削除
  • snapshots.schedule 全自動スナップショット
  • snapshots.pattern 自動生成の名前規則

ふぁーすごい

ただ、scrubが必要など、btrfs のLXDでは、トラブル多かったのでメインに採用するのは避けたい。といってもzfsは扱いにくいしエラー時のリカバリが大変だったのでそれはそれで使いづらい・・・個人的にはbtrfsかなぁ

man pages

マニュアルの呼び出しに、少し癖があって

man btrfs-filesystem
man btrfs-send
man btrfs-scrub

btrfsのサブコマンドをハイフンにしてマニュアルを確認できる。細かいオプションが掲載されてて便利。

オンラインで見たいときは https://btrfs.readthedocs.io/en/latest/ が便利。

filesystemの場合はここを見ると読みやすい。

気づいたこと

btrfs はソコソコ完成していて、ext4 と同じように使える。

LVMの機能もあるので、ext4+lvmと同等なのが改めて理解できた。

サブボリュームは、ディレクトリってのが面白かった。

send / receive でパイプ転送できるってのが良い。でもスナップショットしか転送できないっぽいのが困る。

それなりに安定してるし、機能を見ると、btrfsで十分じゃんって思ってしまった。

次にインストールするときに、採用してみたい。

そうえいば、ファイルシステムといえば速度比較だけど。速度に興味がないので言及してない。高速nvme時代に、速度を気にする必要性に疑問です。

btrfsの速度比較とか言ってもlvm有無で変わるので信用してはいけないね。 その速度比較してるファイルシステムはスナップショット取れるんですか?圧縮できるんですか?と煽りたくなるね。

参考資料

ファイルシステムの本

grubメニューからLinux起動の手順をみて、Grubのコマンドを扱う方法を調べる。

GrubメニューでLinuxをコマンドから起動する。

めったに使わないし、そこまで重要でもない。ので、いつも忘れてググってる。最近は古の記事が削除されて、検索に苦労するのでメモとしてまとめる。

コマンドライン

GRUBメニューからキー c を押すとGRUBのコマンドメニューに入ることができる、

grub のコマンドをが起動したところ

環境変数を設定して

linuxをロードして起動

コマンドで起動する

linuxを指定して、initrdを指定して、bootする

grub> set root=(hd0 ,gpt4) 
grub> linux /boot/vmlinuz root=/dev/sda4 
grub> initrd /boot/initrd.img 
grub> boot

lsなどでページを送りにする

そのままでは、スクロールなしに出力が流れてしまうので、ページャーを使う。

grub> set pager=1          

root=/ HDDを決める

grub> set root=(hd0 ,gpt4) 

linuxカーネルと ルートファイルシステム指定

grub> linux /boot/vmlinuz root=/dev/sda4 

起動ram指定

grub> initrd /boot/initrd.img 

起動

grub> boot

実際に行うと

ファイル名が微妙に異なったり、TABで補完などで上記のスクショのようにならないけど、Ubuntuはリンクを持ってるので、上記のスクショのようにすごくきれいになる。

DebianをBootした場合

HDDを探す

HDDとパーティションを探すときに、lsを叩くだけで、HDDの一覧が見える。GPTが有効になってると、GPTのディスク・パーティションも見えた。

起動メニューを編集

GRUBのメニューでeを押すと、起動メニューを編集することができる。

何が書いてるのか最初はわからないと思うが、上記のスクショのように「コマンド」から「boot」を体験してみるとよく分かる。

すでに見てきた通り、rootをどこにして、linuxをロードして、initrd をロードして bootを叩くだけである。

ディスクコピーやパーティション構成や順番を変えた後に、起動メニューから起動しないとき、編集から(hd0,gpt1)のようなディスク指定を変えれば動く。ディスクの確認は、前に紹介した通りコマンドに入って ls でディスク番号が確認できる。

insmod と lsmod

insmod でgrubに登録されているモジュールをロードできる。

モジュールは手作業でロードすることができる。

grub> lsmod
grub> insmod  usbserial_pl2303

lsmod がスクロールしてしまうときは、pagerを設定すると良い。

grub> set pager=1          

lsmod すると見えるのは grub作られたときにHDDにファイルが作成されている

Linux起動後は、通常シェルから確認できる。

ls -l /boot/grub/x86_64-efi/
total 3500
-rw-r--r-- 1 root root  15600 May 20 03:00 acpi.mod
-rw-r--r-- 1 root root  22944 May 20 03:00 ahci.mod
-rw-r--r-- 1 root root    824 May 20 03:00 all_video.mod
...(略)

grub は起動するけど起動メニューが使えないとき

HDDをディスクコピーして、grub は起動するけど、起動メニューに設定された起動設定が起動しないときに、上記のスクショのように、自分で linux ...initrd ...を指定して起動する

無事に起動したら

grub-install コマンドを使って、HDDにgrubをインストールし直す。

sudo grub-install /dev/sda

update-grubのコマンドを使って、起動メニュー起動設定を作り直してあげる。

sudo update-grub

これらのコマンドを使うと、パーティションを移動することができる。

起動しないときの大半は、hd0,gpt0が hd1,gpt4とかに変わっちゃったとかだからね。

grubすら起動しないとき

ディスクはあるけど、grubすら起動しないときは、GrubだけインストールしたUSBディスクを作れば良い

Rufusで手軽にGRUBを作ることができる。

または、他のCD-ROMやUSBデバイスで起動してGRUBメニューで止める。GRUBコマンドに移行し、コマンドで起動するLinuxを指定すれば良い。

EFIgrubを指定する

EFIは起動パーティションであり、EFIフォルダが有る。EFI内部にGrubがあり、そのGrubから、最初に見つかったGrubが起動して処理が移る。

いまどきデュアルブートなんてやらないよね、と思ってた

いまどきデュアルブートをすることなんて無いと思うじゃん。WindowsUbuntuデュアルブートなんてEFIで快適に使えるとしても、めんどくさいじゃん。

ただ、サーバーのメンテナンスをリモートからやるときに、デュアルブートだと別OSで起動できるので、リモートからルートファイルシステムをチェックディスクしたり、resizefs でリサイズすることができて、デュアルブートは便利かもしれない。

OpenVPNの管理画面をインストールしてVPNを快適に使う。

OpenVPNって面倒なんですよね。

証明書を使ったりCAを作ったり、プロファイルを作ったり面倒なんですよね。

管理画面が公式から提供されていた。

OpenVPN Access Server という名前(通称 OpenVPN-AS ) で管理画面WebUIが提供されていた。 これで、OpenVPNVPN管理できるじゃん。

インストールの手順も公式サイトに記述がある。

Setting Up OpenVPN Access Server On Ubuntu

無料フリー版だと2接続(セッション)に限られるが、管理が楽であるなら使ってみても良いかも

準備

アカウント作って、ログイン

フリー版のサブスクリプションを申し込む

ライセンス・キーをコピーして保存しておく

OpenVPN Access Serverをインストールする。

raspiにインストールして試す

ssh raspi-ubuntu
lxc launch ubuntu:22.04 ovpn
lxc exec ovpn -- bash -c 'apt update && apt upgrade -y'

lxc へホスト側と同じNICで同じLANをMacVlanで使わせる。

lxc config device add ovpn mvlan0 nic nictype=macvlan parent=eth0
lxc exec ovpn dhclient -v eth1

OpenVpnサーバーのインストール lxc コンテナ内部へ入る。

lxc shell ovpn

必要なパッケージをインストール

apt update && apt -y install ca-certificates wget net-tools gnupg
wget https://as-repository.openvpn.net/as-repo-public.asc -qO /etc/apt/trusted.gpg.d/as-repository.asc
truncate -s0 /etc/apt/sources.list.d/openvpn-as-repo.list
## arm用
echo "deb [arch=arm64 signed-by=/etc/apt/trusted.gpg.d/as-repository.asc] http://as-repository.openvpn.net/as/debian jammy main" >>/etc/apt/sources.list.d/openvpn-as-repo.list
## x86用
echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/as-repository.asc] http://as-repository.openvpn.net/as/debian jammy main" >> /etc/apt/sources.list.d/openvpn-as-repo.list

apt update && apt -y install openvpn-as

日付と言語を設定

dpkg-reconfigure tzdata 
 dpkg-reconfigure locales

APTのURLははOpenVPNにアカウントを作れば見える。

インストールが完了した

初期パスワードなどが表示される。

キーを登録してアクティベーション

OpenVPN利用者アカウントを作る

アカウント名とパスワードを登録する。(離れているので注意)

ネットワークのアクセスを許可する。(VPNを超えてLANネットワークやグローバルにアクセスできるように)

アクセス許可をつけないと、VPNホスト以外に通信ができなかった。

コマンドから管理

コマンドから再起動

systemctl restart openvpnas.service

または、専用コマンドから管理。

/usr/local/openvpn_as/scripts/sacli  --help

プロセスが動く

次のようなプロセスが動く

openvpn+    4793  0.0  1.0 125268 81252 ?        S    May22   1:42  \_ /usr/bin/python3 -c from pyovpn.cserv.wserv_entry import start ; start() -no -u openvpn_as -g openvpn_as --pidfile /usr/local/open
root        4794  0.0  0.6  62096 50168 ?        S    May22   0:08  \_ /usr/bin/python3 -c from pyovpn.log.logworker import start ; start()
root        4823  0.0  0.4  46888 35616 ?        S    May22   0:00  \_ /usr/bin/python3 -c from pyovpn.sagent.iptworker import start6 ; start6()
root        4824  0.0  0.4  46912 35696 ?        S    May22   0:53  \_ /usr/bin/python3 -c from pyovpn.sagent.iptworker import start ; start()
root        4861  0.0  0.0   3620  2276 ?        S    May22   0:00  |   \_ iptables-restore -n
openvpn+    4827  0.0  0.0   9476  7800 ?        S    May22   0:00  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4828  0.0  0.0   8940  2900 ?        S    May22   0:00  |   \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4837  0.0  0.0   9208  7496 ?        S    May22   0:00  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4839  0.0  0.0   8940  1484 ?        S    May22   0:00  |   \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4845  0.0  0.0   9208  7504 ?        S    May22   0:00  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4847  0.0  0.0   8940  1484 ?        S    May22   0:00  |   \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4853  0.0  0.0   9208  7488 ?        S    May22   0:00  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4855  0.0  0.0   8940  1484 ?        S    May22   0:00  |   \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4862  0.0  0.0   9356  7808 ?        S    May22   0:01  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4869  0.0  0.0   9100  7424 ?        S    May22   0:01  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4876  0.0  0.0   9100  7408 ?        S    May22   0:01  \_ openvpn-openssl --errors-to-stderr --config stdin
openvpn+    4883  0.0  0.0   9100  7424 ?        S    May22   0:01  \_ openvpn-openssl --errors-to-stderr --config stdin

リッスンポートなどを適宜変更する

iOSなどの接続設定(プロファイル)は自動生成されるので、そのために必要な設定をする。

Hostname or IP Addressが、OVPNプロファイルに設定される名前

Port number: が、OVPNプロファイルに設定されるポートとこのサーバがリッスンするポート

ここで設定した、サーバー名:ポート(TCP)が、iOSから接続するときのプロファイルになる。(プロファイル生成用とリッスンポートは同じ設定になり、分割した設定ができない)

ポート転送などを行う。

ルーターでポート転送を行う。

ルーターでポート・フォワーディングやDNATをして、接続できるようにする

プロファイルの確認

管理画面(/admin)ではなくユーザー用CWEB画面( / )にアクセスする。

ログインしてプロファイルを取り出す

プロファイルをダウンロードして

中身を確認する

意図したとおりに、接続先アドレスとポートが設定されていたらOK

iOSを準備する

iOSを取り出して、OpenVPNのアプリを入れる

インストールしたサーバにアクセスしてログインして、プロファイルをダウンロード

シェアシート(↑)を開いて、OpenVPNを指定する。

OpenVPNにプロファイルが取り込まれる。

パスワードは、作成したアカウントのパスワードを入れる。

プロファイル追加時にiOSVPN追加のためiOSのロックパスワードが要求される。

これで、あとiOSVPN管理画面から接続するだけ。

OpenVPNのアプリではなく、iOSの設定(VPN)から接続する方がいいみたい。

速度を試してみる。

4Gでau LTE が 180Mbps

Wireguardで 70-80Mbps

OpenVPN経由で20-30Mbps

同条件での速度測定ではなく、WireguardはUDPOpenVPNTCPなので、遅いのは仕方ないのですが。それでもスループットは期待したほどでない。。。

docker 版

linuxserver.ioによるDocker版もあります。こちらの方が動かすには楽ちんかもしれない。ただし、もうメンテナンスされてないので、注意が必要です。

私は、lxc で一通り試した後にDockerにしましたが、メンテナンスされてないと気づいて焦ってます。

sslh と組み合わせ

sshlと組み合わせることで、443/tcphttpsOpenVPNを共用することができて便利です。

FWでのVPNの疎通を考えると、問題なく使えるのではないでしょうか。

certbotでDNS認証(debian/ubuntuのaptを使った簡単管理)

certbit のDNS01 認証のメリット

DNS認証すると、ワイルドカード証明書が使えるようになる。

DNSで認証すると、WEBサーバーのポートを開けなくて良い。

ただし、TXTレコードを使う必要がある。プラグインが別途必要

プラグインのインストールする

最近のDebian/UbuntucertbotプラグインがAPTで提供されており、各種DNSサービスに対応している。

sudo apt install python3-certbot-dns-cloudflare

現在確認しているDNSプラグイン

takuya@:~$ apt list *certbot-dns*
一覧表示... 完了
python-certbot-dns-cloudflare-doc/stable 1.6.0-1 all
python-certbot-dns-digitalocean-doc/stable 1.3.0-1 all
python-certbot-dns-dnsimple-doc/stable 1.3.0-1 all
python-certbot-dns-gehirn-doc/stable 1.3.0-1 all
python-certbot-dns-google-doc/stable 1.11.0-1 all
python-certbot-dns-linode-doc/stable 1.7.0-1 all
python-certbot-dns-ovh-doc/stable 1.3.0-1 all
python-certbot-dns-rfc2136-doc/stable 1.10.1-1 all
python-certbot-dns-route53-doc/stable 1.3.0-1 all
python-certbot-dns-sakuracloud-doc/stable 1.3.0-1 all
python3-certbot-dns-cloudflare/stable,now 1.6.0-1 all [インストール済み]
python3-certbot-dns-digitalocean/stable 1.3.0-1 all
python3-certbot-dns-dnsimple/stable 1.3.0-1 all
python3-certbot-dns-gandi/stable 1.2.5-3 all
python3-certbot-dns-gehirn/stable 1.3.0-1 all
python3-certbot-dns-google/stable 1.11.0-1 all
python3-certbot-dns-linode/stable 1.7.0-1 all
python3-certbot-dns-ovh/stable 1.3.0-1 all
python3-certbot-dns-rfc2136/stable 1.10.1-1 all
python3-certbot-dns-route53/stable 1.3.0-1 all
python3-certbot-dns-sakuracloud/stable 1.3.0-1 all

cloudflare で試してみる。

プラグインをインストールする

sudo apt install python3-certbot-dns-cloudflare

root に昇格しつつ、cloudflareのDNS-APIを使ってTXTレコードで認証を行う。

sudo su 
mydomain=ovpn.example.biz
certbot=$(which certbot)
$certbot  \
  certonly \
 -n \
 --cert-name $mydomain \
 --dns-cloudflare \
 --dns-cloudflare-credentials /etc/letsencrypt/cloudflareapi.cfg \
  -d $mydomain \
  -d "*.$mydomain"  \

任意のパスで cloudflareのAPIキーを設置する 。私は、/etc/letsencrypt/cloudflareapi.cfgとした /etc/letsencrypt/cloudflareapi.cfg

dns_cloudflare_email =  admin@example.tld
dns_cloudflare_api_key = xxx4784xxxxx

CloudflareのAPIキーには2種類のAPIキーがある。以前からある。メールアドレスとペアになる以前からのキーと、権限を設定したAPIトークンである。上記は、APIキーの場合。

APIトークンを使う場合は、管理画面から編集権限を付与し、キーをファイルに書く(詳しくは以前の記事

ファイルにはキーのみを書く

/etc/letsencrypt/cloudflare-key.cfg

XXXADFDFXXX

Apt版の特徴(systemd)

apt 版を使うと、自動更新用のsystemdユニットが同時にインストールされる。

systemctl status certbot.timer
systemctl status certbot.service

これはとても便利である。

ただ、不要な証明書をいつまでも更新し続けるので、不要になったら証明書をちゃんと消しておくという運用が必要になる。

必要に応じてtimer を書き換えたり、停止してcronで自分のスクリプトで置き換えるなどの考慮も必要になる。

更新時の設定

systemd ユニットで自動更新が、certbot renew で行われることがわかったが、renew 時にDNSプラグインを使うことをどこに保存しているのか。それは /etc/letsencrypt/renewal フォルダに証明書名ごとに設定が保存されている。

ls /etc/letsencrypt/renewal

例えば、次のようになっている。

証明書名ごとに設定ファイルが作られており

cat /etc/letsencrypt/renewal/example.tld.conf

ファイルの中身は次のような感じだった。

# renew_before_expiry = 30 days
version = 1.12.0
archive_dir = /etc/letsencrypt/archive/example.tld
cert = /etc/letsencrypt/live/example.tld/cert.pem
privkey = /etc/letsencrypt/live/example.tld/privkey.pem
chain = /etc/letsencrypt/live/example.tld/chain.pem
fullchain = /etc/letsencrypt/live/example.tld/fullchain.pem

# Options used in the renewal process
[renewalparams]
account = xxxxxxxxf6c7xxxxx
authenticator = dns-cloudflare
dns_cloudflare_credentials = /etc/letsencrypt/cloudflareapi.cfg
server = https://acme-v02.api.letsencrypt.org/directory

このように、どのディレクトリに格納されているキーをどのアカウントで更新し、プラグインを使う(authenticator)が設定されていた。

certbotは上記の設定を使うように構成されているようだ。

むかし、自分でプラグインをpipでインストールしていたときから比べて、随分と便利になった。

certbotがOCSPが参照できないエラー(malware判定の謗り)

certbotの更新がエラーになってて、エラーアラートが出て、調べたらOCSPが参照できてなかった。

LetsEncryptのOCSPがブロックされる

調べてみると、他の方でも同様のエラーが発生しており。r3.o.lencr.org の名前解決やr3.o.lencr.orgへの接続がブロックされることがある模様。

LetsEncryptでも、Why is my computer fetching this data? Is it malicious? (これはマルウェアをダウンロードしているのですか?)という質問に対して「決してそんなことはない」と掲載している。

certbotのエラー

r3.o.lencr.org に接続しようとしてエラーになっていた。r3.o.lencr.orgはOCSPを提供するアドレス。

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 699, in urlopen
    httplib_response = self._make_request(
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 394, in _make_request
    conn.request(method, url, **httplib_request_kw)
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 234, in request
    super(HTTPConnection, self).request(method, url, body=body, headers=headers)
  File "/usr/lib/python3.9/http/client.py", line 1255, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1301, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1250, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/usr/lib/python3.9/http/client.py", line 1010, in _send_output
    self.send(msg)
  File "/usr/lib/python3.9/http/client.py", line 950, in send
    self.connect()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 200, in connect
    conn = self._new_conn()
  File "/usr/lib/python3/dist-packages/urllib3/connection.py", line 181, in _new_conn
    raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7f3b524a2cd0>: Failed to establish a new connection: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 439, in send
    resp = conn.urlopen(
  File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 755, in urlopen
    retries = retries.increment(
  File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 574, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPConnectionPool(host='r3.o.lencr.org', port=80): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f3b524a2cd0>: Failed to establish a new connection: [Errno -2] Name or service not known'))

なぜブロックされるのか。

これは、個人的な予想だが、「マルウェアがLEの証明書を使って暗号化通信」をしていたために、r3.o.lencr.orgOCSP問合わせがマルウェア由来通信として認知され、どこかのフィルタに登録されてしまう。さらに、それがコピーされ、またコピーされる。そして今にいたり、r3.o.lencr.orgマルウェア配布URLリストに含まれてしまったのだろう。気づいたときには手遅れで、lencr.orgはマルウェア配布URL、またはC&C(コントロール and コマンド)サーバーとして認知されてしまっているのだろう。と予想している。

LetsEncryptの証明書は「信用できない」などという、証明書事業者の間違った言説や解説サイト、これ後押ししているのかもしれない。

証明書が信用できる仕組みで運営されているのと、証明書の所有者が信用できないのは別の話なのにね。

280blocker のリストでも

我が家でも280blocker のリストにr3.o.lencr.org が含まれてしまっていた(2023-05以降)。これによるDNSブロッキングが発動して、certbotOCSPを取得できないというエラーが発生した。

マルウェア対策って。。

結局のところ、ほとんどの人がコピペなんですよね。X509失効リストのような重要なドメインですら、いちどマルウェア判定されるとこのようにコピペで思わぬ弊害を引き起こす。仕方ないこととはいえ、マルウェアがコピペで増殖するように、「間違ったマルウェア対策」もコピペで増殖するんですよね。

curlとgrep でHTMLから指定IDを取り出す。/ 後方参照の代替

grep で HTMLタグの値を取り出す。

curl と組み合わせて戦う

curl $URL | grep -oP '(?<=name="post_id" type="hidden" value=").+(?=" />)'

grep では後方参照がいい感じに取れないので、「後読み(lookbehind)や先読み(lookahead)」を使って対応することになる。

HTMLのinputのvalueを取り出す例

次のようなHTMLがあって、valueだけを取り出したい。とする、。

<input name="post_id" type="hidden" value="XxICIHRNcDFBr9RlssAMZz9U7Y3FHCpk" />'

先読み(look ahead)を使って前方一致を条件に入れる

grep -oP '(?<=name="post_id" type="hidden" value=").+'

後読み(look behind)

grep -oP '.+(?=" />)'

組み合わせて

grep -oP '(?<=name="post_id" type="hidden" value=").+(?=" />)'

これで冒頭条件の構成がわかる。

過去資料

grep にない後方参照を使う方法など

https://takuya-1st.hatenablog.jp/entry/2021/10/07/185957

lxc内部 でdocker を動かしたときのメモ。

Ubuntu のLXCでdockerを動かしたときのメモ

UbuntuのLXCで ubuntuを動かしてその中で、dockerを動かした。

lxc ubuntuを作る

最初に lxc のストレージを作る

lxc storage create bt01 btrfs

作ったストレージ内部にUbuntuを作る

lxc launch ubuntu:22.04 docker-host --storage bt01

LXCの権限を引き上げる。Vmのネスティングを有効にする。

lxc config set docker-host security.privileged true
lxc config set docker-host security.nesting true
lxc restart docker-host

dockerをubuntu on lxc に つくる

lxc に入って、Dockerをインストールする。

lxc shell docker-host

ubuntu に apt 経由で dockerを入れる。

sudo mkdir -m 0755 -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

動作チェック

sudo docker run hello-world

動作チェックの結果

Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:

docker をメンテナンス

 docker images
REPOSITORY          TAG       IMAGE ID       CREATED       SIZE
hello-world         latest    b038788ddb22   2 weeks ago   9.14kB

ubuntuのストレージをチェック

hello-worldは消しておく。

docker rm 5caa83a5f5df
docker image rm hello-world

lxc なら 2GBで動くの便利ですね。

root@docker-host:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop9      9.0G  2.0G  6.7G  23% /
none            492K  4.0K  488K   1% /dev
tmpfs           100K     0  100K   0% /dev/lxd
tmpfs           100K     0  100K   0% /dev/.lxd-mounts
tmpfs           3.9G     0  3.9G   0% /dev/shm
tmpfs           1.6G  300K  1.6G   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
snapfuse         60M   60M     0 100% /snap/core20/1826
snapfuse        110M  110M     0 100% /snap/lxd/24326
snapfuse         44M   44M     0 100% /snap/snapd/18363
tmpfs           786M  4.0K  786M   1% /run/user/0
overlay         9.0G  2.0G  6.7G  23% /var/lib/docker/overlay2/46de0bd4b99f36fbb16e5657c29a0b508fbe71e288b3564827c76eb10efdbf60/merged

sslhでudpをリッスン(できなかった)

sslh-53.conf を作ってUDP53をリッスンしようとした。

UDPで53でリッスンしてWireguardに流そうとした。

listen: (
  {
      host: "192.168.1.1";
      is_udp: true;
      port: "53";
  }
);
protocols:
(
   wireguard
  {
    name: "regex";
    host: "192.168.1.2";
    log_level: 2;
    is_udp: true;
    fork: true;
    port: "51822";
    regex_patterns: [ "\x01\x00\x00\x00" ];
  }
     { name: "openvpn"; host: "localhost"; port: "1194"; },
);

起動しない。

/usr/sbin/sslh -f -F sslh-53.conf
sslh-fork v1.22c started
UDP not (yet?) supported in sslh-fork

バージョン番号的には問題なはずだが、ssl-select ではないので、コマンドが存在しなかった。

opkg を確認

cat /usr/lib/opkg/info/sslh.list
/usr/sbin/sslh
/etc/sslh.conf
/etc/init.d/sslh
/etc/config/sslh

sslh-selectはまだコンパイルされてなかった。

UDP53で WireguardやOpenVPNやiodineを複数のリッスンしたら面白そうと思ったけど、無理そうだ。

パッケージを自分でビルドするしか無い。

GRUBで USB tty を有効にしたい。やっぱり無理だった。

GRUBで USB tty を有効にしたい。できないかなと思って試したけど、やっぱり無理だった。

2本の usb-ttl を差し込んで、通信チェック

[42195.351559] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[42195.371687] usb 1-1.4: pl2303 converter now attached to ttyUSB0
[42216.749739] usb 1-1.3: New USB device found, idVendor=1a86, idProduct=7523, [42216.788474] usb 1-1.3: ch341-uart converter now attached to ttyUSB1
sudo screen /dev/ttyUSB0 115200 
sudo screen /dev/ttyUSB1 115200 

USBで通信できることを確認。

GRUBにinsmod を追記する方法を確認

‘GRUB_PRELOAD_MODULES’

This option may be set to a list of GRUB module names separated by spaces.  
Each module will be loaded as early as possible, at the start of grub.cfg.
https://www.gnu.org/software/grub/manual/grub/html_node/Simple-configuration.html

再度開く

DEV=sda
sudo cryptsetup  open  /dev/${DEV}3 dm_crypt-0

ubuntuの自動マウントを解除する

sudo umount /media/takuya/grub-boot

マウント(sdb)

DEV=sda
## 
sudo mount /dev/mapper/vg-lv /mnt
sudo mount /dev/${DEV}2 /mnt/boot
sudo mount /dev/${DEV}1 /mnt/boot/efi
##
cd /mnt
sudo mount -t proc proc proc/
sudo mount -t sysfs sys sys/
sudo mount -o bind /dev dev/
sudo mount -t devpts pts dev/pts/
## 
sudo chroot /mnt

initramfs の作り直し

update-initramfs -u -k all 

grub の作り直し

mount -t efivarfs none /sys/firmware/efi/efivars
grub-install --target=x86_64-efi 

GRUB_PRELOAD_MODULES

https://www.linux.org/threads/understanding-the-various-grub-modules.11142/

insmod したら、ターミナルの選択肢が増えた

追加したら増えた。

バイスが見えてなくね?

USBはEFIがエミュレートしているので、GRUBから見えないかもしれない。

EFIのUSBエミュレートをオフにしてみる。

GRUBにモジュール追加したらできそうな気がしたが気の所為だったかもしれない。

insmod usbserial_pl2302
insmod usbserial_ftdi
insmod usb

モジュールは追加できるが、いい感じにシリアルポートして認識してくれない。

uefi ではできないかも

corebootEFIに変わるようにな存在である。 そのためEFIで動いてる限り無理なのではないか。

ブート用のファームウェアを変えれば行けるかもしれない。

ubootとかcorebootとかuefi 自体を入れ替えてしまえばいけるかもしれない。

でもそこまでする必要は・・・

https://forums.raspberrypi.com/viewtopic.php?t=283902

こどこそ、諦めた

ダメだね。

シリアルポートがない、小型PC/MiniPCにUSBシリアル/uart/ttyをつけて、リモートから管理をしようとしたけど無理っぽい

その他の解決策

USBしか無いデバイスでも m.2 や mini pciWifi用にあるので、PCIeから攻めるとか

https://amzn.to/435IJtq

小型の商品もあった。

USB On-The-Go(USB OTG)をつかって

RaspberryPiにUSB-HDMIキャプチャを搭載して、USB On-The-Go(USB OTG)でキーを送信するとか。そういう小手先のテクニックで代替手段を確保したほうが良さそうだ。

failed to execute shutdown binary になって再起動(シャットダウン)に失敗する

再起動後に帰ってこない

Ubuntuを再起動したら、終了プロセスでエラーが起きて、シャットダウンに失敗する。

「へ?何言ってんの?」思うかもしれないが、これを見てほしい

上記のようにUSB-HDDの自動アンマウントに失敗し、シャットダウンが失敗し、フリーズする。

そしてリモートから制御不能なる。

再起動なのに、リモート制御不能になる。

SSHもシリアルコンソールも使えない。なぜならOSはすべてシャットダウンをして最後のマウント解除でコケるからだ。 どうしようもない。

ちょっとパターンが違うエラーも出た。 一見するとext4 のエラーに見えるが、btrfs に作ってるlxd のext4 のjoural 落ちてる。

末尾だけみるとbtrfs だが、追いかけてみると、swapがエラーだった。

原因

エラー表記から btrfs や lvm かと思いきや、swap が壊れていた。

swapon / swapoff で見てたら、swapがエラーになっていた。

swapを作り直した。最近はメモリ潤沢だしswap無効も良いかも。

sudo mksoff /dev/mapper/vg-swap
sudo mkswap -L swap00 /dev/mapper/vg-swap
sudo mkson /dev/mapper/vg-swap

btrfs のエラーが頻発するので scrubかけまくった

sudo btrfs  check /dev/mapper/vg-btrfs 
sudo mount /dev/mapper/vg-btrfs /mnt
sudo btrfs scrub start /mnt
sudo btrfs scrub status /mnt
sudo umount /mnt

btrfs も一部エラーがでてたけど、swapが最終的な原因だった。

たぶん、この他にもUSBデバイスを解除するタイミングでエラーが出ているので、 USBデバイスからブートされているときに、解除に失敗してる。

USBデバイスを解除するときにマウントファイルシステムの解除に失敗していると思う。

たぶん、usb-hdd が dm-crypt で lvm でマウントしているからdmsetp 解除に失敗しているじゃないかなと思う(Ubuntu 2204で起きた)

フリーズ対策 : リモート再起動の手法を確保しておく

「再起動が途中で止まる。」などとというとんでもないエラーが、Ubuntuで起きることがわかったので、今後のことも兼ねて、リモートでもモニタリングができる方法や、リモートから電源を強制的にオンオフできる方法が必要だ。

リモートの電源ON/OFF

昔に比べて、IoT用に手軽にこの手に商品が手に入るので<逸般>家庭以外の<一般>の我々でも管理ができるようになっている。

2023-05-20現在 2個セットで2200円

スマートプラグ タイマー Tapo P105 2個セット

HDMIキャプチャでモニタリング

2023-05-20現在 15%オフで1800円くらい

また、ユーチューバー()の増加によりHDMIキャプチャが超格安で入手可能になっててモニタを2枚用意するよりキャプチャ使うほうが手軽になってる。

HDMIキャプチャーボード 1080p/60Hz