2019年12月29日

SMB サーバをデータベース置き場として使う

自宅サーバを Raspberry Pi に徐々に置き換えようとしている。HTTP プロキシや簡単な死活監視くらいは安定して動いているが、データベースをごりごり使うようなサービスは SD カードの寿命を縮めそうで手を出せなかった。かといって外付けディスクは設置場所の狭さで難しく、ケーブルが増えるのも煩わしい。

ふと nasne を活用できるんじゃないかと気づいてやってみたら半日くらいで構築できた。

準備

  • DBサーバ(デーモンが起動する方)
    • Raspberry Pi + Raspbian 10 (Buster)
    • 普段のユーザーは pi
    • ホームルーターに有線接続
  • PostgreSQL。設定は apt で入れたそのまま。
    • データ格納先は /var/lib/postgresql
    • 所有者とグループは postgres:postgres
  • SMBサーバ - nasne
    • こちらも有線 LAN
    • IP は 192.168.0.24
    • 1TB。中身は録画と音楽などで40%ほどの使用量。

nasne 以外の SMB サーバでも基本は変わらないし(出荷終了しましたし)、MariaDB とか別のデータベースでも応用が効くはず。

何も考えずに作る流れ

1. データ退避

データベースサーバを止めて /var/lib/postgresql/var/tmp/postgresql にリネーム。

2. SMB 上でフォルダを準備

必要なパッケージは cifs-utils

マウントの手順は細かく書かないが、nasne なら SMB のバージョンを1に指定するのがポイント。

mkdir /media/nasne
mount.cifs //192.168.0.24/share1 -o guest,vers=1.0
mkdir -p /media/nasne/pi/postgres

nasne のトップ階層にはメディアサーバ(DLNA) で配信されるフォルダがあるので、混じらないよう Pi 用のディレクトリを切ってみた。ホームディレクトリを意識してpostgresql ではなくユーザ名(postgres)で作成している。

$ ls /media/nasne/
MUSIC/  PHOTO/  VIDEO/ pi/

このあとマウントし直すので、フォルダ作成くらいなら他の OS からやってもいい。

3. postgres ユーザでマウント

/etc/fstab に追加。

//192.168.0.24/share1/pi/postgres /var/lib/postgresql   cifs      guest,dir_mode=0750,file_mode=0777,uid=postgres,gid=postgres,nounix,vers=1.0 0 0

マウントポイントを作って mount。

umount /media/nasne
mkdir /var/lib/postgresql
mount -a

4. データ戻して DB 起動

pi ユーザーは /var/lib/postgresql を読めない。

[email protected]:~ $ ls -ld /var/lib/postgresql
drwxr-x--- 2 postgres postgres 0 Dec 27 21:06 /var/lib/postgresql
[email protected]:~ $ ls -l /var/lib/postgresql
ls: cannot access '/var/lib/postgresql': Permission denied

戻すときは root で。

cp /var/tmp/postgresql/* /var/lib/postgresql/

暗号化する流れ

nasne のファイルサーバーにはパスワード保護をかけられないので、同じネットワーク内なら中身が丸見え。データベースのバイナリデータとはいえ、同じようにマウントすれば簡単にデータの読み書きができる。これではデータベースサーバでユーザ認証をつけても無意味なので SMB 上のデータをフォルダ単位で暗号化しようと思う。

今回はディスク全体を暗号化することはできないので dm-crypt は使えない。ファイルシステムレベルで暗号化を行う eCryptfs を使う。ただし長過ぎるファイル名が使えなかったり、ファイルごとに暗号化データが付与されるためにディスク容量を余分に食うデメリットもある。動作保証されているファイルシステムは ext3 などだが特に問題なく動いた。

SMB 上に空ディレクトリ作ってマウントするまでは同じ。

4. 暗号化マウント

パッケージ ecryptfs-utils をインストール、カーネルモジュール ecryptfs を読み込み。

/etc/fstab に入れる前に直接マウントしてみる。 暗号化の方法などのオプションを聞かれた。

  • key type はパスフレーズを選択
  • plaintext passthrough は暗号化ディレクトリにある平文ファイルの読み書きを許可するかどうか。許可しない。(デフォルト)
  • filename encryption はファイル・ディレクトリ名も暗号化するか。ディレクトリ構造が読めてしまうのはよろしくないので有効化。
  • マウント元と先が同じなのはこのディレクトリへの書き込みを必ず ecryptfs を通すようにするため

詳細は man ecryptfs(7) にある。

$ sudo mount.ecryptfs /var/lib/postgresql /var/lib/postgresql
Unable to find a list of options to parse, defaulting to interactive mount
Select key type to use for newly created files: 
 1) tspi
 2) passphrase
Selection: 2
Passphrase: 
Select cipher: 
 1) aes: blocksize = 16; min keysize = 16; max keysize = 32
 2) blowfish: blocksize = 8; min keysize = 16; max keysize = 56
 3) des3_ede: blocksize = 8; min keysize = 24; max keysize = 24
 4) twofish: blocksize = 16; min keysize = 16; max keysize = 32
 5) cast6: blocksize = 16; min keysize = 16; max keysize = 32
 6) cast5: blocksize = 8; min keysize = 5; max keysize = 16
Selection [aes]: 
Select key bytes: 
 1) 16
 2) 32
 3) 24
Selection [16]: 
Enable plaintext passthrough (y/n) [n]: n
Enable filename encryption (y/n) [n]: y
Filename Encryption Key (FNEK) Signature [****************]: 
Unable to find a list of options to parse, defaulting to interactive mount
Attempting to mount with the following options:
  ecryptfs_unlink_sigs
  ecryptfs_fnek_sig=****************
  ecryptfs_key_bytes=16
  ecryptfs_cipher=aes
  ecryptfs_sig=****************
Mounted eCryptfs

最後に出てきたオプションに noauto,key=passphrase を加えて /etc/fstab に追加。パスフレーズを都度入力するようにした。

[email protected]:~# umount /var/lib/postgresql
[email protected]:~# mount --options-source-fource /var/lib/postgresql /var/lib/postgresql
Passphrase: 
Attempting to mount with the following options:
  ecryptfs_unlink_sigs
  ecryptfs_fnek_sig=****************
  ecryptfs_passthrough
  ecryptfs_key_bytes=16
  ecryptfs_cipher=aes
  ecryptfs_sig=****************
Mounted eCryptfs

この手のパスフレーズの管理として「USB メモリに鍵かパスフレーズを入れておいて復号化時にだけ読み込む。メモリは肌身はなさず持つ」が割と定番なのだけど、ここで採用しなかった理由。

  • カバンをもたずに出かけることが多い=家に鍵と pi が一緒に置かれた状況になりやすい
  • 置き場の問題で頻繁な抜き差しがしづらい
  • めったに再起動しない=パスフレーズの入力頻度が苦にならない

5. データを戻す

/var/lib/postgres にデータを入れると NAS 上には暗号化されたデータが書き込まれる。

# cd /var/lib/postgres ; find \| head -n5
.
./11
./11/main
./11/main/postmaster.opts
./11/main/pg_wal
# umount /var/lib/postgres
# cd /var/lib/postgres ; find \| head -n5
.
./ECRYPTFS_FNEK_ENCRYPTED.FWa1nQWjlbVg0EQqeLUkiIx7X7oFBthnp9Kg.eIjgTZv6zsog.---
./ECRYPTFS_FNEK_ENCRYPTED.FWa1nQWjlbVg0EQqeLUkiIx7X7oFBthnp9Kg.eIjgTZv6zsog.---/ECRYPTFS_FNEK_ENCRYPTED.FWZxOfyIkG0ptESF7rvMUbl1BEJjCucjZoiFOeCCSikP.3U4eTZRrUGYXk--
./ECRYPTFS_FNEK_ENCRYPTED.FWa1nQWjlbVg0EQqeLUkiIx7X7oFBthnp9Kg.eIjgTZv6zsog.---/ECRYPTFS_FNEK_ENCRYPTED.FWZxOfyIkG0ptESF7rvMUbl1BEJjCucjZoiFOeCCSikP.3U4eTZRrUGYXk--/ECRYPTFS_FNEK_ENCRYPTED.FWZxOfyIkG0ptESF7rvMUbl1BEJjCucjZoiFRDf5eIRIPt4qrWWZQQtnS---
./ECRYPTFS_FNEK_ENCRYPTED.FWa1nQWjlbVg0EQqeLUkiIx7X7oFBthnp9Kg.eIjgTZv6zsog.---/ECRYPTFS_FNEK_ENCRYPTED.FWZxOfyIkG0ptESF7rvMUbl1BEJjCucjZoiFOeCCSikP.3U4eTZRrUGYXk--/ECRYPTFS_FNEK_ENCRYPTED.FWZxOfyIkG0ptESF7rvMUbl1BEJjCucjZoiF.URRClErN6An0rfbemq3b---

使ってみて

従来は SSD だったものがネットワーク越しの HDD になるので速度が落ちるかと思ったがまったく気にならない。

いい感じなので開発用マシンで使っている elasticsearch も同じ構成にしてみた。ディスクの空きが少なくなるとすぐ書き込み不可になるのに閉口していたが、ここは nasne さんの広い胸(物理)を借りたい。Elasticsearch 自体を載せ替えなかったのは、開発のときにしか使わず24時間動かす必要がないから。なにより Java でリソース食いなので pi にはインストールしたくなかった。また、elasticsearch はディスク上のファイルが多く、ファイル単位で暗号化するものとは相性が悪いので暗号化はかけていない。

今後他のものも載せたくなったときのために ecryptfs のデフォルト設定を /root/.ecryptfsrc に移した。パスフレーズの管理についても、どれか一つの暗号化フォルダに鍵ファイルをまとめて置いておき、残りの復号化の際にはそこから読み込むようにしてもいいと思う。

まとめ

準備のコマンド: (sudo)

apt install -y cifs-utils ecryptfs-utils
modprobe ecryptfs

/root/.ecryptfsrc:

key=passphrase
ecryptfs_unlink_sigs
ecryptfs_key_bytes=16
ecryptfs_cipher=aes
ecryptfs_enable_filename_crypto=y
ecryptfs_passthrough

/etc/fstab: (冒頭4行は元から)

proc            /proc           proc    defaults          0       0
PARTUUID=********-01  /boot           vfat      defaults          0       2
PARTUUID=********-02  /               ext4      defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on\|off]  for that
//192.168.0.24/share1/pi/postgres /var/lib/postgresql cifs      guest,uid=postgres,gid=postgres,dir_mode=0750,nounix,vers=1.0 0 0
/var/lib/postgresql /var/lib/postgresql  ecryptfs  defaults,ecryptfs_fnek_sig=****************,ecryptfs_sig=****************,noauto 0 0

トラブルシューティング

マウント時にエラーが出る

# mount.ecryptfs /var/lib/elasticsearch /var/lib/elasticsearch Unable to link the KEY_SPEC_USER_KEYRING into the KEY_SPEC_SESSION_KEYRING; there is something wrong with your kernel keyring. Did you build key retention support into your kernel?

screen や tmux の中だとこのエラーが出るという報告

pg コマンドが動かない

pg_createcluster とか createdb でなぜかロケール周りの警告が出てこける。

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
    LANGUAGE = (unset),
    LC_CTYPE="en_GB.UTF-8"
    LC_NUMERIC=ja_JP.UTF-8
    LC_TIME=ja_JP.UTF-8
    LC_COLLATE=ja_JP.UTF-8
    LC_MONETARY=ja_JP.UTF-8
    LC_MESSAGES="en_GB.UTF-8"
    LC_PAPER=ja_JP.UTF-8
    LC_NAME=ja_JP.UTF-8
    LC_ADDRESS=ja_JP.UTF-8
    LC_TELEPHONE=ja_JP.UTF-8
    LC_MEASUREMENT=ja_JP.UTF-8
    LC_IDENTIFICATION=ja_JP.UTF-8
    LC_ALL = (unset),
    LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to the standard locale

dpkg-reconfigure locales で治った。

SMB をマウントできない

$ sudo mount.cifs //192.168.0.24/share1 -o guest
mount error(112): Host is down
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)

→SMBバージョンを指定する

パーミッション地獄

当初は nasne 全体を /media/nasne にマウントしたまま pi ユーザや postgres ユーザでフォルダを使い分けようとしていた。

syslog に残っていたので抜き出してみる。

  • uid を指定せずにマウント
    • Error: The cluster is owned by group id 99 which does not exist
  • uid=nobody にしてみる
    • Error: Config owner (postgres:111) and data owner (nobody:65534) do not match, and config owner is not root
  • dir_modeを指定しないと
    • FATAL: data directory “/var/lib/postgresql/11/main” has invalid permissions
    • DETAIL: Permissions should be u=rwx (0700) or u=rwx,g=rx (0750).

chown も利かない。結局ユーザごとにサブディレクトリをマウントするように構成を変えた。

一つ問題を潰したら別の難問が出てくるようなときは、アプローチの方向性が間違っていないか考えたほうがいい。

posted by かぷらす at 00:13| Comment(0) | 作業記録 | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください