takaha.siの技術メモ

勉強したことをお伝えします。ちょっとでも誰かの役に立てればいいな…

絵描きのzfs snapshot活用術

あっ、線画レイヤーに色塗ってる!

突然ですが、私、趣味で絵を描きます(NSFWです)

twitter.com

絵描きあるあるが「線画レイヤーに色を直接塗ってしまっており、それに気づくことなく作業をガンガン進めてしまい、気づいたら手遅れなところまで進んでしまっている」という事故です。ご丁寧に保存しちゃってたりするともはや後には戻れません。なんとか、お絵かきソフトを駆使して線画と中の色塗りの部分を分離するみたいなことをするしかないです。とてもとても面倒くさいです。

なので考えました。せっかくzfsNASを組んだのだから、zfs snapshotで一時間ごとに絵のファイルが保存されてる作業フォルダのスナップショットを取っておけばよいのでは?と。そしたら、ミスに気づいた時点で過去のスナップショットをさかのぼって、そこから色が塗られちゃう前の古い作業データの線画レイヤだけ取り出して現作業ファイルにコピペすればらくらく回復できます。

というわけで、さっそくやってみました。

なお、zfsNASの話は以下のリンクから読めます。

tkh86.hateblo.jp

まず、定期zfs snapshot。cronとかつかって自分でやってもいいですが面倒ですよね。ここはsanoidっていうツールがあるのでこれを使います。

github.com

Pythonで書かれてるっぽいです。まあ、Debianだったらapt-getでインストールできますので楽ちん。

$ sudo apt install sanoid

んで、次に設定ファイル。/etc/sanoid/sanoid.confに設定ファイルあるので、emacsだかviだかで編集します。私はこんな感じにしました。

[tank/working]
        use_template = template_daily

[template_daily]
        hourly = 24
        daily  = 7
        monthly = 0
        yearly = 0
        autosnap = yes
        autoprune = yes

tank/workingというzfsがイラストのファイルが入ってる領域です。これをsambaで外部に公開して、Windowsからマウントして使ってる感じです。

hourly = 24で一日に一時間に一回snapshotを取り、daily = 7で一日一個。保存する感じにしてます。多分これであってる。。。sanoid.confを編集したら、sanoidを再起動しときます。

$ sudo systemctl restart sanoid

これで、定期的にsnapshotが取れます。

root@liella:/home/takahashi# zfs list -t snapshot
NAME                                                USED  AVAIL     REFER  MOUNTPOINT
tank/working@autosnap_2022-03-13_00:22:35_monthly     0B      -     4.81G  -
tank/working@autosnap_2022-03-13_00:22:35_daily       0B      -     4.81G  -
tank/working@autosnap_2022-03-13_00:22:35_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_01:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_02:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_03:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_04:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_05:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_06:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_07:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_08:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_09:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_10:00:01_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_11:00:24_hourly      0B      -     4.81G  -
tank/working@autosnap_2022-03-13_12:00:24_hourly      0B      -     4.81G  -

この設定で一時間に一枚snapshot取れてます。

特定の場所だけsnapshot取りたい!

余談ですが、zfsでスナップショットを取る場合は、zfsファイルシステム(か、zvol)単位になります。つまりzfs内部にある特定のディレクトリだけsnapshotを取る!みたいなことはできないわけです。

root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank

例えば、こんなzfsがあったとしましょう。ここで、/tankディレクトリの下にoekakiみたいなディレクトリを作り、このoekakiディレクトリ内部だけのsnapshotを取りたい!みたいなことはできないんですね。上の例だとtank全体でしかsnapshotがとれません。

tankの下にoekakiとは別のディレクトリもたくさんあって、そこにデータもたくさんある場合はそこもまとめてスナップショットされてしまいます。すると、スナップショットの容量が大きくなってzfs poolの容量を圧迫します。

スナップショット領域は最も重要なファイルがあるディレクトリだけに限定したいですね。

とはいえ、スナップショット対象領域を別にした構成に新たに作り直すのも面倒です。後付で特定のディレクトリだけ、例えば/tank/samba/public/workingの場所だけsnapshot取りたい場合は一工夫すれば一応それっぽいことが可能です。

まず、tankの下に新しいzfsを後付します。

root@liella:/home/takahashi# zfs create tank/working
root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/working

これで、tank/workingという新しいzfsが作れました。あとはMOUNTPOINTを変更します。

root@liella:/home/takahashi# zfs set mountpoint=/tank/samba/public/working tank/working
root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/samba/public/working

これで、tank/workingのスナップショットをとれば/tank/samba/public/workingサブディレクトリの中身のみスナップショットを取るみたいなことが事実上できます。あとはtank/workingをマウントすればいいです。

root@liella:/home/takahashi# zfs mount tank/working
root@liella:/home/takahashi# df -h
Filesystem                  Size  Used Avail Use% Mounted on
tank                         15T  1.7T   13T  12% /tank
tank/working                 13T  4.9G   13T   1% /tank/samba/public/working

こんな感じで。もちろん、もともと/tank/samba/public/workingに存在していたファイルはmount時点で見えなくなってしまうので(消えるわけではなくて見えなくなるだけです。mount解除すればまた見えるようになります)もともとあったファイルはどこかに退避しといた上でzfs mount後に移動し直せばいいです。

もともとあったファイルの移動のひと手間がありますが、zfsを作り直すよりは楽だと思いますので、特定ディレクトリだけスナップショット取りたい場合はこのやり方はおすすめです。

さて、/tank/samba/public/workingのスナップショットを取ったとして、スナップショットもWindowsから楽に参照できればいいですよね?というわけで、このスナップショットをマウントしてSambaで公開します。

まず、スナップショットのマウントから。

root@liella:/home/takahashi# zfs list
NAME                         USED  AVAIL     REFER  MOUNTPOINT
tank                        1.65T  12.8T     1.64T  /tank
tank/working                4.81G  12.8T     4.81G  /tank/samba/public/working
root@liella:/home/takahashi# zfs set snapdir=visible tank/working

さて、zfs set snapdir=visible tank/workingを実行したあとで/tank/samba/public/workingの中身を見てみると。。。

root@liella:/tank/samba/public/working# ls -ahlF
total 1.7G
drwxrwxrwx 9 101000 101000   10 Mar 13 09:18 ./
drwxrwxr-x 9 101000 101000   13 Mar 13 16:46 ../
drwxr-xr-x 2 101000 101000   55 Mar 13 09:18 0.下描き/
drwxr-xr-x 2 101000 101000   16 Mar 13 09:18 1.ペン入れ/
drwxr-xr-x 2 101000 101000    6 Mar 13 09:21 2.彩色/
drwxr-xr-x 3 101000 101000   10 Mar 13 09:18 3.QA_Review/
drwxr-xr-x 3 101000 101000   42 Mar 13 09:21 4.Done/
drwxr-xr-x 2 101000 101000   19 Mar 13 09:18 done_drawing/
-rw-r--r-- 1 101000 101000  70M Mar 13 01:16 knowhow.pur
-rw-r--r-- 1 101000 101000 1.6G Mar  9 22:03 myboard.pur
drwxrwxrwx 1 root   root      0 Mar 13 17:26 .zfs/

.zfsという隠しディレクトリができてることがわかります。この中身を見てみると。。。

root@liella:/tank/samba/public/working# cd .zfs/
root@liella:/tank/samba/public/working/.zfs# ls -ahlF
total 17K
drwxrwxrwx 1 root   root    0 Mar 13 17:26 ./
drwxrwxrwx 9 101000 101000 10 Mar 13 09:18 ../
drwxrwxrwx 2 root   root    2 Mar 13 17:26 shares/
drwxrwxrwx 2 root   root    2 Mar 13 22:00 snapshot/
root@liella:/tank/samba/public/working/.zfs# cd snapshot/
root@liella:/tank/samba/public/working/.zfs/snapshot# ls -ahlF
total 264K
drwxrwxrwx 2 root   root    2 Mar 13 22:00 ./
drwxrwxrwx 1 root   root    0 Mar 13 17:26 ../
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_daily/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_00:22:35_monthly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_01:00:01_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_02:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_03:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_04:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_05:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_06:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_07:00:24_hourly/
drwxrwxrwx 8 root   root   10 Mar 13 09:18 autosnap_2022-03-13_08:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_09:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_10:00:01_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_11:00:24_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_12:00:24_hourly/
drwxrwxrwx 8 101000 101000 10 Mar 13 09:18 autosnap_2022-03-13_13:00:01_hourly/

はい、このように、snapshotが見えます。

なので、この.zfs領域をSambaで公開してそれをWindowsから見ればOKです。こんな感じで見れます。

f:id:tkh86:20220313221750p:plain

ここで問題発生

ここで問題発生。なんかスナップショットのファイル名が妙ちくりんな名前になってます。これは、Windowsがファイル名に:を使えないことに起因します。sanoidが取るスナップショット名に:が入っちゃってるので、Sambaが変換をかけてこうなっちゃうんですね。

sanoidのほうでsnapshotの名前を:を使わないなにか別のフォーマットに変えられないのか?と思ったんですが、これは無理です。sanoidにその機能はないとのこと

なのでsamba側でなんとかする

なので、samba側でなんとかします。sambaにはwindowsでは表示できない記号がファイル名に使われてた場合、それを置き換える機能vfs objects = catiaというものがあって、それを使います。

[snapshot]
    comment = zfs snapshots
    vfs objects = catia
    mangled names = no
    catia:mappings = 0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6
    path = /samba/public/working/.zfs
    writable = no
    guest ok = no

この0x22:0xa8,0x2a:0xa4,0x2f:0xf8,0x3a:0xf7,0x3c:0xab,0x3e:0xbb,0x3f:0xbf,0x5c:0xff,0x7c:0xa6ってのが変換テーブルなわけですが、これの内訳はこういう意味です。

0x22:0xa8 = ダブルクオーテーション (") -> ウムラウト(¨)
0x2a:0xa4 = アスタリスク(*) -> 汎用通貨記号(¤)
0x2f:0xf8  = スラッシュ(/) ->スラッシュ付きオー(⊘)
0x3a:0xf7 = コロン(:) ->除算記号(÷)
0x3c:0xab = 山括弧左(<) ->二重山括弧左(《)
0x3e:0xbb = 山括弧右(>) ->二重山括弧右(》)
0x3f:0xbf  = クエスチョンマーク(?) ->逆さクエスチョン(¿)
0x5c:0xff   = Yenマーク(\) ->Yの分音記号(ÿ)
0x7c:0xa6 = 垂直線(|) ->分割垂直線(¦)

この設定で。いい感じに表示できます。

f:id:tkh86:20220313222712p:plain

2つ注意点。

この変換テーブルをいじらないほうがいいです。例えば、÷(除算記号)はいやだから_(アンダーバー)に変換したい!と思ってもいじっちゃダメです。理由はあらゆるファイルの_がすべて:に変換されてしまうので、_を含むファイルやディレクトリをみようとした瞬間全体がおかしくなります。_はファイルパスとしてわりかし普通に使うので変換先として使うのはやめましょう。また、このvfs objects = catiaをsambaの[global]セクションに入れるのもやめましょう。理由はsnapshot以外のすべての領域において万が一除算記号(÷)といった記号がファイル名に使われてしまうと、上述した理由でこれまたSamba全体がおかしくなるからです。snapshotの部分は除算記号(÷)などがパスに使われることはないと保証できますので(sanoidはそんな記号使わないから)snapshot領域のみにvfs objects = catiaを適用しましょう。

はい、これで一時間おきに自動的にスナップショットがとられていつでも過去のデータを参照することができるようになります!

これでHappyお絵かきライフが捗るんじゃぁ~

次はバックアップだねぇ。。。

lxcコンテナのI/Oが異様に遅いという話

NASの運用をようやく開始

LXCコンテナのI/Oが遅い気がします。Dockerの話じゃないですLXCです。

1年ちょい前に10GbENASを自作したという話をしました。作ったのはいいんですが、OSインストールを終わらせた後ちょっといじってその後完全放置してたんですよね。だってHDDをそれなりの数揃える勇気がなかったのよ。惰性でQNAP使ってました。なんともったいない。

tkh86.hateblo.jp

流石にそろそろ真面目に運用するか?と思って+20万円ぐらい追加課金してHDDや追加部品を揃えました。もう「なるべくお安く」なんてのはどこ吹く風で徹底的に課金しています。

OSも入れ直し。ProxmoxっていうDebianベースのOSにしました。なおこのProxmox。コンテナ(LXC)やVMをWebのGUIで管理できるっていうスグレモノです。VMware ESXiみたいなものですね(VMMはKVMなのでType-Iではないですよ。勘違いしてる人いますが違いますよ)

www.proxmox.com

f:id:tkh86:20211017172711p:plain
こんな感じの素敵なGUIがついてきます

課金しなくても十分使えます。インストールも簡単出しおすすめです。結構使ってる人いますね。

お家で始める仮想化環境 Proxmox VE 環境構築編 | Nishiの独り言

Proxmox VE6.3のインストールと、やっておくと良い初期設定

本題:LXCコンテナのI/Oが遅いって話

そんで、せっかくなのでSambaデーモンはLXCコンテナ上に置き、ホストのZFS領域をそのコンテナ上にマウントしてそこをSambaで公開するというのを取りました。なんかセキュアな感じがするのでそうしました。ちなみに、私はlxcコンテナに今回はじめて触れました。コンテナといえばDockerが有名ですが、Dockerとの違いはフルのLinuxシステムが動くかどうか?というところだと思います。Dockerが一コンテナ一プロセスという哲学で開発されているのに対して、lxcコンテナはフルのLinuxシステム、つまり、systemdやinitとかも動かしてOKのようです。VMにより近いコンテナと言えると思います。

Windowsから早速アクセスしてベンチマークをとってみました。

f:id:tkh86:20211017173338p:plain
CrystalDiskMarkでのNASベンチマーク

シーケンシャルはいい、が、ランダムアクセスが遅くないですか????いや、HDD6台でRAIDZ2だとこんなもんじゃない?って思うでしょ?いやいや、このZFSね。SLOGとかL2ARCをSamsung 980 PROのNVMe SSDに置いてるんですよ。見てくださいこれ。あと、メモリだって64GB積んでますからね?

root@liella:~# zpool status
  pool: tank
 state: ONLINE
config:

        NAME                                          STATE     READ WRITE CKSUM
        tank                                          ONLINE       0     0     0
          raidz2-0                                    ONLINE       0     0     0
            ata-ST4000VN008-2DR166_ZDHAA43Z           ONLINE       0     0     0
            ata-WDC_WD40EFZX-68AWUN0_WD-WX32D515UU33  ONLINE       0     0     0
            ata-ST4000VN008-2DR166_ZDHAAMFM           ONLINE       0     0     0
            ata-WDC_WD40EFZX-68AWUN0_WD-WX32D514ZD4J  ONLINE       0     0     0
            ata-ST4000VN008-2DR166_ZDHAA46D           ONLINE       0     0     0
            ata-WDC_WD40EFZX-68AWUN0_WD-WX32D515UH9R  ONLINE       0     0     0
        logs 
          nvme0n1p2                                   ONLINE       0     0     0
        cache
          nvme0n1p1                                   ONLINE       0     0     0

errors: No known data errors

んで、Samsung 980 PROのランダムアクセスはもっと速いはずなんですよ。。。こんなもんじゃないのですよ。

ほんとにほんとに本題

さて、色々と調べて「これはLXCコンテナのI/Oが遅いのではないか?」という結論に至りました。ベンチマークをとってみます。ホストにあるZFSと、ホスト上で動いてるLXCコンテナ経由でZFS(ホストのZFS領域をコンテナ内へマウントしてます)でそれぞれbonnie++ベンチマークをとってみました。

# bonnie++ -u root -d ./ -n 50:1048576:1048576:1 -s 4096 -r 1024

結果です

Version  2.00       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Name:Size etc        /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
liella           4G 1898k  99  1.6g  53  1.6g  55 2892k  99  7.4g  99 +++++ +++
Latency              5300us      54us      43us    3030us      29us      95us
Version  2.00       ------Sequential Create------ --------Random Create--------
liella              -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
files:max:min        /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
 50:1048576:1048576 51200  68 51200  99 51200  73 51200  69 51200  99 51200  43
Latency             16339us    2735us    7303us   11797us    3517us    1094ms

random craeteでも51200ファイル/sec(であってますよね?)作れてて、なによりレイテンシがcreateで11797us。readで3517usで、deleteが1094msぐらいです。

それでは、このZFS領域をLXCコンテナへマウントして、LXCコンテナ内部から測ってみましょう。なお、LXCコンテナにはCPU4コアとメモリは8GB割り当ててます。

Version  2.00       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Name:Size etc        /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
fortress         4G 1735k  99  1.6g  55  1.5g  57 2652k  99  7.0g  99 +++++ +++
Latency              4858us      92us      46us    3367us      24us     396us
Version  2.00       ------Sequential Create------ --------Random Create--------
fortress            -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
files:max:min        /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
 50:1048576:1048576     0  54     0  71     0  43     0  45     0  31     0  25
Latency               347ms     331ms    2880ms     368ms     326ms    1658ms

うーん。顕著に性能ダウンしてます。特にSequential CreateとRandom Createのあたり。これゼロってことは一秒間に一個も作れてないってことですよね?

なにより、Latencyが文字通り桁違いに遅くなってます。100ms=100000usなので。。。おそすぎです。。。

うーん。この性能差はどこから来るのでしょう???何がボトルネックなのでしょう???謎いです。

pythonのデフォルト引数の挙動は可怪しいという話

Pythonをすごいやりたい!ってわけではないんですが、DiscordのBot作るのにPython便利そうなので使ってます。

で、プログラミング言語にはデフォルト引数っていう機能がありますよね?Pythonにもあるんですが、Pythonのデフォルト引数の仕様は普段私が使ってる言語たち(Ruby, CL, Scala etc)と仕様がちょっと違うのでハマりましたよという話。

import time
import datetime
class Foo:
    def bar(self, now = datetime.datetime.now()):
        print(f"{now}")
hoge = Foo()
while True:
    hoge.bar()
    time.sleep(1)

ここでbarっていうFooクラスのインスタンスメソッドがあったとして、普通barが呼ばれるたびにnowは更新されると思いませんか?。ところがpythonってそうならないんですよね。。。classがnewされたときのnowが固定で代入されてずーっと呼び出されます。

私このPythonの意味不明仕様をしらなかったもんで、これに1時間ぐらいハマりました。ユニットテストでは通るのに、実際にコードを動かすと思ったように動かない。

他の言語ではどうなんですか?っていう話ですが

例えばRuby

require 'date'
class Foo
  def bar(now = Time.now)
    puts now
  end
end
hoge = Foo.new()
while true
  hoge.bar()
  sleep(1)
end

Rubyの結果

2020-12-29 15:47:16 +0900
2020-12-29 15:47:17 +0900
2020-12-29 15:47:18 +0900
2020-12-29 15:47:19 +0900
2020-12-29 15:47:20 +0900
2020-12-29 15:47:21 +0900
2020-12-29 15:47:22 +0900
2020-12-29 15:47:23 +0900
^CTraceback (most recent call last):
        1: from test.rb:14:in `<main>'
test.rb:14:in `sleep': Interrupt

普通はこうですよね!メソッド呼び出しのたびにデフォルト引数内部は更新されますよ!変わりますってば!

Scalaの場合

class Hoge {
  def _now() {
  }
}
class Foo {
  def bar(now: Hoge = new Hoge()) {
    println(now)
  }
}
val hoge = new Foo()
while (true) {
  Thread.sleep(1000)
  hoge.bar()
}
Main$$anon$1$Hoge@16f65612
Main$$anon$1$Hoge@311d617d
Main$$anon$1$Hoge@7c53a9eb
Main$$anon$1$Hoge@ed17bee
Main$$anon$1$Hoge@2a33fae0
Main$$anon$1$Hoge@707f7052
Main$$anon$1$Hoge@11028347
Main$$anon$1$Hoge@14899482
Main$$anon$1$Hoge@21588809
Main$$anon$1$Hoge@2aae9190
Main$$anon$1$Hoge@2f333739
Main$$anon$1$Hoge@77468bd9
Main$$anon$1$Hoge@12bb4df8

Scalaの場合もそうです。呼び出しごとに更新されます。当たり前です。

Common Lispの場合

(let ((foo ()))
  (defun bar (&optional (now (random 100)))
    (print now)))

(loop (progn
        (bar)
        (sleep 1)))
14 
69 
6 
48 
60 
73 
8 
31 
43 
4 
6 
71 
14 
81 
78 
14 

いや普通(世の中に出回ってる言語の多数派)こうだと思いますが。。。はて。。。?

Pythonってこの他にも「なぜこうした?」っていう意味不明な仕様が多くて、例えば、mapfilterといった関数もmap objectとかfilter objectとかが返ってきたりしますよね?

いやこういうのって、普通基底クラスなりなんなりにEnumerableとかTraversableみたいなのがあって、ArrayとかListとかはそいつを継承させるものなんじゃないんですか?だからmap objectとかfilter objectとか別途用意するんじゃなくて、mapの結果が直接ArrayとかListを出せるようにするべきなんじゃないんですかね?とかとか

最近のディスアセンブラとかデバッガとかの話

ちょっとリバースエンジニアリングマルウェア解析ではない)をしないといけなくなったのでそこらへんのツールを探してみた。ここらへんの知識が2008年ぐらいで止まってるので、情報更新。

デバッガ

昔、バイナリ解析と言ったらOllyDbgが有名だったと思うんですが2014年を最後に更新が止まってしまっているようです。

じゃあ今の人たちは何を使ってるのかなぁ?って探すとx64dbgってのがいいみたいです。ちなみにx64と命名されてますが32bit版もあります。

x64dbg.com

OSS(GPLv3)でGitHubで開発されています。

コマンド体系は(MS製でWindowsのでバドラ開発とかに使う)WinDbgに似てるかなぁ。という感じ。bp ReadFileとかでWin32APIにブレークポイントしかけられます。

「いいなぁ」と思ったのは「ユーザーコードまで実行」というコマンドがあることで、これは嬉しいなと思いました。リバースエンジニアリングってのはWin32APIを手がかりにブレークポイント引っ掛けて、そこから解析していくと思うんですが、その時このコマンドを使えば、一気に解析対象のソフトウェアコードまで戻るわけです。

ディスアセンブラ

みんな憧れのIDA Proはまだ現役らしいですね。20年以上この業界でデファクトスタンダードの地位に留まれているのは素晴らしい。

とはいえ、IDA Proは10万円以上する高価なソフトウェアなので、別に本業でマルウェア解析してるわけでもない私みたいな個人が手軽に手を出すのはちょい辛いかなと思います。

ただ、IDA FreewareってのがあってIDA Proの機能限定版がタダで使えます。

www.hex-rays.com

ディスアセンブラなんですが、ディスアセンブルしたコードの参照関係のグラフを出してくれるのが嬉しいです。この昨日はIDA Freewareでも省かれてないので嬉しい。

SAIのサムネイルをWindows上のエクスプローラで見れるようにする

ペイントソフトSAIのファイルってWindowsエクスプローラでサムネイルが見れないですよね。これ地味に不便でなんとかしたい。SAIはものすごく良いソフトでペンが軽快で描いてて気持ち良いソフトなんで愛用してるんですが、このサムネイルが見れないというのにフラストレーションを感じていました。

検索するとSaiThumbsってのがあってこれをインストールするとWindowsエクスプローラから見れるようになります。

github.com

ここからダウンロードできます

https://wunkolo.itch.io/saithumbs

寄付してーって言われますが、一応寄付せずにダウンロードするだけもできます。「No thanks, just take me to the downloads」をクリックすればOK

ダウンロードして解凍して中の「install.bat」を実行すればOKです。エクスプローラからsaiファイルのサムネイルが見れるようになります。

f:id:tkh86:20200724174429p:plain
SAIのサムネイルが見れるようになる

SAI2は残念ながらまだないようです。

余談ですが、内部的にはlibsaiってのを使ってて、saiのファイルはほとんど解析されてるようです。

github.com

saiのファイルは暗号化がかかってるというのはバイナリエディタで中をみてぼんやりと感じてたんですが、見事に解析してますね。。。すごいなぁ。。。

VPSのvultrがいいよって話

www.vultr.com

vultrというVPSサービスがある。最近ちょくちょく聞くので使ってみた。用途はT-potの運営のためのサーバを借りるため。AWS EC2上で運用しようかなと思ったけどEC2はisoイメージからのインストールの際、まずはじめにS3にisoイメージを上げてーとかちょっと面倒なことをしないといけない。ので、ついでにvultr使ってみようかなと思った。VPSで有名所といえば、あとはさくらVPSがあるけど、さくらVPSはもう使っていて何台か借りてる。のでものは試しでvultrにしてみた。

ちょっと使ってみたところ、AWS Lightsailと比べて(VPS単体で見れば)Vultrのほうが機能面で充実しているかなとおもった。S3とかDynamoDBとかRDSとかと組み合わせないのであればVultrのほうが良い気がします。とはいえ、Vultrもブロックストレージ(AWS EBS相当のもの)とオブジェクトストレージ(AWS S3互換のよう)とLoad Balancerはあるみたいです。

以下箇条書き

  1. isoイメージからの起動ができる。自分で好きなisoイメージをアップロードしてそこからBootしてインストール可能。Lightsailはできない。
  2. Webコンソールの画面操作はVGAVNC経由で飛ばしている。LightsailはWebコンソールもSSH経由なのでiptablesとかでしくじると復旧の手段が完全に絶たれるが、vultrはそんなことはない
  3. セキュリティインシデントあるとメールで警告してくれる。
  4. いまなら100USDクレジットくれる!
  5. 支払いにBitcoin (BTC)とBitcoin Cash (BCH)が使える!まあネタだと思うけど、PRQかよw

ただ、課金形体がプリペイド式で独特です。クレジットカードで課金するとVultr内のクレジットにチャージできて、そのクレジットから毎時間の使用量が引き落とされる感じです。この点AWS Lightsailのように1ドル単位で使用した料金をクレカから引き落としてしくれるわけではないみたいです。この点はちょっとLightsailと比べると柔軟性が低いかもしれません。

ところで、この請求の仕方をライバルのさくらVPSと比べると、時間単位請求という点で一見さくらVPSよりは柔軟のように見えるが一長一短あるかなという感じ。プリペイド方式なので「使った金額がそのまま月額課金される」わけではなく「事前にプリペイドでチャージしておかないといけない」わけで、実際の使用量よりも多くチャージしないとクレジットが足りないのでやむなく多めにチャージするけど端数が出てしまうという感じになりそうです。ここで個人使用だとちょっと拒否感が出る人がいるかもです。さくらVPSはたしかに時間単位請求はしてくれませんが、月額のみがクレジットカードから引き落とされますのでプリペイドチャージで使用しきれない端数が出ることはないわけです。

あと、上述しましたが、今だと100USDクレジットただでもらえるので、インスタンスにもよりますが、二ヶ月ぐらいはただで使えますよ。太っ腹!

なお、上のリンクは私へのアフィリンクになってますので踏んでくれると私にクレジットが入りますのでぜひ踏んでください ;-)

AWS Lightsail上でT-potは運用しないほうがいいという話

AWS Lightsail上でDebianVMインスタンスを借りて、その上でT-potをインストールした。

最初の方はうまく動いていたのだが、1時間ぐらいするとVMが落ちる問題が発生。SSHはもちろんのことpingすら通らなくなる。AWSのWebIFからVMの再起動をかけると再び上がってくるが、また1時間ほど放置するとVMが落ちる。これでは使い物にならない。なお、AWS LightsailのWebIFはSSH経由なのでこうなってしまうとVMにアクセスする方法がなくなるので調査も面倒だった。再起動するしかない。これ使い勝手悪いよね。。。せめてシリアルポート経由にしてもらえないか?と思う。

ログやらなんやらを色々みて何が起こっているかはわかったので、誰かの役に立つかもしれないので一応書き残しておく。

まずAWS LightsailにはNATがある。VMが直接グローバルIPのセグメントにぶら下がってるわけではない。NATの後ろに隠れている。そんで、そのNATの内部のプライベートIPはDHCPで降ってくる。

で、このNAT内部のプライベートIPのリース期間が短い。3000sec前後しかないのである。したがってこのDHCPのIPリース期間が切れるとIPが剥奪されて一切の通信ができなくなるのである。これが落ちてた原因

dhclientのリース期間が切れたときは当然dhclientが再取得するはずだが、T-potインストール後の環境にはこのdhclientが常駐してない。だから再取得も行われるそのまま沈黙してしまう。

他のT-potをインストールしていないLightsailインスタンスには確かにdhclientが常駐している。T-potがインストール時にこの設定を消してしまうのだろう。

f:id:tkh86:20200712061730p:plain
他のLightsailインスタンスにはdhclientが常駐

ところで、よくわかってないのだが、dhcpのリース期間を無限に設定してある自宅LAN環境のLinuxサーバではdhclientは常駐してない。リース期間が有限だとNetworkManagerあたりが自動的に立ち上げるのかしら?ここらへんは調べてもよくわからなかった。

じゃあdhclient起動すればいいんでしょ!ってんで手動でdhclientを立ち上げてみたらどうか?と思い。以下のコマンドを叩いた。

# dhclient -r -v eth0 && rm /var/lib/dhcp/dhclient.* ; dhclient -v eth0

すると、たしかに1時間たってもVMは落ちなくなった。

しかしまたもや問題発生。1時間ではVMは落ちなくなったが、今度は4時間後ぐらいにまたVMが落ちてしまう問題が発生した。時間が伸びただけでやっぱり落ちるのである。

Jul 01 01:52:45 rapidassistance dhclient[19188]: DHCPREQUEST for 172.26.3.244 on eth0 to 172.26.0.1 port 67
Jul 01 01:52:45 rapidassistance dhclient[19188]: DHCPACK of 172.26.3.244 from 172.26.0.1
Jul 01 02:18:04 rapidassistance dhclient[19188]: DHCPREQUEST for 172.26.3.244 on eth0 to 172.26.0.1 port 67
Jul 01 02:18:04 rapidassistance dhclient[19188]: DHCPACK of 172.26.3.244 from 172.26.0.1
Jul 01 02:44:37 rapidassistance dhclient[19188]: DHCPREQUEST for 172.26.3.244 on eth0 to 172.26.0.1 port 67
Jul 01 02:44:37 rapidassistance dhclient[19188]: DHCPACK of 172.26.3.244 from 172.26.0.1
Jul 01 03:08:46 rapidassistance dhclient[19188]: DHCPREQUEST for 172.26.3.244 on eth0 to 172.26.0.1 port 67
Jul 01 03:08:46 rapidassistance dhclient[19188]: DHCPACK of 172.26.3.244 from 172.26.0.1
Jul 01 03:27:39 rapidassistance dhclient[403]: Internet Systems Consortium DHCP Client 4.4.1
Jul 01 03:27:39 rapidassistance dhclient[403]: For info, please visit https://www.isc.org/software/dhcp/
Jul 01 03:27:39 rapidassistance dhclient[403]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3
Jul 01 03:27:39 rapidassistance dhclient[403]: DHCPOFFER of 172.26.3.244 from 172.26.0.1
Jul 01 03:27:39 rapidassistance dhclient[403]: DHCPREQUEST for 172.26.3.244 on eth0 to 255.255.255.255 port 67
Jul 01 03:27:39 rapidassistance dhclient[403]: DHCPACK of 172.26.3.244 from 172.26.0.1
Jul 01 03:27:39 rapidassistance dhclient[428]: Internet Systems Consortium DHCP Client 4.4.1
Jul 01 03:27:39 rapidassistance sh[414]: Internet Systems Consortium DHCP Client 4.4.1
Jul 01 03:27:39 rapidassistance sh[414]: For info, please visit https://www.isc.org/software/dhcp/
Jul 01 03:27:39 rapidassistance dhclient[428]: For info, please visit https://www.isc.org/software/dhcp/
Jul 01 03:27:39 rapidassistance sh[414]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 4
Jul 01 03:27:39 rapidassistance sh[414]: DHCPOFFER of 172.26.3.244 from 172.26.0.1
Jul 01 03:27:39 rapidassistance sh[414]: DHCPREQUEST for 172.26.3.244 on eth0 to 255.255.255.255 port 67
Jul 01 03:27:39 rapidassistance sh[414]: DHCPACK of 172.26.3.244 from 172.26.0.1

ログをみると、Jul 01 03:27:39まではdhclientはご機嫌でIPのリースを更新してくれているようだが、それ以降ログが途切れている。誰かがNetworkManager周りに再起動をかけたのか?このとき、僕が手動で起動したdhclientも殺されてまたDHCPリースが切れたときにIPが消えて落ちているようだ。元の木阿弥である。

じゃあdhclientをsystemdで起動してWatchdogでプロセスが殺されても復活するようにすればいいのでは?とおもってsystemdのconfigを書いてdhclientを起動させた。。。

ところでどうやら何やら不味いことをやってしまったらしく、systemd start dhclient.serviceとやった瞬間VMが落ちた。すでにenableにした後だったのでAWSのWebIFから再起動をかけてもSSHにはつながらない。\(^o^)/オワタ

AWSのWebIFはSSH経由での接続しかサポートしてないのでこうなってしまうとどうやってもVMにはアクセスできなくなるのでインスタンスを削除してOS再インストールからやり直しである。

ここで心が折れた。もうAWS Lightsail上でのT-pot運用は諦めることにする。。。これ以上は面倒くさいし不毛だ。。。

T-potはOSごと含めたISOイメージからのインストール手段も提供している。ので、EC2でインスタンスを借りて、ISOイメージからインストールしてみようかなと思う。

というわけでLightsailでT-potの運用は面倒くさいんでやめたほうがいいという結論になりました。