Top/EBUG勉強会/20221126_NSHonFuguIta

河豚板でルータを作る ~ アプライアンスの製作例

EBUG 第83回会合 2022年11月26日
川俣吉広、kaw@on.rim.or.jp

概要

EBUGの勉強会では、2014年5月に実用で使う河豚板と題して特定のアプリケーションを動かすための専用機(時計表示端末)に仕立てる方法を紹介した。

今回はnsh (network config shell)の河豚板への組み込みを例にとり、現行バージョンの河豚板 7.2を用いたアプライアンスの製作手順を紹介する。

作業の流れ

導入作業の全体の流れは下図の通り:

                   初回起動          +-----------------------+         モード0で起動し、
[LiveUSBの作成]-->[起動(モード0)]-->|河豚板の設定:          |--+        OpenBSDの設定
                   手動              |  終了時のファイル保存 |  |        河豚板の設定
                                     |  起動時の自動読込     |  |
                                     |  etc...               |  |
                                     +-----------------------+  |
                                                                |
              +--------------------[reboot]---------------------+
              |                                                  
              |    2回目の起動       +-----------------------+         nsh関連の設定
              +-->[起動(モード3)]-->|nshのインストール・設定|--+        インストール
                   自動              +-----------------------+  |        ユーザアカウント作成
                                                                |        タイムゾーン
              +--------------------[reboot]---------------------+
              |                                                  
              |    3回目以降の起動   +-----------------------+         nshを運用しながら、
Power On----->+-->[起動(モード3)]-->|nshの運用              |--+      設定の修正・調整を実施
              ^    自動              +-----------------------+  |
              |                                                 |
Power Off<----+--------------------[halt or reboot]-------------+

nshについて

プロジェクトホームページ: https://www.nmedia.net/nsh/ より

NSH は、OpenBSD ベースのネットワークアプライアンスを対象とした CLI です。これは ifconfig, sysctl および route を独自のシンプルなコマンド言語と置き換えるもので 他のデーモンのコンフィギュレーションをひとつの場所にカプセル化し、効果的に アプライアンススタイルで使用するために /etc/netstart と /etc/rc の一部を置き換えています。
nshによってカプセル化されたデーモンとサービス: pf, ospfd, ospf6d, bgpd, ripd, ldpd, relayd, ipsecctl, iked, rad.Dvmrpd, sdpf6d, bgpd, ripd, ldpd, relayd, ipsecctl, iked, rad, dvmrpd, sasyncd, dhcpd, snmpd, sshd, ntpd, ifstated, tftp-proxy, ftp-proxy, tftpd, npppd, tftpd, npppd, tftp-proxy, ftp-proxy, tftpd、npppd、resolv.conf、inetd、smtpd、ldapd、ifstated

期間限定で以下のリンクを作成した

作者のChris Cappuccio氏はまた、フラッシュデバイスで動作するOpenBSDイメージを作成するflashrdの作者でもある。


LiveUSB版河豚板を作成

ダウンロード

ダウンロードミラー一覧からよさげな場所を選んでダウンロード

# ftp https://jp2.dl.fuguita.org/SHA256
Trying 160.16.199.59...
Requesting https://jp2.dl.fuguita.org/SHA256
100% |*******************************************************|   640       00:00
640 bytes received in 0.00 seconds (2.65 MB/s)
# ftp https://jp2.dl.fuguita.org/FuguIta-7.2-amd64-202211151.img.gz
Trying 160.16.199.59...
Requesting https://jp2.dl.fuguita.org/FuguIta-7.2-amd64-202211151.img.gz
100% |*******************************************************|   332 MB    00:32
348583114 bytes received in 32.14 seconds (10.34 MB/s)

ファイルの整合性チェック

MD5かSHA256のいずれかを用いてダウンロードしたファイルが壊れていないかチェックする。以下はSHA256の例:

# sha256 -C SHA256 FuguIta-7.2-amd64-202211151.img.gz
(SHA256) FuguIta-7.2-amd64-202211151.img.gz: OK

デバイスに書込み

# zcat FuguIta-7.2-amd64-nsh_test.img.gz | dd of=/dev/rsdXc bs=1m

/dev/rsdXcには適宜デバイス名を充当して実行。

初回起ち上げ

システムデバイスの指定

scanning partitions: sd0a sd0d sd0i
FuguIta's operating device(s): sd0a.

Which is FuguIta's operating device? [default: sd0a] ->

河豚板デバイスを一つだけ装着しているので、デフォルト(ENTERのみ押下)でOK。

MFSサイズの指定

available memory: 2031M

Enter mfs size.
  You can add suffix K, M, or G.
  % is a percentage of memory size.
  and %% is a percentage of the total memory and swap.
  otherwise considered "megabytes"

[default: 1523M] ->
set mfs size to 1523MB

デフォルトでは、実装メモリの75%をMFSに割り当てる。
(実測したところ、物理メモリは192MB程度以上実装されていれば使用可能となった)

起動モードの指定

LiveUSBの標準的なモードである起動モード0で起動。

Boot modes:
   0: fresh boot - standard mode as a live system
   1: fresh boot - less memory, faster boot
		  (/usr is non-writable, can't pkg_add)
   2: fresh boot - works using only RAM
		  (about 1GB or more of RAM required)
   3: boot with retrieving saved files from storage device
      or enter passphrase for an encrypted volume
   4: boot with retrieving saved files from floppy disk
   5: interactive shell for maintenance
-> 0
Running manual setup.

モード1での起動も可。この場合、運用中は完全オン・メモリで動作する。

この後、OSが動作するための基本的な設定について訊かれてゆくので、適宜入力してゆく。

rootパスワード

Changing password for root.
New password:
Retype new password:
passwd: password updated successfully

簡単すぎるパスワードは弾かれるので注意。

ネットワーク関連の設定

ここでの設定は、河豚板をネットワーククライアントとして使用することを想定している。

今回、ルータなどとしての使用を想定しているので、複数のネットワークインターフェースとその間のフォワーディングなどに関する設定を追加する必要がある。
それらの設定は、起動後にrootでログインして行うか、あるいはnsh内で行う。

ホスト名

Hostname with domain part (FQDN):
only host name without domain part is also OK.
-> nshrouter.local

IPバージョン

今回はIPv4のみでの運用を想定

IP protocol version(s) to be enabled: 4, 6, 46, 64 or "none"
  4: enable only IPv4
  6: enable only IPv6
  46: give priority to IPv4 name resolution
  64: give priority to IPv6 name resolution
  none: operate as standalone
[64] ->4

ネットワークインターフェース

Network Interfaces: Choose one

  NIC	 type	   Name
-------- ----- ------------
    vio0 ether unknown
[vio0] ->

IPアドレス、デフォルトルート

IPv4 - address and routing:
  Enter "auto" or "IPv4_address[/mask] [default_gateway]"
  "auto" is an automatic setting by DHCP.
  The "/mask" part can be specified in either format,
  such as "/255.255.255.0" or "/24".
  If there is no default gateway,
  set the second field to "none" or leave it blank.
[auto] -> 100.64.1.3/31 100.64.1.2

ネームサーバ

DNS servers: up to 3 IP addresses, separated by spaces
-> 9.9.9.9

ログイン方法

Do you login with C)onsole or X) Window System?
[default: C] ->

この後、OpenBSD本来のブートシーケンスが続き、最後にログインプロンプトが表示される。

設定

起動が完了し、ログインプロンプトが表示されたら、先ほど設定したパスワードを用いてrootでログインし、以降の作業を行う。

タイムゾーン

nshrouter# date
Tue Nov 22 05:28:53 UTC 2022
nshrouter# ln -sf usr/share/zoneinfo/Asia/Tokyo /etc/localtime
nshrouter# date
Tue Nov 22 14:29:02 JST 2022

シャットダウン時のファイル自動保存

河豚板では、MFS上のデータをUSBメモリ等に保存するにはusbfadmというユティリティを使うが、/etc/rc.shutdownを以下のように編集することでシャットダウン時にusbfadmを実行し、自動的に保存を行うようになる。

nshrouter# vi /etc/rc.shutdown

force_umount=No   # set Yes for forced umount /ram at shutdown
force_resync=Yes  # set Yes to re-sync at shutdown  <== これをYesに変更

# stop_all_daemons - to eliminate all daemons at umount /ram
#
stop_all_daemons () {
    :
    :

起動時の設定の自動化

USBメモリ内のnoasksというファイルを編集することにより、起動時に手動で入力していた設定が自動化される。
前回の終了時に保存したファイルの読み込みも、このnoasks内で指定する。

nshrouter# mount /dev/sd0d /mnt
nshrouter# vi /mnt/livecd-config/7.2/amd64/noasks

#
# noasks - parameter settings for non-interactive boot
#
# Make statements uncommented
# Then assign real values
#
#
# FuguIta system device
#   - Use one of two lines
#noask_rdev=sd0a  # device name format
noask_rdev=3b5447abd772121d.a  # DUID format  <==河豚板のシステムデバイス
#                                                常に同じデバイスを指定できる
# mfs size in MB                                 ので、DUID (Disklabel Unique ID)
noask_umem=75%  <==MFSのサイズ                   による指定を推奨
#
# boot mode
noask_setup_rw_mode=3  <==起動モード 3=保存されたファイルを読み込んで起動
#
# storage device
#   - Use one of two lines
#noask_confdev=sd0d  # device name format
noask_confdev=3b5447abd772121d.d  # DUID format  <==ファイルを読み込むデバイス
#                                                   noask_rdevと同じくDUID推奨
# data set name in USB flash drive
noask_confdir=nshrouter  <==読み込むファイルの保存名

nshrouter# umount /mnt

リブートの動作確認

ここまでの設定をとりあえず保存

usbfadmを起動し、保存先、保存名を指定して保存を実行

nshrouter# usbfadm

Welcome to usbfadm.
USB flash drive administration tool for FuguIta

 Version/Arch: 7.2/amd64  (FuguIta-7.2-amd64-202211151)
    Boot mode: manual
Target device: not set
Data saved as: not set

readline capability available
TAB to complete the reserved words

Type ? for help.

? : ? ->target

Searching storage device
Please make sure the device inserted.
Then press ENTER ->
sd0a +sd0d
target device->sd0d

sd0d : ? ->saveas
Name of saved data->nshrouter

Your data will be saved as ``nshrouter''.

sd0d : nshrouter ->sync

Sync current mfs as ``nshrouter'' , OK? [y/N] -> y

34.4MiB 0:00:04 [8.57MiB/s] [=================================]105% ETA 0:00:00
waiting for pax to finish ... syncing ... done.

sd0d : nshrouter ->quit

Bye bye...
nshrouter# 

リブート

nshrouter# reboot

チェック項目

nsh関連の設定

nshのインストール

packagesからインストールする

nshrouter# pkg_add nsh
quirks-6.42(signed) 2022-11-20T19:34:54Z
quirks-6.42:ok
Ambiguous:
a	0: <None>
	1: nsh-1.0.20220919
	2: nsh-1.0.20220919-static
Your choice: 1
nsh-1.0.20220919:ok
nshrouter# nsh
% NSH v1.0
nshrouter.local/quit
% Session terminated.
nshrouter#

nsh-1.0.20220919-staticは、nshの静的リンク版(chrootして実行したい時などに使用?)。

注: 時計がずれているとTLS通信が失敗し、pkg_addが不成功に終わる場合がある。その場合は、dateコマンドを用い、手動で時刻合わせをするか、あるいは、

nshrouter# rdate ntp.nict.jp

などとして時刻を合わせる。

nshの実行アカウントを作成

adduserコマンドは初回実行時、adduser.confの作成について色々訊かれるが、全部デフォルト(ENTER押下)でよい。

nshrouter# adduser nsh
Couldn't find /etc/adduser.conf: creating a new adduser configuration file
Reading /etc/shells
Enter your default shell: csh ksh nologin nsh sh [ksh]:
Your default shell is: ksh -> /bin/ksh
Default login class: authpf bgpd daemon default pbuild staff unbound vmd xenodm
[default]:
Enter your default HOME partition: [/home]:
Copy dotfiles from: /etc/skel no [/etc/skel]:
Send welcome message?: /path/file default no [no]:
Do not send message(s)
Prompt for passwords by default (y/n) [y]:
Default encryption method for passwords: auto blowfish [auto]:
Use option ``-silent'' if you don't want to see all warnings and questions.

Reading /etc/shells
Check /etc/master.passwd
Check /etc/group

Ok, let's go.
Don't worry about mistakes. There will be a chance later to correct any input.
Enter username []: nsh
Enter full name []: Network SHell
Enter shell csh ksh nologin nsh sh [ksh]:
Uid [1000]:
Login group nsh [nsh]:
Login group is ``nsh''. Invite nsh into other groups: guest no
[no]: wheel
Login class authpf bgpd daemon default pbuild staff unbound vmd xenodm
[default]:
Enter password []:
Enter password again []:

Name:	     nsh
Password:    ****
Fullname:    Network SHell
Uid:	     1000
Gid:	     1000 (nsh)
Groups:	     nsh wheel
Login Class: default
HOME:	     /home/nsh
Shell:	     /bin/ksh
OK? (y/n) [y]: y
Added user ``nsh''
Copy files from /etc/skel to /home/nsh
Add another user? (y/n) [y]: n
Goodbye!
nshrouter# exit

OpenBSD/amd64 (nshrouter.local) (tty00)

login: nsh
Password:

nshはroot権限でコマンドを実行するので、nshアカウントはwheelグループに所属させておく。

/home/nsh/.profileの編集

ログイン直後にroot権限でnshが実行されるようにする。

nshrouter$ vi ~/.profile
exec doas /usr/local/bin/nsh
exit  # for fail safe

pkg_addでnshをインストールすると、/etc/shellsにnshが登録されるので、これをログインシェルとしてもいいような気もするが、マニュアルページではこの方法は推奨されていない。

nsh(8) Section 7 > Adding system users to:
Do NOT add nwrapper (or nsh) to /etc/shells. They should not be entered here.

/etc/doas.confの編集

ユーザnshがパスワード無しでroot権限のnshを実行できるように設定

nshrouter# echo permit nopass nsh cmd /usr/local/bin/nsh > /etc/doas.conf

設定の保存

以上で、ユーザアカウントnshでログインするとnshが起動するようになった。 念の為、ここまでの状態をUSBメモリに保存しておく

nshrouter# usbfadm -r
========================================
= Sync /dev/sd0d with current mfs as nshrouter
=
sending incremental file list
etc/fstab
            154 100%    0.00kB/s    0:00:00 (xfr#1, ir-chk=1078/1094)
            :
            :
var/spool/smtpd/purge/2135993693/
var/spool/smtpd/temporary/
nshrouter# 

一度、保存を行っている場合はコマンドラインから usbfadm -r で保存を再実行できる。

データ保存の初回実行時はpaxを使用し、次回からはrsyncによる差分転送を行う。

トラブルシューティング

OpenNTPDの設定で警告が出る

% NSH v1.0
nshrouter.local/enable
nshrouter.local(p)/ntp edit
servers ntp.nict.jp
configuration OK
nshrouter.local(p)/ntp disable
-s option no longer works and will be removed soon.      <== ntpdの -s オプションは古いので
Please reconfigure to use constraints or trusted servers.    ntpdがこの警告を出す。
nshrouter.local(p)/ntp enable
-s option no longer works and will be removed soon.
Please reconfigure to use constraints or trusted servers.
nshrouter.local(p)/!ntpctl -s all  <== ntpdの同期状態は、ntpctlコマンドを子プロセスとして
0/5 peers valid, clock unsynced        呼出し、確認する。

peer
   wt tl st  next  poll		 offset	      delay	 jitter
133.243.238.163 from pool ntp.nict.jp
    1  3  1    2s    5s		    ---- peer not valid ----
133.243.238.164 from pool ntp.nict.jp
    1  3  1    4s    7s		    ---- peer not valid ----
61.205.120.130 from pool ntp.nict.jp
    1  3  1    2s    5s		    ---- peer not valid ----
133.243.238.243 from pool ntp.nict.jp
    1  3  1    2s    5s		    ---- peer not valid ----
133.243.238.244 from pool ntp.nict.jp
    1  3  1    3s    6s		    ---- peer not valid ----
nshrouter.local(p)/

NTPの参照サーバがntp.pool.orgからntp.nict.jpに変っているので、設定変更自体は反映されている。

nshからreboot/haltでファイルが自動保存されない

nshrouter.local(p)/reboot  <= nshの特権モードでrebootを実行
% Reboot initiated
syncing disks... done  <= ファイルが自動保存されず、いきなりreboot
vmmci0: powerdown         /etc/rc.shutdownが実行されない
rebooting...

Using drive 0, partition 3.
Loading......
probing: pc0 com0 mem[638K 2046M a20=on]
disk: hd0+
>> OpenBSD/amd64 BOOT 3.55/*

nshのソースで該当部分を見てみる
commands.c

/*
 * Reboot
 */
int
nreboot(void)
{
        printf ("%% Reboot initiated\n");
        if (reboot (RB_AUTOBOOT) == -1)  <== reboot(2)が呼び出されている
                printf("%% reboot: RB_AUTOBOOT: %s\n", strerror(errno));
        return(0);
}
               
int
halt(void)
{
        printf ("%% Shutdown initiated\n");
        if (reboot (RB_HALT) == -1)  <== reboot(2)が呼び出されている
                printf("%% reboot: RB_HALT: %s\n", strerror(errno));
        return(0);
}

/etc/rc.shutdownが呼び出されないのは、nshがreboot(2)を直接呼出してOSの停止処理を行っているため。

改修

ports treeを展開し、/usr/ports/shells/nsh 内にパッチファイルを追加

/usr/ports/shell/nsh/patches/patch-ctl_c

--- ctl.c.orig	Tue Sep 20 04:18:55 2022
+++ ctl.c	Tue Nov 22 09:37:12 2022
@@ -412,7 +412,7 @@
 char *ctl_ntp_test[] = { NTPD, "-nf", REQTEMP, NULL };
 struct ctl ctl_ntp[] = {
 	{ "enable",     "enable service",
-	    { NTPD, "-sf", REQTEMP, NULL }, NULL, DB_X_ENABLE, T_EXEC },
+	    { NTPD, "-f", REQTEMP, NULL }, NULL, DB_X_ENABLE, T_EXEC },
 	{ "disable",    "disable service",
 	    { PKILL, table, "ntpd", NULL }, NULL, DB_X_DISABLE, T_EXEC },
 	{ "edit",       "edit configuration",

/usr/ports/shell/nsh/patches/patch-commands_c

--- commands.c.orig	Tue Sep 20 04:18:55 2022
+++ commands.c	Tue Nov 22 09:37:12 2022
@@ -1962,7 +1962,7 @@
 nreboot(void)
 {
 	printf ("%% Reboot initiated\n");
-	if (reboot (RB_AUTOBOOT) == -1)
+	if (system("/sbin/reboot") == -1)
 		printf("%% reboot: RB_AUTOBOOT: %s\n", strerror(errno));
 	return(0);
 }
@@ -1971,7 +1971,7 @@
 halt(void)
 {
 	printf ("%% Shutdown initiated\n");
-	if (reboot (RB_HALT) == -1)
+	if (system("/sbin/halt -p")  == -1)
 		printf("%% reboot: RB_HALT: %s\n", strerror(errno));
 	return(0);
 }

portsにてパッチを適用し、ビルド

nshrouter# pkg_delete nsh
nsh-1.0.20220919:ok
Read shared items:ok
nshrouter# cd /usr/ports/shells/nsh
nshrouter# make install
===>  Checking files for nsh-1.0.20220919
>> Fetch https://github.com/yellowman/nsh/archive/fcad0f6af197ab0c96e2c5410c....
>> (SHA256) nsh-1.0.20220919-fcad0f6a.tar.gz: OK
===> nsh-1.0.20220919 depends on: sqlite3-* -> sqlite3-3.39.3
===>  Verifying specs:	c curses edit sqlite3
===>  found c.96.2 curses.14.0 edit.5.2 sqlite3.37.20
===>  Extracting for nsh-1.0.20220919
===>  Patching for nsh-1.0.20220919    <== 今ほど追加したパッチが適用されている
===>   Applying OpenBSD patch patch-commands_c
Hmm...	Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- commands.c.orig	Tue Sep 20 04:18:55 2022
|+++ commands.c Tue Nov 22 09:37:12 2022
--------------------------
Patching file commands.c using Plan A...
Hunk #1 succeeded at 1962.
Hunk #2 succeeded at 1971.
done
===>   Applying OpenBSD patch patch-ctl_c
Hmm...	Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- ctl.c.orig Tue Sep 20 04:18:55 2022
|+++ ctl.c	Tue Nov 22 09:37:12 2022
--------------------------
Patching file ctl.c using Plan A...
Hunk #1 succeeded at 412.
done
===>  Compiler link: clang -> /usr/bin/clang
===>  Compiler link: clang++ -> /usr/bin/clang++
===>  Compiler link: cc -> /usr/bin/cc
===>  Compiler link: c++ -> /usr/bin/c++
===>  Generating configure for nsh-1.0.20220919
===>  Configuring for nsh-1.0.20220919
===>  Building for nsh-1.0.20220919
cc -O2 -pipe -Wmissing-prototypes -Wformat -Wall -Wbad-function-cast ....
sh /ram/usr/ports/pobj/nsh-1.0.20220919/nsh-fcad0f6af197ab0c96e2c5410....
        :
        :
cc -O2 -pipe -Wmissing-prototypes -Wformat -Wall -Wbad-function-cast ....
cc -L/usr/local/lib  -o nsh arp.o compile.o main.o genget.o commands ....
===>  Faking installation for nsh-1.0.20220919
/usr/ports/pobj/nsh-1.0.20220919/bin/install -c -s -m 755 /usr/ports ....
===>  Building package for nsh-1.0.20220919
Create /usr/ports/packages/amd64/all/nsh-1.0.20220919.tgz
        :
        :
Link to /usr/ports/packages/amd64/ftp/nsh-1.0.20220919.tgz
===>  Verifying specs: c curses edit sqlite3
===>  found c.96.2 curses.14.0 edit.5.2 sqlite3.37.20
===>  Installing nsh-1.0.20220919 from /usr/ports/packages/amd64/all/
nsh-1.0.20220919:9ok
nshrouter# nsh
% NSH v1.0
nshrouter.local/enable

雑感

nshの利用についてはここまでで、ネットワーク機能の本格的な設定はこれから。

nshの初期化について

write-configを実行すると/etc/nshrcに設定が保存される。マニュアルでは、 On the next startup of the system, this saved configuration is used. という記述があるが、次の起動では保存された設定は反映されない。

nshの2つのコマンドラインオプション、

-c config-script-file
nsh execute the command(s) in the config-script-file. This is typically used to implement scripted changes to configuration.
-i rcfile
load the inital system configuration from the command(s) in the rcfile. This is typically used to clear the configuration load a full nsh configuration script from rcfile .

を指定しても、ファイル内のnshコマンドを実行した後、終了する。
/etc/nshrcは以下のように記述した方がよいかもしれない。

if [ -r /etc/nshrc ]; then
    doas /usr/local/bin/nsh -i /etc/nshrc
fi
exec doas /usr/local/bin/nsh
exit  # fail safe

この構築手順を用いた他の事例

設定手順のダウンロードから起動時の設定の自動化までは、河豚板でなにがしかのシステムを構築する場合の定石。
以下の事例でもこの手順に従ってシステムを構築している。


Front page   New Page list Search Recent changes   Help   RSS of recent changes