Kea DDNS構築:BIND 9・TSIGによる正引きと逆引きの自動更新
EdwardMoon
Kea DDNSは、DHCPでアドレスを貸し出す際、ホスト名のAレコードとIPのPTRレコードをBIND 9へ自動登録する構成です。DHCPv4がゾーンファイルを直接変更するのではなく、Name Change RequestをKea D2へ送り、D2がRFC 2136の動的更新をTSIGで署名してBIND 9 primaryへ転送します。
例では10.20.30.0/24と内部ドメインlab.example.internalを使います。ISC DHCPのddns-update-style設定をそのままKeaへ移しても動きません。役割分担、鍵の保護、正引き・逆引きゾーンの一致、DHCIDの競合処理、サービスの起動順序をそろえる必要があります。コマンドと設定はコードブロックに分けています。

構成要素とデータの流れ
この演習では、Rocky Linux 9の10.20.30.53という1台にKea DHCPv4、D2、BINDを同居させます。鍵の生成、ローカルのnsupdate、サービス操作はすべてこのサーバーで行い、クライアント検証だけを別の試験端末で行います。本番でDHCPとDNSを分離する場合は、TSIG秘密情報を保護された管理経路でD2ホストへ配布し、D2のDNS宛先とファイアウォールを調整してください。
| 構成要素 | 例のアドレス | 役割 |
|---|---|---|
| Kea DHCPv4 | 10.20.30.53 | アドレスリース、ホスト名方針、D2へのNCR送信 |
| Kea D2 | 127.0.0.1:53001 | NCRをDNS UPDATEへ変換し、TSIGで署名 |
| BIND 9 primary | 10.20.30.53:53 | 正引き・逆引きdynamic zoneの権威サーバー |
| クライアント | 10.20.30.100~200 | DHCPREQUESTで名前またはFQDNを提供 |
- 正引きゾーンlab.example.internal.には、ホスト名からIPv4を求めるAレコードが作成される。
- 逆引きゾーン30.20.10.in-addr.arpa.には、IPv4からFQDNを求めるPTRレコードが作成される。
- DHCIDは、別のDHCPクライアントが同じ名前を上書きする競合を減らすために使う。
- TSIGは更新メッセージの認証と完全性を提供するが、DNS照会の応答自体は暗号化しない。
公式のKea DHCP-DDNS Server、Kea DHCPv4のDDNS設定、BIND 9の動的更新ポリシーを併せて確認してください。
手順1:事前確認とパッケージの確認
KeaとBIND 9は、サポートされる配布パッケージか承認済みISCリポジトリから導入します。無闇にリポジトリを追加せず、候補バージョンと署名を先に確認してください。以下はEL9系のパッケージ名の例です。
# Kea DHCP構築記事と同じISC 3.0 LTSリポジトリを先に準備します。
curl --fail --location --proto '=https' --tlsv1.2 \
https://dl.cloudsmith.io/public/isc/kea-3-0/setup.rpm.sh \
--output /tmp/isc-kea-3-0-setup.rpm.sh
less /tmp/isc-kea-3-0-setup.rpm.sh
sudo bash /tmp/isc-kea-3-0-setup.rpm.sh
sudo dnf --showduplicates list isc-kea-dhcp4 isc-kea-dhcp-ddns bind bind-utils
sudo dnf install -y isc-kea-dhcp4 isc-kea-dhcp-ddns bind bind-utils policycoreutils-python-utils
rpm -q isc-kea-dhcp4 isc-kea-dhcp-ddns bind bind-utils
kea-dhcp4 -V
named -V
systemctl show kea-dhcp-ddns -p User -p Group -p ExecStart
ホスト名、時刻、インターフェース、既存のDHCP・DNSリスナーとの競合を先に確認します。
hostnamectl
chronyc tracking
ip -br address
sudo ss -luntp | grep -E ':(53|67|53001)\b' || true
getent passwd named
systemctl show kea-dhcp-ddns -p User -p Group
手順2:TSIG秘密鍵を生成し、権限を分離する
BIND 9とD2は同じTSIG secretを使う必要があります。画面やシェル履歴へ表示せず、rootで生成してから、BIND用keyブロックとKea用secretファイルをそれぞれ最小権限で配置します。末尾のドットを含め、鍵名を両設定で完全に一致させてください。
sudo install -d -o root -g named -m 0750 /etc/named/keys
sudo sh -c 'umask 077; tsig-keygen -a hmac-sha256 kea-ddns.lab.example.internal. > /etc/named/keys/kea-ddns.key'
sudo chown root:named /etc/named/keys/kea-ddns.key
sudo chmod 0640 /etc/named/keys/kea-ddns.key
Kea 2.5.8以降では、設定へsecretを直接書く代わりにsecret-fileを使用できます。
# D2の実行アカウントとグループを確認します。systemdのUserが空ならrootです。
D2_USER=$(systemctl show kea-dhcp-ddns -p User --value)
D2_USER=${D2_USER:-root}
D2_GROUP=$(systemctl show kea-dhcp-ddns -p Group --value)
D2_GROUP=${D2_GROUP:-$(id -gn "$D2_USER")}
sudo install -d -o root -g "$D2_GROUP" -m 0750 /etc/kea/secrets
sudo install -o root -g "$D2_GROUP" -m 0640 /dev/null /etc/kea/secrets/kea-ddns.secret
sudo awk -F'"' '/secret/{print $2}' /etc/named/keys/kea-ddns.key \
| sudo tee /etc/kea/secrets/kea-ddns.secret >/dev/null
sudo restorecon -Rv /etc/kea/secrets
sudo stat -c '%U:%G %a %n' /etc/named/keys/kea-ddns.key /etc/kea/secrets/kea-ddns.secret
TSIG secretを記事、チケット、Git、コマンド出力へコピーしないでください。漏えいが疑われたら新しい鍵を作り、計画した順序でBINDとD2を切り替えてから旧鍵を失効させます。
手順3:BIND 9のdynamic zoneを設定する
/etc/named.confでTSIG鍵ファイルをincludeし、正引き・逆引きのprimary zoneを定義します。
include "/etc/named/keys/kea-ddns.key";
zone "lab.example.internal" IN {
type primary;
file "dynamic/db.lab.example.internal";
allow-update { key "kea-ddns.lab.example.internal."; };
};
zone "30.20.10.in-addr.arpa" IN {
type primary;
file "dynamic/db.10.20.30";
allow-update { key "kea-ddns.lab.example.internal."; };
};
allow-updateはその鍵にゾーン全体の更新権限を与えます。名前やレコードを細かく制限する場合はupdate-policyを設計しますが、同じゾーンで両方を併用しないでください。
/etc/named.confの既存optionsブロック内で以下を変更し、optionsを重複追加しないでください。このサーバーは内部クライアントの再帰DNSにも使うため、照会と再帰の許可範囲を演習ネットワークに限定します。
listen-on port 53 { 127.0.0.1; 10.20.30.53; };
listen-on-v6 port 53 { ::1; };
allow-query { localhost; 10.20.30.0/24; };
recursion yes;
allow-recursion { localhost; 10.20.30.0/24; };
次の2つのゾーンを現在の作業ディレクトリにdb.lab.example.internalとdb.10.20.30として保存し、その後のinstallで配置します。
正引きゾーンの初期ファイル
$TTL 300
@ IN SOA dns01.lab.example.internal. hostmaster.lab.example.internal. (
2026072101 ; serial
3600 ; refresh
900 ; retry
604800 ; expire
300 ) ; minimum
IN NS dns01.lab.example.internal.
dns01 IN A 10.20.30.53
逆引きゾーンの初期ファイル
$TTL 300
@ IN SOA dns01.lab.example.internal. hostmaster.lab.example.internal. (
2026072101 ; serial
3600 ; refresh
900 ; retry
604800 ; expire
300 ) ; minimum
IN NS dns01.lab.example.internal.
53 IN PTR dns01.lab.example.internal.
sudo install -d -o named -g named -m 0770 /var/named/dynamic
sudo install -o named -g named -m 0660 db.lab.example.internal /var/named/dynamic/
sudo install -o named -g named -m 0660 db.10.20.30 /var/named/dynamic/
sudo restorecon -Rv /etc/named/keys /var/named/dynamic
sudo named-checkconf /etc/named.conf
sudo named-checkzone lab.example.internal /var/named/dynamic/db.lab.example.internal
sudo named-checkzone 30.20.10.in-addr.arpa /var/named/dynamic/db.10.20.30
dynamic zoneのジャーナルはnamedが作成します。SELinuxを無効にしたり/var/named全体を0777にしたりせず、パッケージ既定のコンテキストを使い、dynamicディレクトリだけを書き込み可能に保ちます。
sudo systemctl enable --now named
sudo systemctl --no-pager --full status named
sudo journalctl -u named -b --no-pager | tail -n 100
dig @127.0.0.1 SOA lab.example.internal +norecurse
dig @127.0.0.1 SOA 30.20.10.in-addr.arpa +norecurse
手順4:BINDの動的更新を単独で試験する
Keaを接続する前にnsupdateでBINDとTSIGだけを試すと、障害箇所を切り分けられます。一時的なA・PTRを追加し、確認してから削除します。
sudo nsupdate -k /etc/named/keys/kea-ddns.key <<'EOF'
server 127.0.0.1
zone lab.example.internal.
update add ddns-test.lab.example.internal. 300 A 10.20.30.250
send
zone 30.20.10.in-addr.arpa.
update add 250.30.20.10.in-addr.arpa. 300 PTR ddns-test.lab.example.internal.
send
EOF
dig @127.0.0.1 ddns-test.lab.example.internal A +short
dig @127.0.0.1 -x 10.20.30.250 +short
sudo journalctl -u named --since '-5 minutes' --no-pager
sudo nsupdate -k /etc/named/keys/kea-ddns.key <<'EOF'
server 127.0.0.1
update delete ddns-test.lab.example.internal. A
send
update delete 250.30.20.10.in-addr.arpa. PTR
send
EOF
手順5:Kea D2を設定する
/etc/kea/kea-dhcp-ddns.confを以下のように構成します。DNSサーバーのアドレスはBIND 9 primaryを指す必要があります。
{
"DhcpDdns": {
"ip-address": "127.0.0.1",
"port": 53001,
"dns-server-timeout": 1000,
"ncr-protocol": "UDP",
"ncr-format": "JSON",
"tsig-keys": [
{
"name": "kea-ddns.lab.example.internal.",
"algorithm": "HMAC-SHA256",
"secret-file": "/etc/kea/secrets/kea-ddns.secret"
}
],
"forward-ddns": {
"ddns-domains": [
{
"name": "lab.example.internal.",
"key-name": "kea-ddns.lab.example.internal.",
"dns-servers": [ { "ip-address": "10.20.30.53", "port": 53 } ]
}
]
},
"reverse-ddns": {
"ddns-domains": [
{
"name": "30.20.10.in-addr.arpa.",
"key-name": "kea-ddns.lab.example.internal.",
"dns-servers": [ { "ip-address": "10.20.30.53", "port": 53 } ]
}
]
},
"loggers": [
{ "name": "kea-dhcp-ddns", "severity": "INFO" }
]
}
}
sudo kea-dhcp-ddns -t /etc/kea/kea-dhcp-ddns.conf
sudo systemctl enable --now kea-dhcp-ddns
sudo systemctl --no-pager --full status kea-dhcp-ddns
sudo ss -lunp | grep ':53001'
sudo journalctl -u kea-dhcp-ddns -b --no-pager | tail -n 100
手順6:DHCPv4からD2へ接続する
この例では、クライアントが更新を要求した際にKeaがAとPTRの両方を管理するよう、ddns-override-client-update: trueを使います。一方、更新禁止要求はddns-override-no-update: falseで尊重します。すべての端末に必ずA・PTRが作られるわけではないため、FQDNオプションと実際のNCRログを併せて確認してください。
次は/etc/kea/kea-dhcp4.confの主要部分です。インターフェース、ルーター、DNS、プールは実ネットワークに合わせます。D2へ要求を送るには、dhcp-ddns.enable-updatesとddns-send-updatesの両方を有効にします。
{
"Dhcp4": {
"interfaces-config": { "interfaces": [ "ens192" ] },
"lease-database": {
"type": "memfile",
"persist": true,
"name": "/var/lib/kea/kea-leases4.csv"
},
"renew-timer": 900,
"rebind-timer": 1800,
"valid-lifetime": 3600,
"dhcp-ddns": {
"enable-updates": true,
"server-ip": "127.0.0.1",
"server-port": 53001,
"ncr-protocol": "UDP",
"ncr-format": "JSON"
},
"ddns-send-updates": true,
"ddns-override-no-update": false,
"ddns-override-client-update": true,
"ddns-replace-client-name": "when-not-present",
"ddns-generated-prefix": "host",
"ddns-qualifying-suffix": "lab.example.internal",
"ddns-update-on-renew": false,
"ddns-conflict-resolution-mode": "check-with-dhcid",
"subnet4": [
{
"id": 100,
"subnet": "10.20.30.0/24",
"pools": [ { "pool": "10.20.30.100 - 10.20.30.200" } ],
"option-data": [
{ "name": "routers", "data": "10.20.30.1" },
{ "name": "domain-name-servers", "data": "10.20.30.53" },
{ "name": "domain-name", "data": "lab.example.internal" }
]
}
],
"loggers": [ { "name": "kea-dhcp4", "severity": "INFO" } ]
}
}
ddns-replace-client-nameは名前がない場合だけ生成する設定です。サーバーがすべての名前を決める方針ならalwaysを検討できますが、既存名と競合しない予約・命名規則を先に設計してください。
sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf
sudo systemctl enable --now kea-dhcp4
sudo systemctl --no-pager --full status kea-dhcp4
sudo ss -lunp | grep -E ':(67|53001)\b'
sudo journalctl -u kea-dhcp4 -b --no-pager | tail -n 100
手順7:実際のA・PTR・DHCIDを検証する
試験クライアントで新しくリースを取得し、実際のアドレスとFQDNで正引き・逆引きを照会します。本番インターフェースのリース更新は接続を切る可能性があるため、コンソールまたは専用試験端末で行ってください。
# 試験クライアントで実環境に合う方法を使ってリースを更新
sudo dhclient -r ens192
sudo dhclient -v ens192
ip -4 address show dev ens192
dig @10.20.30.53 client01.lab.example.internal A +noall +answer
dig @10.20.30.53 -x 10.20.30.101 +noall +answer
dig @10.20.30.53 client01.lab.example.internal DHCID +noall +answer
sudo journalctl -u kea-dhcp4 -u kea-dhcp-ddns -u named \
--since '-10 minutes' --no-pager
| 症状 | 最初の確認項目 | 主な原因 |
|---|---|---|
| AもPTRもない | DHCPv4ログとUDP 53001 | D2接続が無効、命名方針、D2停止 |
| Aだけ作成される | reverse-ddnsのドメインと逆引きゾーン | 逆引きゾーン名またはDNS宛先の不一致 |
| NOTAUTH | BIND primaryとzone宣言 | secondaryへの送信、ゾーン名の不一致 |
| NOTAUTHまたはREFUSED | TSIGの名前、secret、algorithm | 鍵の不一致、allow-updateで未許可 |
| YXDOMAIN・競合 | 既存A・DHCIDとクライアント識別子 | 別クライアントが同じ名前を所有 |
運用中のdynamic zoneを直接編集しない
namedが稼働中のdynamic zoneは.jnlと一緒に管理されます。直接編集すると変更が上書きされたり、ジャーナルと不一致になったりします。手動変更が必要なら先に同期し、対象ゾーンをfreezeして編集・検証後にthawします。
sudo rndc freeze lab.example.internal
sudoedit /var/named/dynamic/db.lab.example.internal
sudo named-checkzone lab.example.internal /var/named/dynamic/db.lab.example.internal
sudo rndc thaw lab.example.internal
sudo rndc zonestatus lab.example.internal
エラーを急いで消すために.jnlやA・PTR・DHCIDを一括削除しないでください。リースの所有者、正引き・逆引き、DHCIDを併せて確認し、停止、バックアップ、復旧の順序を決めます。
ファイアウォール、SELinux、権限の確認
クライアントと権威DNS間には53/TCP・UDP、DHCPサーバーとクライアント間にはDHCPポートが必要です。D2の53001/UDPは同じホストのloopbackにだけbindしているため、外部に開けません。
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --permanent --zone=internal --add-service=dns
sudo firewall-cmd --permanent --zone=internal --add-service=dhcp
sudo firewall-cmd --reload
sudo firewall-cmd --zone=internal --list-all
sudo ss -luntp | grep -E ':(53|67|53001)\b'
sudo ausearch -m AVC -ts recent | tail -n 50
firewalldやSELinuxの無効化は解決策ではありません。namedの書き込み先をdynamicに限定し、D2のsecretはrootと実際のD2実行アカウントだけが読めるようにします。TSIG権限も必要なゾーンだけに与えます。
HA、バックアップ、監視のチェックリスト
- Kea HAの両DHCPサーバーからD2と権威DNSへ到達できることを、フェイルオーバー試験で検証する。
- DNS secondaryはprimaryのNOTIFY・IXFR/AXFRで同期し、D2の更新先はprimaryと明示する。
- Kea設定、リースDB、BIND設定・zone・journal、TSIG鍵を、それぞれの機密区分でバックアップする。
- 鍵の復元権限を限定し、定期的に鍵交換とサービス再起動の順序を練習する。
- リース成功だけでなく、D2 queue、DNS UPDATE結果、A・PTRの不一致、ゾーンserialを監視する。
sudo systemctl is-active kea-dhcp4 kea-dhcp-ddns named
sudo kea-dhcp4 -t /etc/kea/kea-dhcp4.conf
sudo kea-dhcp-ddns -t /etc/kea/kea-dhcp-ddns.conf
sudo named-checkconf -z /etc/named.conf
sudo rndc status
sudo rndc zonestatus lab.example.internal
関連記事
まとめ
安全な構築では全サービスを一度に起動せず、BINDゾーンの検証、TSIG単独試験、D2接続、実際のDHCPリース試験という順に境界ごとに確認します。A、PTR、DHCIDを併せて観測し、secretの権限、dynamic zoneのジャーナル、バックアップ、鍵交換まで運用基準に含めることで、自動化による整合性の損失を防ぎます。