自宅サーバー構築譚：dhcpサーバ冗長化

[自宅サーバー構築譚：基本構想](https://qiita.com/katz_engineer/items/cb592be44daa28a51d9b)に基づく自宅サーバー構築の続きです。

最初に立てるサービスとして、 DHCP サーバの冗長化を図ります。

# 能書き

一般的な家庭においては、家庭内 LAN に接続されるマシンの IP アドレスは無線 LAN ルータなどの内蔵 DHCP サーバが割り振る事になります。これを無効化する設定も可能ですけど、まあ普通はしませんよね。わざわざ手間を掛けて、今時どうでもいいような枯れた技術である DHCP サーバをインストール・設定する人はあんまり居ないでしょう。

今回はその点でちょっとしたイタズラです。

ちょっとやそっとでは機能を停止しないような、可用性の高いパソコン環境を家庭内に構築する事は私の長年の夢でした。というか今まで何度も痛い目に遭って不本意ながら涙目の思い出を積み上げてきてしまった私としては、堅牢な環境の構築は非常に重要な目標なのです！（力説）

などという話は[以前](https://qiita.com/katz_engineer/items/04396d58add14cb63573)も挑戦した訳ですが。今回はUbuntuでの実施になります。

という訳で今回、ちょっとだけ言葉などを勉強しました。

私の目指してる、二重化による高可用性サーバはHAクラスタと言うらしいですな。Linuxを利用したクラスタはLinuxクラスタと呼ばれます。今回はその第一歩としてIPアドレスを整備する事になります。

そんな訳で DHCP サーバの多重化を試みます。同時に IP アドレス設定に関する覚え書きを残せるようにします。家庭向け無線 LAN ルータの内蔵 DHCP サーバではコメントを残す機能なんてありませんので。AirMac では残せたんですがね。

# 我が家 IP アドレス体系

DHCP サーバを立てる前に、家庭内の IP アドレス体系を見直します。[以前同じ事をやりました](https://qiita.com/katz_engineer/items/04396d58add14cb63573)が、ほんの少し変更します。クラスBにして、第3オクテットで分類して使い分ける方針は同じ。そこから HA クラスタを意識して下記のように微調整しました。

DHCP プロトコルについて。プロトコルの仕様は、LAN 内に複数の DHCP サーバが在っても問題無く動くようになっているそうです。問題は DHCP サーバの IP アドレス管理が同期されない事で、 IP アドレスが重複して発行されてしまう事態になりかねない点にあります。だから DHCP サーバ毎に IP アドレス範囲を分離する事で、多重発行を防ぐようにすれば良い、との事。

我が家のネットワークアドレス: **172.16.0.0/16**

<dl>
<dt>172.16.0.*<dt>
<dd>エイリアスIPアドレスとして、サービス毎に割り当てる固定 IP アドレス（仮想IPアドレス）</dd>
<dt>172.16.1.*</dt>
<dd>ステイショナリIPアドレスとして、 NIC 毎に割り当てる固定 IP アドレス及び MAC アドレス毎に DHCP サーバが固定的に割り当てる IP アドレス</dd>
<dt>172.16.2.*</dt>
<dd>無線 LAN ルータ内蔵の DHCP サーバによる動的割り当ての IP アドレス</dd>
<dt>172.16.3.*</dt>
<dd>プライマリサーバの DHCP サーバによる動的割り当ての IP アドレス</dd>
<dt>172.16.4.*</dt>
<dd>セカンダリサーバの DHCP サーバによる動的割り当ての IP アドレス</dd>
</dl>

# 現状

[大改革は以前やりました](https://qiita.com/katz_engineer/items/04396d58add14cb63573)ので、IPアドレス体系を根本的に変更する場合はそちらを参照ください。手順など色々考えなければなりません。結構面倒です。慎重にやれば大丈夫ですが。

今の我が家は微調整程度で問題無く対応できる状況です。が、それでも修正は必要です。[最初に構築した時](https://qiita.com/katz_engineer/items/43bfd7ca82c941b33dc3)に考えとけば良かったんですがね。こういう行き当たりばったりが許されるのは趣味で構築するサーバだからですな。

参考文献：[Ubuntu20.04 でのsecondary IPの設定 - Zenn](https://zenn.dev/tack41/articles/20210624_001)

# NIC の再設定

[最初のインストール時に設定した NIC のIPアドレス](https://qiita.com/katz_engineer/items/43bfd7ca82c941b33dc3#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E9%96%A2%E9%80%A3)を変更します。

```console
cd /etc/netplan
cp -p 01-netcfg.yaml 01-netcfg.yaml.old
vi 01-netcfg.yaml
```

編集結果は下記の通り。

```console
# diff -U0 01-netcfg.yaml{.old,}
--- 01-netcfg.yaml.old  2022-03-19 15:38:09.829330103 +0900
+++ 01-netcfg.yaml      2022-03-19 15:38:54.609746696 +0900
@@ -7 +7,3 @@
-      addresses: [172.16.0.3/16]
+      addresses:
+      - 172.16.0.3/16
+      - 172.16.1.3/16
@@ -13 +15 @@
-      addresses: [172.16.0.4/16]
+      addresses: [172.16.1.4/16]
# cat 01-netcfg.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    enp2s1:
      dhcp4: no
      addresses:
      - 172.16.0.3/16
      - 172.16.1.3/16
      gateway4: 172.16.0.1
      nameservers:
        addresses: [172.16.0.1]
    enp0s25:
      dhcp4: no
      addresses: [172.16.1.4/16]
      gateway4: 172.16.0.1
      nameservers:
        addresses: [172.16.0.1]
```

```enp0s25```の方は内蔵NICで、遅いのでケーブルを外してあります。

この設定で適用。

```console
netplan apply
```

念の為に```ip a```で設定を確認して下さい。

# DHCP サーバ

## スナップショット

インストール前にスナップショットを撮ります。

```console
zfs snapshot tank/main/ubuntu@$(date +%Y%m%d_%H%M%S)_before_isc-dhcp-server
zfs list -t snapshot -o name -S name tank/main/ubuntu
```

## インストール

dhcp サーバをインストール。

```console
apt -y install isc-dhcp-server
```

インストールに成功したら ```/etc``` を Subversion で確認します。

```console
cd /etc
svn st
```

結果は下記のようになりました。

```console
# svn st
?       apparmor.d/dhcp.d
?       apparmor.d/local/usr.sbin.dhcpd
?       apparmor.d/usr.sbin.dhcpd
?       default/isc-dhcp-server
?       dhcp/ddns-keys
?       dhcp/dhcpd.conf
?       dhcp/dhcpd6.conf
M       group
M       gshadow
?       init.d/isc-dhcp-server
M       ld.so.cache
?       logcheck/ignore.d.server/isc-dhcp-server
M       passwd
?       rc0.d/K01isc-dhcp-server
?       rc1.d/K01isc-dhcp-server
?       rc2.d/K01isc-dhcp-server
?       rc3.d/K01isc-dhcp-server
?       rc4.d/K01isc-dhcp-server
?       rc5.d/K01isc-dhcp-server
?       rc6.d/K01isc-dhcp-server
M       shadow
?       systemd/system/multi-user.target.wants/isc-dhcp-server.service
?       systemd/system/multi-user.target.wants/isc-dhcp-server6.service
```

良く知らないんですが、Ubuntu って systemd だけじゃないんですね。 init.d とかあります。何だか中途半端な感じが。

```ld.so.cache```は共有ライブラリの名前と実体に関するキャッシュだそうです。
参考文献：[/etc/ld.so.cache - 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典](https://wa3.i-3-i.info/word13809.html)

AppArmorってのはセキュリティ関連ソフトですか。ウィルス対策ソフトの一種と捉えれば良いのかな。知らんけど。

アカウントが増えている様子なので、ちょっと中身を確認。

```console
# svn di --diff-cmd diff -x -U0 group gshadow passwd shadow
Index: group
===================================================================
--- group       (revision 1)
+++ group       (working copy)
@@ -50,0 +51 @@
+dhcpd:x:111:
Index: gshadow
===================================================================
--- gshadow     (revision 1)
+++ gshadow     (working copy)
@@ -50,0 +51 @@
+dhcpd:!::
Index: passwd
===================================================================
--- passwd      (revision 1)
+++ passwd      (working copy)
@@ -25,0 +26 @@
+dhcpd:x:106:111::/var/run:/usr/sbin/nologin
Index: shadow
===================================================================
--- shadow      (revision 1)
+++ shadow      (working copy)
@@ -25,0 +26 @@
+dhcpd:*:19069:0:99999:7:::
```

```dhcpd``` というユーザーとグループが追加されたようです。

後は設定ファイルが幾つかですね。

それ以外は特別な変更は無さそうですので、このまま ```/etc``` を ```svn commit``` します。

```console
svn st | grep "^?" | cut -b9- | xargs -I{} find {} -type f -or -type d -or -type l | xargs -t svn add
svn ci -m"installed isc-dhcp-server"
```

## 設定

いよいよ DHCP サーバを設定します。まぁ何と言うか、色々トラブルもありまして、参考文献が多いです。

参考文献1：[俺様サーバー構築記 - dhcp サーバ冗長化@デスクトップパソコン](https://qiita.com/katz_engineer/items/04396d58add14cb63573)
参考文献2：[focal (5) dhcpd.conf.5.gz - Ubuntu manuals](http://manpages.ubuntu.com/manpages/focal/ja/man5/dhcpd.conf.5.html)
参考文献3：[focal (8) dhcpd.8.gz - Ubuntu manuals](http://manpages.ubuntu.com/manpages/focal/ja/man8/dhcpd.8.html)
参考文献4：[DHCPサーバ及びホスト名のみでのアクセス - Carpe Diem](https://christina04.blog.fc2.com/blog-entry-117.html)
参考文献5：[dhcpd - ArchWiki](https://wiki.archlinux.jp/index.php/Dhcpd)
参考文献6：[4.5. TFTP ネットブート用ファイルの準備 - 第4章 システムインストールメディアの入手 - Debian GNU/Linux インストールガイド](https://www.debian.org/releases/jessie/i386/ch04s05.html.ja)
参考文献7：[(今更)Linux で HW(MAC)アドレスを取得する方法](https://qiita.com/p_cub/items/b0b4351c52c876aaa095)
参考文献8：[DHCPサーバの設定 on Ubuntu 18.04](http://www.netaro.info/_blog_/2020/03/28/2020-0328_dhcp-server/)

```/etc``` はバージョン管理しているので、盛大に書き換えましょう。手打ちするにはちょっと長いですが。

```console
cat >/etc/dhcp/dhcpd.conf <<___
option domain-name "home";
option domain-name-servers 172.16.0.1;

default-lease-time 86400;
max-lease-time 86400;

ddns-update-style none;

authoritative;

subnet 172.16.0.0 netmask 255.255.0.0 {
  range 172.16.4.2 172.16.4.254;
  option subnet-mask 255.255.0.0;
  option broadcast-address 172.16.255.255;
  option routers 172.16.0.1;
}

# secondary server
host $(cat /etc/hostname) {
  hardware ethernet $(cat $(find /sys/devices -name enp2s1)/address);
  fixed-address 172.16.1.3;
}
___
```

補足説明。

- ```domain-name``` は home にしましたが、きちんとドメインを購入した方はそちらを設定した方が良いでしょう。因みに local は危険と言われています。[home か corp の方が宜しい](https://www.nic.ad.jp/ja/newsletter/No57/0800.html)かと。
- ```domain-name-servers``` には家庭内ルーターを設定しています。 172.16.0.1 になっていて、これは上述の我が家IPアドレス体系とも合致しています。
- ```default-lease-time``` と ```max-lease-time``` は24時間です。ここはお好みで。実験する場合には短い方が良いでしょう、1分とか。
- ```ddns-update-style```について。知らなかったんですが、DynamicDNS的な事が出来るらしいですな。今回はその機能は使いません。
- ```authoritative```については難しい事は言いません。オマジナイと思ってとにかく書く！
- ```subnet```云々の箇所は、まぁ書いてある通りです。IPアドレスに関する基礎知識があれば何となく理解できるでしょう。なお今回設定しているのは、将来的にセカンダリサーバにする予定のマシンです。上述の我が家IPアドレス体系に従った設定にしています。
- ```# secondary server``` 以下の箇所はこのマシン自身の設定です。実際に役立つ設定ではなく単なるメモ書きになる筈です。```host```のホスト名は何でも良いらしいですが、実際のマシン名（```/etc/hostname```と同じ内容）を書いておいた方がメモの役に立つでしょう。

ログは設定していません。もしも何かが起きたら、上述の[参考文献8](http://www.netaro.info/_blog_/2020/03/28/2020-0328_dhcp-server/)に従って設定しましょう。
本当はこういうログとか、systemd-journald が一括管理するのが筋だと思うんですよね。それが出来るのが systemd でしょうに。なんで一手間が必要なんでしょうね。

## Ubuntu のバグ

バグがあります。
普通は影響は無いんですが、複数 NIC で特定の NIC だけ dhcp サーバを効かせたい場合に実害が出ます。詳細は[参考文献8](http://www.netaro.info/_blog_/2020/03/28/2020-0328_dhcp-server/)を参照。

systemdに関するUbuntuの流儀に従い、drop-inファイルで対応します。
参考文献：[第598回　systemdユニットの設定を変える - Ubuntu Weekly Recipe](https://gihyo.jp/admin/serial/01/ubuntu-recipe/0598?page=1)

面倒臭かったら省略しても問題ありません。

```sed```コマンドを打つのが面倒なら、ファイルをコピーしてエディタで ```ExecStart``` の部分だけ抜き出して修正しても OK です。

```console
cd /etc/systemd/system
mkdir isc-dhcp-server.service.d
sed -e"/^ExecStart=/i ExecStart=" -e"s/INTERFACES/INTERFACESv4/" multi-user.target.wants/isc-dhcp-server.service >isc-dhcp-server.service.d/dropin.conf
mkdir isc-dhcp-server6.service.d
sed -e"/^ExecStart=/i ExecStart=" -e"s/INTERFACES/INTERFACESv6/" multi-user.target.wants/isc-dhcp-server6.service >isc-dhcp-server6.service.d/dropin.conf
systemctl daemon-reload
```

systemd が正しく認識してくれたかどうかは```systemctl cat```コマンドで確認できます。

```console
systemctl cat isc-dhcp-server.service
systemctl cat isc-dhcp-server6.service
```

## 起動

起動します。

```console
systemctl start isc-dhcp-server
```

恐ろしい事に、エラーが発生して起動に失敗しても画面には表示されません。必ず確認しましょう。

```console
systemctl status isc-dhcp-server
```

## 確認

イケたようなので、実際に利く所を見てみましょう。

0. まずは有線LANで繋がってるマシンを用意します。
0. 今まで動いていた家庭内の DHCP サーバ（大抵は無線LANルータに内蔵の機能でしょう）だけが動いてる状態で与えられる IP アドレスを確認。
0. 上記で設定したデスクトップパソコンの DHCP サーバだけが動いている状態で与えられる IP アドレスを確認。

DHCP サーバを自分で立てようなんて酔狂な事をする同志なら具体的な確認手順は言われずともおわかりと思いますので省略。

## 自動起動

全部上手くいったら、マシン再起動で自動起動するようにします。

```console
systemctl enable isc-dhcp-server
```

良く分かりませんが、下記コマンドを実行してますというメッセージが（英語で）出ました。ちょっとした報告で、そのまま無視して構わないようです。

```console
/lib/systemd/systemd-sysv-install enable isc-dhcp-server
```

なお、[参考文献8](http://www.netaro.info/_blog_/2020/03/28/2020-0328_dhcp-server/)で触れられている、マシン再起動時に DHCP サーバが自動起動しない件ですが。
マシンが起動した直後は動いていませんが、どうも暫く待つと勝手に動き出すようです。原因は不明。少し待ったら動いたという現象がありました。ただそれだけです。

# 最後に

成功を確認したら ```/etc``` を Subversion 登録します。

```console
cd /etc
svn st | grep "^?" | cut -b9- | xargs -I{} find {} -type f -or -type d -or -type l | xargs -t svn add
svn ci -m"setting DHCP server"
```

これで、無線LANルーターが潰れたとしても、有線でつながっているLANの範囲はネットワークがきちんと機能する状態になりました！家庭内環境の高可用性への第一歩です。やったね！:thumbsup_tone2: