はじめに
自宅ファイルサーバーの起動用USBメモリのコントローラーがやられたようで認識が怪しくなったため、OSのバージョンアップも兼ねて更新を行いました。更新のたびに細かな部分を忘れて毎回調べるのに時間がかかってしまうため、作業記録(備忘録)として残すことにしました。
自宅サーバーの運用に特化しているため、あまり参考にならないかもしれませんが。。。
サーバー構成
- ASRock N100DC-ITX RAM32GB
- NIC Realtek RTL8126(PCIe ×1)
- SATA HDD 18TB×3(ZFS RAIDZ1)
- SATA SSD 128GB(ZIL+L2ARC)
- M.2形状のPCIe→SATAコントローラを増設
FreeBSD14.4(カスタムカーネル)+Samba4.19で構成しています。
この記事で書くこと
- FreeBSDのカーネルコンパイルおよび環境構築の手順
- jp.106(JIS配列)のキーボードを前提としたFreeBSDブートローダーを作成するための設定
- GELIによるルートパーティションの暗号化(ディスクの接続(SATA/SAS/USB)に依らず起動可能な構成方法)
この記事で書かないこと
- Sambaの細かな設定方法
- ZFSアレイの構築・設定や運用方法
構築のポイント
1. ブート時のキーボード配列問題の解消
FreeBSDルートパーティションをGELIで暗号化すると、起動時にルートをマウントする前の段階でGELIのパスフレーズ入力を求められまが、その時点では当然ルートパーティション内にあるrc.confが読まれていないためキー配列がUS配列となっており、JISキーボードだと記号を含むパスフレーズ入力が面倒です。
これを解決するため、デフォルトのキーマップをJIS配列に書き換えたカーネルをビルドして使用します。
ちなみにEFIブートローダーのキー配列もソースを書き換えれば変更可能ですが、労力に見合わないためEntireDiskの暗号化は行いません。
2. ルートパーティションの暗号化
一旦シャットダウンしたらパスフレーズを知る自分以外は起動不能な形で構成しておきます。まぁ、別のディスクをつないで起動することを防げるわけではないので気休めです(共有に使用するZFSボリュームも当然暗号化はしてあります)。
3. ポータビリティの確保
ddイメージを焼きこめばHDD/SSD/USBメモリを問わずゼロコンフィグで起動できるように構成しています。
4. USBメモリの寿命対策
USBメモリはNANDフラッシュメモリの特性からくる上書き回数の限界があり、普通にマウントして使用すると/varへのログ出力などで劣化が進み限界を超えるとにOSがクラッシュしてしまうので下記で対策します。
-
tmpfsの使用:
/tmpや/varなど頻繁に更新されるディレクトリをmfsでマウントし、物理ディスクへの書き込みを回避します - Read-Only Root: ルートパーティションを読み取り専用でマウントして運用できるように構成します
全体の流れ
- ビルド環境の準備: カーネルソースの取得とビルド環境の整備
- カスタムカーネルの構築: JISキーボード対応および最適化されたカーネルのコンパイル
- ディスクイメージの作成: イメージファイル上でのOSインストールと詳細設定
- USBメモリへの書き出し: 完成したイメージを物理メディアへ展開
用意するもの
- サーバーで使用するバージョンのFreeBSDISOイメージかMemstickイメージ(今回は 14.4-RELEASEを使用)
- 上記のFreeBSDが動作する物理PCまたは仮想環境(今回はVMWare Workstation Proを使用)
- ディスクは64GBあれば十分です。CPU/RAMはケチらない方が良いかと(コンパイルに時間がかかる)。
- サーバー起動用USBメモリ
- 入れるパッケージによって必要容量は変わります(Sambaのみの最小構成なら4GBあれば十分です。)
ステップ 0. FreeBSDをインストール
普通にFreeBSDをインストールします。使い捨て環境なのでUFSで良いですし、細かくパーティションを割る必要もなく、むしろ/に全容量割り当てる方が後の作業で不要な制限もかからないです。
ステップ 1. ビルド環境の準備
カスタムカーネルをビルドするためのベース環境を整えます。
gitのインストール
pkg update
pkg install git
カーネルソースのクローン
インストールするバージョンと一致するrelengブランチをクローンします。
cd /usr/src
git clone --depth 1 --branch releng/14.4 https://git.FreeBSD.org/src.git .
エラッタ情報を確認して想定している用途で支障がありそうなら問題ないバージョンを使用する、stableブランチからパッチをcherry-pickするなどしておきます。
ステップ 2. カスタムカーネルの構築
JIS配列キーボードのデフォルト設定とGELIサポートを含むカーネルを作成します。
カーネルコンフィグファイルの作成
GENERICをベースにカスタムのコンフィグファイルを作成します。
cd /usr/src/sys/amd64/conf
cp GENERIC CUSTOM
vi CUSTOM
不要なデバイスや機能を削ります。(不要なものはnooptionsやnodeviceにする)
不要に見えて別のデバイスや機能と関連していたりカーネルやユーザーランドとの兼ね合いで必須だったりするものもあるので判断がつかないものは触らない方が良いです。
以下のオプションを追加・編集します。
# USB キーボードサポート
device ukbd
options UKBD_DFLT_KEYMAP
makeoptions UKBD_DFLT_KEYMAP=jp.106
# キーボード multiplexer サポート
device kbdmux
options KBDMUX_DFLT_KEYMAP
makeoptions KBDMUX_DFLT_KEYMAP=jp.106
# GEOM_ELI サポート (ディスク暗号化)
options GEOM_ELI
# tmpfs サポート
options TMPFS
ビルド最適化の設定 (/etc/make.conf)
cat <<_EOF_ >> /etc/make.conf
KERNCONF=CUSTOM #上で作成したカーネルコンフィグの名前を入れます
LDFLAGS+=-Wl,-O1
STRIP=-s
NODEBUG=yes
MK_PROFILE=no
USA_RESIDENT=NO
MODULES_OVERRIDE=zfs xdr fdescfs
_EOF_
最近のバージョンでは、CFLAGSやCOPTFLGは最適化フラグ(-O2 -pipe)が設定された状態が標準のため、それらをあえて設定する必要もありません。
不要機能の除外設定 (/etc/src.conf)
不要なツールや機能をビルド対象から除外してフットプリントを削減します。
カーネルコンフィグと重複しているように思えるかもしれませんが、こちらはユーザーランドの設定です。
cat <<_EOF_ > /etc/src.conf
WITHOUT_GAMES=yes
WITHOUT_HTML=yes
WITHOUT_LPR=yes
WITHOUT_BLUETOOTH=yes
WITHOUT_WIRELESS=yes
WITHOUT_FLOPPY=yes
WITHOUT_SENDMAIL=yes
WITHOUT_NIS=yes
WITHOUT_NLS=yes
WITHOUT_FTP=yes
WITHOUT_TELNET=yes
WITHOUT_INFO=yes
WITHOUT_EXAMPLES=yes
WITHOUT_SHAREDOCS=yes
WITHOUT_DEBUG_FILES=yes
WITHOUT_ASSERT_DEBUG=yes
WITHOUT_PROFILE=yes
WITHOUT_DTRACE=yes
WITHOUT_LLDB=yes
WITHOUT_LLVM_TARGET_ALL=yes
WITHOUT_KTRACE=yes
WITHOUT_TESTS=yes
CPUTYPE?=skylake
_EOF_
CPUTYPEは起動対象のデバイスに合わせてください(対応範囲を広くしたい場合は無指定でも可)。
src.confの設定内容は下記コマンドで詳細な情報を得ることができます。
man src.conf
IPv4のみの環境の場合、WITHOUT_INET6を指定したくなりますが、pkgのSamba使用する場合にRPCコールに失敗してWindowsエクスプローラから正常に使用できなくなるなどの弊害があるので設定しない方が無難です。
WITHOUT_INET6を指定した場合はpkgのSambaは使用せずportsからINET6サポートを外してSambaをコンパイル・インストールする必要があります。
コンパイルの実行
-jオプションの引数はコンパイルの並列数なので使用している環境のCPUコア数などに合わせてください。
cd /usr/src
make -j12 buildworld
make -j12 buildkernel
ステップ 3. ディスクイメージの作成
USBメモリに直接インストールしても構いませんが、最近のUSBメモリはコストダウンのためにランダムライトが死ぬほど遅いものが多く、頻繁にプチフリーズを起こすため、一旦仮想ディスクイメージを作成してddで書き込んだ方が良いです。
仮想ディスクの作成とパーティショニング
truncate -s 8g /tmp/image #-sはディスクのサイズ、USBメモリの容量以下にすること
mdconfig -a -t vnode -f /tmp/image
これにより /dev/md0 として仮想デバイスが作成されます。
続いてパーティションを作成していきます。
gpart create -s gpt md0
gpart add -t efi -b 40 -s 409560 md0
gpart add -s 512m -t freebsd-ufs md0
gpart add -t freebsd-ufs -l bsdroot md0
newfs_msdos -F 32 -c 1 -L EFISYS md0p1
newfs md0p2
GELIによるディスク暗号化(パスフレーズ方式)
# 暗号化の初期化 (パスフレーズを求められます)
# キーファイルを設定することもできます(その場合はブートローダが認識できる場所にキーファイルを置いてloader.confのgeli_~の設定を有効にする必要があります)
geli init -s 4k -b gpt/bsdroot
# パスフレーズを入力
geli attach gpt/bsdroot
newfs /dev/gpt/bsdroot.eli
システムのインストール
ビルドしたworldとカーネルを作成した仮想デバイス上に展開します。
mount /dev/gpt/bsdroot.eli /mnt
DESTDIR=/mnt make installworld
DESTDIR=/mnt make distribution
DESTDIR=/mnt make installkernel
EFIブートローダーの配置
mkdir -p /efi
mount -t msdosfs /dev/da1p1 /efi
mkdir -p /efi/EFI/BOOT
cp /mnt/boot/loader.efi /efi/EFI/BOOT/BOOTX64.EFI
umount /efi
loader.conf の設定
cat <<_EOF_ >> /mnt/boot/loader.conf
#ルートパーティションのGELIのkeyfileを指定する(init時に指定した場合)
#geli_bsdroot_keyfile_load="YES"
#geli_bsdroot_keyfile_type="gpt/bsdroot:geli_keyfile"
#geli_bsdroot_keyfile_name="filename.key"
# diskid/gptidベースのGEOMデバイス検出を無効化
kern.geom.label.disk_ident.enable="0"
kern.geom.label.gptid.enable="0"
# ELIパスフレーズ入力試行回数
kern.geom.eli.tries="1"
# CAMバスのデバイス認識待ち時間を長めに設定(CAMバス登録失敗によるマウントエラー防止)
kern.cam.boot_delay="10000"
# ルートパーティションのマウントポイントを指定
vfs.root.mountfrom="ufs:/dev/gpt/bsdroot.eli"
_EOF_
mountrootをGPTラベルで指定しているため、ブート時のGELIパスフレーズ入力時に
passphrase for da0p3:
を失敗させて
passphrase for gpt/bsdroot:
にフォールバックさせる必要があります。
(デバイス名よりもラベル検出を優先させることは可能なんですかね?ご存じの方がいましたら教えてください)
ブートローダーの配置
mkdir -p /mnt2
mount /dev/md0p2 /mnt2
cd /mnt
tar cf - boot | tar xf - -C /mnt2
umount /mnt2
chroot環境での初期設定
chrootした先でdevfsを使用できるようにする。
mount -t devfs devfs /mnt/dev
chroot /mnt
ホストIDの生成
uuidgen > /etc/hostid
rootパスワードの設定
passwd root
ユーザーの追加(Sambaのユーザーなど)
pw useradd sambauser -c "Samba User" -s /sbin/nologin
DNS設定
以下は一例です。設定内容は各人の環境に合わせて変更してください。
cat >>_EOF_
nameserver 192.168.1.1
_EOF_ >> /etc/resolv.conf
各種設定
以下は一例です。設定内容は各人の環境に合わせて変更してください。
sysrc ifconfig_re0="192.168.1.3 netmask 255.255.255.0"
sysrc defaultrouter="192.168.1.1"
sysrc tmpmfs="YES"
sysrc tmpsize="32m"
sysrc varmfs="YES"
sysrc varsize="32m"
sysrc sshd_enable="YES"
sysrc ntpdate_enable="YES"
sysrc ntpdate_hosts="ntp.nict.jp"
カーネルのデフォルトkeymapがjp.106のためkepmapは無くてもよい
タイムゾーン・NTPの設定
tzsetup Asia/Tokyo
vi /etc/ntp.conf
# 設定例
# server time.google.com iburst
sysrc ntpd_enable="YES"
fstab
cat <<_EOF_ >> /etc/fstab
/dev/gpt/bsdroot.eli / ufs ro 1 1
devfs /dev devfs rw 0 0
_EOF_
省電力設定
sysrc powerd_enable="YES"
sysrc powerd_flags="-a hiadaptive -i 25 -r 70 -N"
パッケージのインストール
用途に合わせてSambaなどを導入します。
pkg update
pkg install samba419
#その他必要なパッケージ
#/usr/local/etc/smb4.confに共有の設定
pdbedit -a smbuser
sysrc samba_server_enable="YES"
その他、各自の用途に合わせて設定を進めていきます。
番外:Realtek製NIC使用時のWatchdog timeout対策
Realtek製NICはビルトインのreドライバで運用すると高負荷時にwatch dogtimeoutのエラーを吐いて無応答になってしまい再起動するまで使用不能になる問題があります。
pkgからRealtekのオフィシャルドライバをインストールすることで回避できます。
/var配下の復元設定
/varをmfsでマウントするようにしているのでシャットダウンのたびに/var配下のデータが揮発してしまいますが、ディレクトリ構造と揮発したら困るデータをブート時に復元するようにしておきます。
# パッケージキャッシュは不要なので削除しておきます
# (後の構成変更でpkgを追加する場合も再ダウンロードされる)。
pkg clean -a
rm -rf /var/db/pkg/repos/*
mtree -c -d -i -n -k uname,gname,mode,tags /var > /etc/mtree/BSD.var.dist
# 復元用スクリプト
cat <<_EOF_ >> /etc/rc.d/restorevardb
#!/bin/sh
# PROVIDE: restorevardb
# REQUIRE: var
# BEFORE: samba_server
. /etc/rc.subr
name="restorevardb"
rcvar="restorevardb_enable"
# Configuration variables
: ${restorevar_srcdir:=}
start_cmd="${name}_start"
stop_cmd=":"
restorevardb_start() {
for arc in ${restorevardb_files}; do
if [ -e "${restorevardb_srcdir}/$arc" ]; then
tar xvf "${restorevardb_srcdir}/${arc}" -C /var/db
fi
done
}
load_rc_config ${name}
run_rc_command "$1"
_EOF_
chmod +x /etc/rc.d/restorevardb
sysrc restorevardb_enable="YES"
sysrc restorevardb_srcdir="/usr/local/var/restorevardb"
sysrc restorevardb_files="pkg.xz samba4.xz"
mkdir -p /usr/local/var/restorevardb
cd /var/db
tar cJf - pkg > /usr/local/var/restorevardb/pkg.xz
tar cJf - samba4 > /usr/loval/var/restorevardb/samba4.xz
chrootから抜けて後始末
sync
exit
umount /mnt/dev
umount /mnt
geli detach /dev/md0p3.eli
mdconfig -d -u 0
ステップ 4. USBメモリへの書き出し
USBメモリを接続し、ディスクイメージを書き込んでGPTのセカンダリパーティションテーブルを修復します。
dd if=/tmp/image of=/dev/da1 bs=10485760
gpart recover da1
da1 の部分は書込対象のデバイス合わせて変更してください。
誤ったデバイスを指定すると、接続されている他のディスクのデータを破壊する恐れがあります。
以上となります。
機械学習ブームでLinuxの認知度は爆上がりしている一方でFreeBSDナニソレ?という方も多いと思いますが、シンプルでわかりやすく堅牢なOSですので一度触ってみてはいかがでしょうか?
参考文献