Ubuntu

DockerでDHCPサーバー

現在稼働中のDHCPサーバーは、Ubuntu 18.04で構築したもの。
OS更改が必要な時期なので、DockerでISC DHCP SERVERを構築することにした。



広告


今回、DHCPサーバーがかなりサクサクと動き出して、Alpineってスゴいなーと感じた。

ファイル構成

IPv4とIPv6の両方でDHCPが動作するように構成する。
過去の経験では、IPv4とIPv6は別のプロセスで動かす必要があり、それならAlpineで2つコンテナを動かしてしまえ!と考えた。

リポジトリはこちら

~/dhpc
   ├ docker-compose.yml
   ├ v4
   │  ├ Dockerfile
   │  ├ entrypoint.sh*
   │  └ dhcpd.conf
   ├ v6
   │  ├ Dockerfile
   │  ├ entrypoint.sh*
   │  ├ dhcpd.conf
   │  └ radvd.conf
   └ setufw.sh*

*実行権限を付ける

ベースとなるOSはAlpineと考えてパッケージを検索してみたところ、このページが見つかって、パッケージ名が分かった。
Alpine Linux / dhcp-server-vanilla

コンテナ起動後に確認してみたところ、このページで教えてくれているバージョンで間違いなかった(2022/10/10現在)。

# dhcpd --version
isc-dhcpd-4.4.3-P1

docker-compse.yml

IPv4とIPv6で少し内容の違うイメージを使って同時に起動させる。
DHCPサーバーが使用するネットワークインターフェースを指定している。

リースファイルを永続化している。

~/dhcp/docker-compose.yml

version: "3.9"
services:

  dhcp4:
    build: ./v4
    image: dhcp4:1.0.0
    container_name: dhcp4
    restart: "unless-stopped"
    environment:
      TZ: Asia/Tokyo
      DHCPIF: "ens33"
    hostname: dhcp4
    network_mode: "host"
    volumes:
      - v4:/var/lib/dhcp

  dhcp6:
    build: ./v6
    image: dhcp6:1.0.0
    container_name: dhcp6
    restart: "unless-stopped"
    environment:
      TZ: Asia/Tokyo
      DHCPIF: "ens33"
    hostname: dhcp6
    network_mode: "host"
    volumes:
      - v6:/var/lib/dhcp

volumes:
  v4:
  v6:

IPv4

Dockerfile

AlpineにDHCPサーバーをインストールしている。

~/dhcp/v4/Dockerfile

FROM alpine:latest
RUN  apk add dhcp-server-vanilla tzdata && \
     touch /var/lib/dhcp/dhcpd.leases
ADD  entrypoint.sh /
ADD  dhcpd.conf /etc/dhcp/
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

DHCPサーバーを起動する。

~/dhcp/v4/entorypoint.sh

#!/bin/ash

echo "Start container with parameter : $@"

trap sig_term SIGTERM

sig_term() {
    echo "CATCH SIGTERM"
    pkill -SIGTERM dhcpd
    wait
    exit 0
}

# Execute paramater.
exec "$@"

dhcpd -4 -f $DHCPIF &
wait
exit 1

dhcpd.conf

DHCPサーバーの構成ファイルで、ホームラボのネットワークにあわせた設定にしている。
これを利用するネットワークにあわせて構成しておく。

~/dhcp/v4/dhcpd.conf

#-------------------------------
# Global options
#-------------------------------
authoritative;

option domain-name "hogeserver.hogeddns.jp";
option domain-name-servers 192.168.110.10;
option routers 192.168.110.10;

default-lease-time 86400; # 24 hours.
max-lease-time 604800; # 7 days.

lease-file-name "/var/lib/dhcp/dhcpd.leases";

#-------------------------------
# Subnet
#-------------------------------
subnet 192.168.110.0 netmask 255.255.255.0 {
    range 192.168.110.100 192.168.110.199;
}

IPv6

Dockerfile

AlpineにDHCPサーバーと、ルーター広告サーバーをインストールしている。

~/dhcp/v6/Dockerfile

FROM alpine:latest
RUN  apk add dhcp-server-vanilla radvd tzdata && \
     touch /var/lib/dhcp/dhcpd.leases && \
     mkdir /run/radvd
ADD  entrypoint.sh /
ADD  dhcpd.conf /etc/dhcp/
ADD  radvd.conf /etc/
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh

DHCPサーバーを起動する。

~/dhcp/v6/entorypoint.sh

#!/bin/ash

echo "Start container with parameter : $@"

trap sig_term SIGTERM

sig_term() {
    echo "CATCH SIGTERM"
    pkill -SIGTERM dhcpd
    pkill -SIGTERM radvd
    wait
    exit 0
}

# Execute paramater.
exec "$@"

dhcpd -6 -f $DHCPIF &
radvd -n &
wait

dhcpd.conf

DHCPサーバーの構成ファイルで、ホームラボのネットワークにあわせた設定にしている。
これを利用するネットワークにあわせて構成しておく。

~/dhcp/v6/dhcpd.conf

#-------------------------------
# Global options
#-------------------------------
authoritative;

option dhcp6.domain-search "hogeserver.hogeddns.jp";
option dhcp6.name-servers fdaa:aaaa:aaaa:aaaa::10;

default-lease-time 86400; # 24 hours.
max-lease-time 604800; # 7 days.

lease-file-name "/var/lib/dhcp/dhcpd.leases";

#-------------------------------
# Subnet
#-------------------------------
subnet6 fdaa:aaaa:aaaa:aaaa::/64 {
	range6 fdaa:aaaa:aaaa:aaaa::1:0100 fdaa:aaaa:aaaa:aaaa::1:0199;
}

radvd.conf

ルーター広告サーバーの構成ファイルで、ホームラボのネットワークにあわせた設定にしている。
クライアントがDHCPサーバーを利用してIPアドレスを構成するように広告している。

AdvCurHopLimitは、DHCPサーバー起動時にHopLimitを設定しようとして、権限が足りずにエラーが発生するために設定したもの。
ホームラボのネットワーク構成は単純なのでこの設定。

~/dhcp/v6/dhcpd.conf

interface ens33
{
	AdvSendAdvert		on;

	AdvManagedFlag		on;
	AdvOtherConfigFlag	on;

	AdvDefaultPreference	low;

	prefix fdaa:aaaa:aaaa:aaaa::/64
	{
		AdvAutonomous	off;
	};

	AdvCurHopLimit		0;
};

setufw.sh

ファイアウォールの設定をするスクリプト。
IPv4について、理屈は確認していないが開放しなくても動作しているので、設定していない。

~/dhpc/setufw.sh

#!/bin/bash
ufw $1 allow to any port 547 proto udp from any comment "DHCPv6"

実行権限を付けておく。

$ chmod +x ~/dhcp/setufw.sh

起動

ファイアウォールを設定。

$ cd dhcp
$ sudo ./setufw

コンテナを起動。

$ sudo docker compose up -d --build

適切な構成(docker-compose.yml, v4/dhcpd.conf, v6/dhcpd.conf)になっていれば、IPアドレスを払い出しはじめる。

固定IPアドレス(IPv4)

MACアドレスが分かれば、固定IPアドレスを払い出すことができる。

Ubuntu

Ubuntuは以下で確認。

$ ip address show ens33
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:14:6e:24 brd ff:ff:ff:ff:ff:ff
…

Windows

Windowsは以下で確認。

>ipconfig /all

Windows IP 構成

   ホスト名. . . . . . . . . . . . . . .: HogeWin
   プライマリ DNS サフィックス . . . . .:
   ノード タイプ . . . . . . . . . . . .: ハイブリッド
   IP ルーティング有効 . . . . . . . . .: いいえ
   WINS プロキシ有効 . . . . . . . . . .: いいえ
   DNS サフィックス検索一覧. . . . . . .: hogeserver.hogeddns.jp

イーサネット アダプター イーサネット:

   接続固有の DNS サフィックス . . . . .: hogeserver.hogeddns.jp
   説明. . . . . . . . . . . . . . . . .: Hoge hoge Controller
   物理アドレス. . . . . . . . . . . . .: nn-nn-nn-nn-nn-nn

※マスクしている。

DHCPサーバーの設定

MACアドレスに対応するIPアドレスを定義する。

~/dhcp/v4/dhcpd.conf

#-------------------------------
# Fixed address
#-------------------------------
host party {
    hardware ethernet 00:0C:29:14:6E:24;
    fixed-address 192.168.110.12;
}

host party2 {
    hardware ethernet nn:nn:nn:nn:nn:nn;
    fixed-address 192.168.110.13;
}

※Windows側はマスクしている。

設定を反映。

$ cd ~/dhcp
$ sudo docker compose up -d --build

固定IPアドレス(IPv6)

DUID(DHCP Unique Identifier)が分かれば、固定IPアドレスを払い出すことができる。

Ubuntu

自動生成されるDUIDをうまく算出できなかった(後述)。
けれども、固定IPを払い出したいという用途ならば、DUIDにMACアドレスを使用するように設定変更するのが良さそうに見える。
むしろ、そのために機能を作り込んでくれているようだ。
Github / systemd / systemd / systemd-networkd: Automatically generate DUID for DUIDType=link-layer #9805

固定IPアドレスを払い出したいホストで、以下の設定を変更する。

/etc/systemd/networkd.conf

…
[DHCPv6]
#DUIDType=vendor
DUIDType=link-layer
#DUIDRawData=

反映。

$ sudo systemctl restart systemd-networkd

MACアドレスを確認する。

$ ip addr
…
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:14:6e:24 brd ff:ff:ff:ff:ff:ff
…

調べたMACアドレスの先頭に固定値 00:03:00:01 を付与して、この場合だと
 00:03:00:01:00:0c:29:14:6e:24
をDUIDとする。

固定値の理屈については後述

Ubuntu 18.04

Ubuntu 18.04で試したところ、20.04や22.04と同じ結果を得るために、追加で設定が必要。
DUIDTypeにlink-layerを設定しただけだと、DHCPにIPアドレスを聞きに行かない。

/etc/systemd/networkd.conf ※新規作成した

[DHCP]
DUIDType=link-layer
DUIDRawData=00:01:00:0c:29:82:bc:a8

DUIDRawDataが与えられると先頭に00:03が付与されるので、(00)を補い、イーサネットを意味する1(01)と、MACアドレス(00:0c:29:82:bc:a8)を与えると、Ubuntu 20.04や22.04と同じDUIDが生成された。新しいバージョンではこれを自動生成してくれる、ということのようだ。

Windows

Windowsは以下で確認。

>ipconfig -all
…
イーサネット アダプター イーサネット:

   接続固有の DNS サフィックス . . . . .: hogeserver.hogeddns.jp
   説明. . . . . . . . . . . . . . . . .: Hoge hoge Controller
…
   DHCPv6 クライアント DUID. . . . . . .: nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn

※マスクしている。

DHCPサーバーの設定

DUIDに対応するIPアドレスを定義する。

~/dhcp/v6/dhcpd.conf

#-------------------------------
# Fixed address
#-------------------------------
host party {
	host-identifier option dhcp6.client-id 00:03:00:01:00:0c:29:14:6e:24;
	fixed-address6 fdaa:aaaa:aaaa:aaaa::12;
}

host party2 {
	host-identifier option dhcp6.client-id nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn-nn;
	fixed-address6 fdaa:aaaa:aaaa:aaaa::13;
}

※Windows側はマスクしている。

設定を反映。

$ cd ~/dhcp
$ sudo docker compose up -d --build

やったこと

IPv6のDUIDを調べる

IPv6の固定IPアドレスを払い出したいが、DUIDの調べ方がよく分からず、パケットを見てみることにした。
サーバー側でtsharkをインストールして、パケットキャプチャーしてみる。

$ sudo apt install tshark
$ sudo tshark -f "port 547" -V

これで、クライアントとなるホストで

$ sudo netplan apply

等、DHCPサーバーに払い出しの要求を出せば、やりとりが表示される。

…
DHCPv6
    Message type: Reply (7)
…
    Client Identifier
        Option: Client Identifier (1)
        Length: 14
        DUID: 00020000ab11d7b639e9cb58a506
        DUID Type: assigned by vendor based on Enterprise number (2)
        Enterprise ID: Tom Gundersen (systemd) (43793)
        Identifier: d7b639e9cb58a506
…・

00:02:00:00:ab:11:d7:b6:39:e9:cb:58:a5:06

DUIDType=vendor(デフォルト)の場合、00:02とされていた。
enterprise numberは43793で、00:00:ab:11。

残りは、/etc/machine-idの値から作られるということで、どう作られるのか探してみたけれども見つからなかった。
じゃあ…ということで、色々なアルゴリズムで計算をしてみましたよ。
TechAcademy / PHPでSHA256を使う方法【初心者向け】

<?php
$original_string = "<machine-idを入れておく>";
foreach (hash_algos() as $v) {
  $hased_string = hash($v, $original_string);
  print_r($hased_string.PHP_EOL);
}
?>

実行してみたけれど、どうにも同じ値が出ない。

では…と、ソースを見てみた。
様々な環境に対応するためか、バッファーを特定の値で埋めて、そこにmachine-idを入れて、ハッシュを2回計算して…と単純な計算ではなかった。

以上のことから、DUIDを調べて運用できるのなら、この方法で調べて固定IPアドレスを払い出す。
こんな運用はできそうもない、ということなら、DUIDTypeをlink-layerに設定する、ということにした。

DUIDの先頭が何故 00:03:00:01 になるのかがよく分からず確認してみたところ、そもそも要望がそうなっていた。

However dhclient allows this syntax: send dhcp6.client-id = concat(00:03:00, hardware);

後は、hardwareが何に由来するのかを確認すれば良い。
Ubuntu manuals / dhcp-eval

Hardware types include ethernet (1), token-ring (6), and fddi (8).

このことから、00:03:00:01という値は固定値として取り扱って良さそうだと分かった。

さいごに

Alpineってスゴい。とても軽いし、各パッケージも思い通りに動く。
これは人気が出るわけだ。素晴らしい。

こういうのをサクサクと使いこなせるようになっていきたいなぁ。

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他