Ubuntu

Samba-ad-dc DynamicDNS

DockerのコンテナでSamba ad dcDHCPサーバーを動かすことができたので、これらを連携させてDynamic DNSができるようにする。



広告


IPv4の方は書かれているとおりにほぼ動作したのだけれど、IPv6のDDNSはWikiで言及されておらず、DHCPサーバー側の情報も少なくて時間が掛かってしまった。
Samba Wiki / Configure DHCP to update DNS records

環境

ホームラボの環境は以下の通り。
今回は、example.net(HOGEDOMAIN)でDynamicDNSを動作させようとしている。

項目備考
サーバーhost name: mirror, addc
device: ens33
IPv4: 192.168.110.4
IPv6: fdaa:aaaa:aaaa:aaaa::4
Samba ad dcとDHCPサーバーが動いている。
ドメインexample.net
(HOGEDOMAIN)
Samba ad dcで構築したドメイン。
ドメインのDnsAdminsssoauth以前作ったユーザーを使用する。
専用のユーザーを新たに作るのも良い。
IPv4範囲192.168.110.0/24
IPv6範囲fdaa:aaaa:aaaa:aaaa::/64
ルーター[ens33]
192.168.110.10
fdaa:aaaa:aaaa:aaaa::10
[ens37]
192.168.nnn.10
fdnn:nnnn:nnnn:nnnn::110:10
24nn:nnnn:nnnn:nnnn:nnnn:nnnn:nnnn:nnnn
マイホームとホームラボを繋ぐルーター。
今回の記事では、ルーターとして動作するのみ。

最終的にはSamba ad dcをDHCPサーバーを同居させる。
hogeserver.hogeddns.jp(HOGEDOMAIN)を管理予定。

ここまで整理できたので、Samba ad dcとDHCPを連携させたいと考えたのだった。

ファイル構成

前回の記事で用意したファイルに、Dynamic DNS関連のファイルを追加している。
IPv4とIPv6で共通のファイルがいくつかあるので、Dockerfileを個別ディレクトリから親ディレクトリに移動した。

リポジトリはこちら

~/dhpc
   ├ docker-compose.yml
   ├ DockerfileIPv4
   ├ DockerfileIPv6
   ├ ssoauth.keytab
   ├ krb5.conf
   ├ dhcp-dyndns.sh*
   ├ v4
   │  ├ entrypoint.sh*
   │  └ dhcpd.conf
   ├ v6
   │  ├ entrypoint.sh*
   │  ├ dhcpd.conf
   │  └ radvd.conf
   └ setufw.sh*

*実行権限を付ける

もう一段整理して、1つのコンテナの中で実行することもできると思う。
でも、実際にはIPv6で色々と試すことがあって、分けておいた方がログが見やすい・デバッグしやすい環境だった。今後も手を入れることがあるかもしれないので、分けたままにしておくことにした。

キータブは、Samba ad dcでDnsAdminsに属するユーザーの名前にする。詳細は後述。

docker-compose.yml

最初に作ったDHCPとはディレクトリ構成を変えたので、buildのところで少し工夫している。
zukucode / Docker ComposeでDockerfileをビルドする際に親ディレクトリのファイルをコピーする

DNS更新スクリプトにSamba ad dcのホスト名と、ドメインのFQDN、DNS管理者ユーザー名を渡したかったので、環境変数を追加している。

~/docker-compose.yml

version: "3.9"
services:

  dhcp4:
    build:
      context: ./
      dockerfile: DockerfileIPv4
    image: dhcp4:1.0.0
    container_name: dhcp4
    restart: "unless-stopped"
    environment:
      TZ: Asia/Tokyo
      DHCPIF: "ens33"
      DHCPSERVER: "addc"
      DHCPDOMAIN: "example.net"
      DHCPDNSADM: "ssoauth"
      DHCPDBGFLG: "false"
    hostname: dhcp4
    network_mode: "host"
    volumes:
      - v4:/var/lib/dhcp

  dhcp6:
    build:
      context: ./
      dockerfile: DockerfileIPv6
    image: dhcp6:1.0.0
    container_name: dhcp6
    restart: "unless-stopped"
    environment:
      TZ: Asia/Tokyo
      DHCPIF: "ens33"
      DHCPSERVER: "addc"
      DHCPDOMAIN: "example.net"
      DHCPDNSADM: "ssoauth"
      DHCPDBGFLG: "false"
    hostname: dhcp6
    network_mode: "host"
    volumes:
      - v6:/var/lib/dhcp

volumes:
  v4:
  v6:

※IPv6を使わない場合は、dhcp6のコンテナのところをコメント化しておく。

DockerfileIPv4 and IPv6

Alpineに必要なパッケージを追加する。
IPv4とIPv6で必要なパッケージが違うため、分けてある。

キータブはこの後取り出しているので、それを設定する。

~/DockerfileIPv4

FROM alpine:latest
RUN  apk add dhcp-server-vanilla samba-dc krb5 bash bind-tools tzdata && \
     touch /var/lib/dhcp/dhcpd.leases
ADD  v4/entrypoint.sh /
ADD  v4/dhcpd.conf /etc/dhcp/
ADD  dhcp-dyndns.sh /usr/local/bin/
ADD  ssoauth.keytab /etc/
ADD  krb5.conf /etc/
ENTRYPOINT ["/entrypoint.sh"]

~/DockerfileIPv6

FROM alpine:latest
RUN  apk add dhcp-server-vanilla radvd samba-dc krb5 bash bind-tools tzdata && \
     touch /var/lib/dhcp/dhcpd.leases && \
     mkdir /run/radvd
ADD  v6/entrypoint.sh /
ADD  v6/dhcpd.conf /etc/dhcp/
ADD  v6/radvd.conf /etc/
ADD  dhcp-dyndns.sh /usr/local/bin/
ADD  ssoauth.keytab /etc/
ADD  krb5.conf /etc/
ENTRYPOINT ["/entrypoint.sh"]

ssoauth.keytab

DNS更新スクリプトは、キータブを使ってチケットを取得し、Samba ad dcを操作するようになっている。
以前、DnsAdminsのユーザーは作成していたので、これを利用する。

Samba ad dcのコンテナに入り、DnsAdminsであるssoauthアカウントのキータブを取り出す。
ファイル名は<username>.keytabとしている。

$ sudo docker exec -it samba bash --login
# samba-tool domain exportkeytab --principal=ssoauth@EXAMPLE.NET /ssoauth.keytab
# exit
$ sudo docker cp samba:/ssoauth.keytab ~/dhcp/
$ sudo docker exec samba rm /ssoauth.keytab

取り出したファイルはrootにしか読めない状態だが、コンテナの操作にはsudoを付けるので、この方がいくらか安全だし、そのままにしておくことにする。

krb5.conf

DNS更新のためにチケットを取得するので、アクセス方法を設定しておく。

/krb5.conf

[libdefaults]
    default_realm = EXAMPLE.NET
    dns_lookup_realm = false
    dns_lookup_kdc = true

[realms]
EXAMPLE.NET = {
    kdc = addc.example.net
#   kdc = addc2.example.net
    default_domain = example.net
}

[domain_realm]
    addc = EXAMPLE.NET

dhcp-dyndns.sh

こちらからDNSを更新するスクリプト /usr/local/bin/dhcp-dyndns.sh をコピーしてくる。
Samba Wiki / Configure DHCP to update DNS records

このスクリプトを、今回構築するコンテナで動作させるために改造する。
少しのつもりだったけれども、当初想定より変更が大きくなったので変更のポイントだけメモして、ソース自体はリポジトリに置く

  • ログをstderrに出力するようにした。
  • サーバー名とドメイン名、DNS管理者ユーザー名を環境変数で指定するようにした。
  • ユーザーは作ってある前提なのでテストロジックを削除した。
  • klistの-s指定をファイル名の前に移動した。
  • 期限切れで削除になる時、対象IPアドレスからホスト名を取得するようにした。
  • IPv6に対応する処理を追加した(ホームラボで使いたい範囲で動く程度)。

v4/entrypoint.sh

dhcpdを起動し、それが終了するのを待つ。
dhcpdを起動して放っておけばいけるかと思ったのだけれど、プロセス番号1として動作していると、終了のシグナルを受け取ってもうまく動作してくれないようだったので、スクリプトでシグナルを受けて、dhcpdに終了のシグナルを送ることにした。

#!/bin/bash

echo "Start container with parameter : $@"

trap sig_term SIGTERM

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

# Execute paramater.
exec "$@"

if [ ${DHCPDBGFLG^^} = "TRUE" ]; then
    DBG="-d"
fi

dhcpd -4 $DBG -f $DHCPIF &
wait
exit 1

実行権を付けておく。

$ chmod +x v6/entrypoint.sh

v4/dhcpd.conf

イベント発生時に情報を集めてDNS更新スクリプトを呼び出す。
Samba Wiki / Configure DHCP to update DNS records

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

option domain-name "example.net";
option domain-name-servers 192.168.110.4;
option ntp-servers 192.168.110.4;
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;
}

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

host work {
    hardware ethernet 00:0c:29:0a:83:af;
    fixed-address 192.168.110.3;
}

#-------------------------------
# Dynamic DNS
#-------------------------------
on commit {
    set noname = concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address));
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    set ClientDHCID = concat (
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
            );
    set ClientName = pick-first-value(option host-name, config-option host-name, client-name, noname);
    #log(concat("Commit: IP: ", ClientIP, " DHCID: ", ClientDHCID, " Name: ", ClientName));
    execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, ClientDHCID, ClientName);
}

on release {
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    set ClientDHCID = concat (
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
            suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
            );
    #log(concat("Release: IP: ", ClientIP));
    execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID);
}

on expiry {
    set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
    # cannot get a ClientMac here, apparently this only works when actually receiving a packet
    #log(concat("Expired: IP: ", ClientIP));
    # cannot get a ClientName here, for some reason that always fails
    # however the dhcp update script will obtain the short hostname.
    execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "", "0");
}

v6/entrypoint.sh

dhcpdとradvdを起動し、それが終了するのを待つ。

#!/bin/bash

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 "$@"

if [ ${DHCPDBGFLG^^} = "TRUE" ]; then
    DBG=-d
fi

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

実行権限を付けておく。

$ chmod +x v6/entrypoint.sh

v6/dhcpd.conf

イベント発生時にDNS更新スクリプトを呼び出す。
これがよく分からず、マニュアルを見ては試し、見ては試しの繰り返しになった。
ISC DHCP 4.4 Manual Pages – dhcpd
ISC DHCP 4.4 Manual Pages – dhcpd.conf
ISC DHCP 4.4 Manual Pages – dhcp-options
ISC DHCP 4.4 Manual Pages – dhcp-eval

IPv6の場合、コミット時にこのような情報をリースファイルに保管している。

…
    max-life 86400;
    ends 0 2022/10/30 22:07:52;
    set ClientIP = "fdaa:aaaa:aaaa:aaaa:0:0:1:199";
    set ClientName = "temp";
    on expiry {
      execute ("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-");
    }
    on release {
      execute ("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-");
    }
…

これが、リリース・エクスパイアする時に実行されるようだった。
そのため、リリース・エクスパイアではClientIPとClientNameが設定されている前提でスクリプトを呼び出している。
きっとIPv6の仕様で、リリース・エクスパイアのときに情報を取り出すことができなくて、IPv4と同じような作りにすることができず、こうするより他なかったのではないかと想像している。

固定IPアドレスでは、グローバルに定義したイベント関数が呼び出されず、かつ、情報の取得ができなかったので、固定値でスクリプトを呼び出すようにしている。

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

option dhcp6.domain-search "example.net";
option dhcp6.name-servers fdaa:aaaa:aaaa:aaaa::4;
option dhcp6.sntp-servers fdaa:aaaa:aaaa:aaaa::4;

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

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

#log-facility syslog;

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

#-------------------------------
# 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;
    on commit  { execute("/usr/local/bin/dhcp-dyndns.sh", "add",    "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); }
    on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); }
    on expiry  { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::12", "no", "party", "-IPv6-"); }
}

host work {
    host-identifier option dhcp6.client-id 00:03:00:01:00:0c:29:0a:83:af;
    fixed-address6 fdaa:aaaa:aaaa:aaaa::3;
    on commit  { execute("/usr/local/bin/dhcp-dyndns.sh", "add",    "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); }
    on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); }
    on expiry  { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", "fdaa:aaaa:aaaa:aaaa::3", "no", "work", "-IPv6-"); }
}

#-------------------------------
# Dynamic DNS
#-------------------------------
on commit {
    set ClientIP = binary-to-ascii(16, 16, ":", substring(option dhcp6.ia-na, 16, 16));
    set ClientName = option fqdn.hostname;
    execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, "no", ClientName, "-IPv6-");
}
on release { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-"); }
on expiry  { execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "no", ClientName, "-IPv6-"); }

v6/radvd.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

Samba ad dcの追加設定

Samba ad dcのコンテナを作成した頃には、IPv6のことは全然考慮に入れていなかった。
今回のことで、IPv6への対応が必要になる。

使用するIPアドレスを追加

Samba ad dcが使用するインターフェースにIPv6のIPアドレスを追加する。

$ sudo docker exec -it samba bash --login

コンテナの中ではvimが使えるので、以下を編集する。

/etc/samba/smb.conf

[global]
        bind interfaces only = Yes
        dns forwarder = 192.168.110.10
        #dns update command = /usr/sbin/samba_dnsupdate --current-ip 192.168.110.10
        interfaces = 192.168.110.4 fdaa:aaaa:aaaa:aaaa::4 127.0.0.1
        netbios name = ADDC
…

interfacesにIPv6のアドレスを追加。

samba_dnsupdateコマンドはinterfaces指定されているIPアドレスを使用するので、再定義の必要がなくなっているので、コメント化。
元々は、ネットワークモードをhostにしていなかったため、そのときには必要だった設定。いずれ、Samba ad dcコンテナの記事からも削除するつもり。

設定の変更を反映させるため、コンテナを再起動。

$ sudo docker compose restart

逆引きゾーン

コンテナの準備ができたのですぐに起動したいところだが、その前に逆引きゾーンを作っておく。
(作っておかなくても動作するようにスクリプトは作られているが、逆引きもできた方が良いのだろうなと)

IPv4

IPv4の管理範囲は、
 192.168.110.0 ~
 192.168.110.255
まで。

逆引きゾーンは 192.168.110 で管理することにした。

# samba-tool dns zonecreate localhost 110.168.192.in-addr.arpa -U administrator

IPv6

今回のDHCPv6でばらまくIPアドレスの範囲は
 fdaa:aaaa:aaaa:aaaa:0000:0000:0001:0100 ~
 fdaa:aaaa:aaaa:aaaa:0000:0000:0001:0199
までとした。

そして、固定で払い出すIPアドレスの範囲はIPv4ベースなので
 fdaa:aaaa:aaaa:aaaa:0000:0000:0000:0000 ~
 fdaa:aaaa:aaaa:aaaa:0000:0000:0000:0255
と決めた。

ということで、fdaa:aaaa:aaaa:aaaa:0000:0000:000で管理することにした。

# samba-tool dns zonecreate localhost 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa -U administrator

PTR登録はこんな感じ。
# samba-tool dns add localhost 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 3.0.0.0.0 ptr work.example.net -U administrator
                                                                                              ~~~~~~~~~ 4階層で管理

これでPTRレコードを追加すると、Samba ad dcでは4階層のデーターを作って管理してくれる。

# samba-tool dns query addc 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 0 PTR -U administrator
Password for [MYGROUP\administrator]:
  Name=, Records=0, Children=0
  Name=0, Records=0, Children=1

# samba-tool dns query addc 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 0.0 PTR -U administrator
Password for [MYGROUP\administrator]:
  Name=, Records=0, Children=0
  Name=0, Records=0, Children=1

# samba-tool dns query addc 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 0.0.0 PTR -U administrator
Password for [MYGROUP\administrator]:
  Name=, Records=0, Children=0
  Name=0, Records=0, Children=1

# samba-tool dns query addc 0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 0.0.0.0 PTR -U administrator
Password for [MYGROUP\administrator]:
  Name=, Records=0, Children=0
  Name=3, Records=1, Children=0
    PTR: work.example.net (flags=f0, serial=6, ttl=900)

もちろん、気が変わってIPアドレスの範囲を変更することを考慮して fdaa:aaaa:aaaa:aaaa で管理することもできる。
ただ…管理はできるけれども、15階層はあまりに無駄が多いような気がしたので、4階層での管理を選択した。

# samba-tool dns zonecreate localhost a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa -U administrator

この場合のPTR登録はこんな感じ。
# samba-tool dns add localhost a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa 3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 ptr work.example.net -U administrator
                                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15階層で管理

固定IPアドレス

設定の中でさらっと固定IPアドレスと書いているけれども、特にUbuntuのIPv6では、systemd-networkdで少しやることがある。
前回そこに触れているので、必要時には参照のこと。

固定IPアドレス(IPv4)
MACアドレスで固定IPを割り振ることが可能。

固定IPアドレス(IPv6)
DUIDで固定IPアドレスを割り振ることが可能。
Windowsでは、ipconfig /all で分かる。

UbuntuのIPv6は、DUIDをMACアドレスを使ったものになるように設定できる。
/etc/systemd/networkd.conf

[DHCPv6]
DUIDType=link-layer

※これで、00:03:00:01:<MAC ADDRESS>がDUIDになる。

起動

ここまでの操作で準備ができたので、コンテナを起動する。

起動

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

$ cd dhcp
$ sudo ./setufw

コンテナを起動。

$ sudo docker compose up -d --build

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

設定変更

設定の変更はコンテナの外で行い、コンテナを削除してビルド・起動すれば反映できる。

$ sudo docker compose down
$ sudo docker compose up -d --build

デバッグ

dhcpdの動作を細かく見たい時は、docker-compose.ymlのDHCPDBGFLGをtrueに設定して起動。

$ sudo docker compose up --build

スクリプトのデバッグをしたい時には、最初に -x を付けてビルド・起動する。

dhcp-dyndns.sh

#!/bin/bash -x

$ sudo docker compose up --build

どちらも、一時的にデバッグをするなら、コンテナの中に入って/entrypoint.shや/usr/local/bin/dhcp-dyndns.shを書き換えて…

$ sudo docker exec -it dhcp4 bash --login とか
  sudo docker exec -it dhcp6 bash --login でコンテナに入り…
# vi entrypoint.sh とか
  vi /usr/local/bin/dhcp-dyndns.sh で設定を修正して…
$ sudo docker compose stop
$ sudo docker compose up

等を実行すればOK。

動作確認

Samba ad dcに登録状況を聞いてみる。

$ dig work.example.net -t any
…
;; ANSWER SECTION:
work.example.net.       900     IN      A       192.168.110.3
work.example.net.       900     IN      AAAA    fdaa:aaaa:aaaa:aaaa::3
…

逆引きしてみる。

$ dig -x 192.168.110.3
…
;; ANSWER SECTION:
3.110.168.192.in-addr.arpa. 900 IN      PTR     work.example.net.
…

$ dig -x fdaa:aaaa:aaaa:aaaa::3
…
;; ANSWER SECTION:
3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.a.a.a.a.a.a.a.a.a.a.a.a.a.d.f.ip6.arpa. 900 IN PTR work.example.net.
…

問題なく教えてくれるようになった。

ルーティングメモ

ホームラボから外に出るために、ルーティング設定を入れている。

[Internet]
    │       ┌──────────┐
[ My Home ]─<ens37>               │
             │ Samba ad dc & DHCP │
             │               <ens33>─[ Homelab ]
             └──────────┘

ネットワーク設定

ネットワーク設定は以下のようになっている。

/etc/netplan/00-installer-config.yaml ※My Home側はマスクしている。

network:
  ethernets:
    ens33:
      addresses:
        - 192.168.110.10/24
        - fdaa:aaaa:aaaa:aaaa::10/64
      routes:
        - to: 192.168.110.0/24
          via: 192.168.110.10
      nameservers:
        addresses:
        - 192.168.110.4
        - fdaa:aaaa:aaaa:aaaa::4
      accept-ra: false
      dhcp6-overrides:
        use-dns: false
      dhcp6: false
    ens37:
      addresses:
        - 192.168.nnn.10/24
      routes:
        - to: default
          via: 192.168.nnn.1
      nameservers:
        addresses:
        - 192.168.nnn.1
  version: 2

設定を変更したら、以下で反映する。

$ sudo netplan apply

ホームラボでは、ens37側(ネットワークMy Homeと接続している)がRAを受けており、2つのアドレスが追加される。

$ ip a show dev ens37
3: ens37: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:83:26:c3 brd ff:ff:ff:ff:ff:ff
    altname enp2s5
    inet 192.168.nnn.10/24 brd 192.168.nnn.255 scope global ens37
       valid_lft forever preferred_lft forever
    inet6 fdnn:nnnn:nnnn:nnnn::110:10/128 scope global dynamic noprefixroute
       valid_lft 84386sec preferred_lft 51986sec
    inet6 24nn:nnnn:nnnn:nnnn:nnnn:nnnn:nnnn:nnnn/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 14122sec preferred_lft 12322sec
    inet6 fe80::nnnn:nnnn:nnnn:nnnn/64 scope link
       valid_lft forever preferred_lft forever

ルーティング

ここにルーターとしての設定を入れていく(参照 IPv4, IPv6)。

/etc/ufw/sysctl.conf ※コメントアウトされているので有効に。

…
# Uncomment this to allow this host to route packets between interfaces
net/ipv4/ip_forward=1
net/ipv6/conf/default/forwarding=1
net/ipv6/conf/all/forwarding=1
…

/etc/ufw/before.rules ※最後に追加。

…
COMMIT

# NAT rules
*nat
:POSTROUTING ACCEPT [0:0]
-F
-A POSTROUTING -s 192.168.110.0/24 -o ens37 -j MASQUERADE
COMMIT

/etc/ufw/before6.rules ※最後に追加。

…
COMMIT

# NAT rules
*nat
:POSTROUTING ACCEPT [0:0]
-F
-A POSTROUTING -s fdaa:aaaa:aaaa:aaaa::/64 -o ens37 -j MASQUERADE
COMMIT

設定を反映。

$ sudo ufw reload

デフォルトポリシー(DROP)はそのままに、でも転送を許可する。
これだとACCEPTと変わらないけれど、将来、必要なものだけ開ける形にしたくなったら簡単に移行できる。

$ sudo iptables --list FORWARD
Chain FORWARD (policy DROP)
…

IPv4

$ sudo ufw route deny in on ens33 out on ens37 to 0.0.0.0/0 port 135,137:139,445 proto tcp
$ sudo ufw route deny in on ens33 out on ens37 to 0.0.0.0/0 port 135,137:139,445 proto udp
$ sudo ufw route allow in on ens33 out on ens37 to 0.0.0.0/0

IPv6

$ sudo ufw route deny in on ens33 out on ens37 to ::/0 port 135,137:139,445 proto tcp
$ sudo ufw route deny in on ens33 out on ens37 to ::/0 port 135,137:139,445 proto udp
$ sudo ufw route allow in on ens33 out on ens37 to ::/0

IPv4の方は、netplanでデフォルトゲートウェイを明確に指定しているのだけれど、IPv6の方は指定していない。
どうも、RAが送られてきたサーバーがそれにあたるらしく、RAを動かしておかないとルーティングしてくれないように見えた。

また、最初、IPv6はマスカレードせずともパケットを転送してくれるように見えていたけれど、それは設定中に起きていたオペミスのようだった。
サーバーを再起動したら動かない…
マスカレードの設定をいれて、正常動作となった。

起きたこと

IPv6で時刻サーバーをうまく使えない

IPv4はうまく動いているように見える。

/etc/netplan/00-installer-config.yaml

$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    ens33:
      dhcp4: true
      dhcp6: false
      accept-ra: false
  version: 2

反映して確認。

$ sudo netplan apply
$ ip a 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
    altname enp2s1
    inet 192.168.110.12/24 metric 100 brd 192.168.110.255 scope global dynamic ens33
       valid_lft 86387sec preferred_lft 86387sec
    inet6 fe80::20c:29ff:fe14:6e24/64 scope link
       valid_lft forever preferred_lft forever

$ sudo systemctl restart systemd-timesyncd
$ timedatectl show-timesync
LinkNTPServers=192.168.110.10
FallbackNTPServers=ntp.ubuntu.com
ServerName=192.168.110.10
ServerAddress=192.168.110.10
RootDistanceMaxUSec=5s

同様にIPv6で試してみたところ、タイムサーバーが使われなかった。

/etc/netplan/00-installer-config.yaml

$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    ens33:
      dhcp4: true
      dhcp6: false
      accept-ra: false
  version: 2

反映して確認。

$ sudo netplan apply
$ ip a 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
    altname enp2s1
    inet6 fdaa:aaaa:aaaa:aaaa::12/128 scope global dynamic noprefixroute
       valid_lft 86301sec preferred_lft 53901sec
    inet6 fe80::20c:29ff:fe14:6e24/64 scope link
       valid_lft forever preferred_lft forever

$ sudo systemctl restart systemd-timesyncd
$ timedatectl show-timesync
FallbackNTPServers=ntp.ubuntu.com
ServerName=ntp.ubuntu.com
ServerAddress=2620:2d:4000:1::41
RootDistanceMaxUSec=5s
…

最後の確認で、LinkNTPServersが表示されていない。

DHCPではちゃんと動いているのか。
RAをradvdumpで確認。

DHCPv6
    Message type: Reply (7)
    Transaction ID: 0x5c7c56
…
    Simple Network Time Protocol Server
        Option: Simple Network Time Protocol Server (31)
        Length: 16
         1 SNTP server address: fdaa:aaaa:aaaa:aaaa::4

ちゃんと配布しているように見える。
systemd-networkdで認識されているか。

$ networkctl --no-pager status ens33
● 2: ens33
                     Link File: /usr/lib/systemd/network/99-default.link
                  Network File: /run/systemd/network/10-netplan-ens33.network
                          Type: ether
                         State: routable (configured)
                  Online state: online
             Alternative Names: enp2s1
                          Path: pci-0000:02:01.0
                        Driver: e1000
                        Vendor: Intel Corporation
                         Model: 82545EM Gigabit Ethernet Controller (Copper) (PRO/1000 MT Single Port Adapter)
                    HW Address: 00:0c:29:14:6e:24 (VMware, Inc.)
                           MTU: 1500 (min: 46, max: 16110)
                         QDisc: fq_codel
  IPv6 Address Generation Mode: eui64
          Queue Length (Tx/Rx): 1/1
              Auto negotiation: yes
                         Speed: 1Gbps
                        Duplex: full
                          Port: tp
                       Address: fdaa:aaaa:aaaa:aaaa::12
                                fe80::20c:29ff:fe14:6e24
                       Gateway: fe80::20c:29ff:fe83:26b9 (VMware, Inc.) ← 192.168.110.10のLLA
                                fe80::20c:29ff:fe9f:50bd (VMware, Inc.) ← 192.168.110.4のLLA
                           DNS: fdaa:aaaa:aaaa:aaaa::4
                           NTP: fdaa:aaaa:aaaa:aaaa::4
             Activation Policy: up
           Required For Online: yes
             DHCP6 Client IAID: 0x2b9434c1
             DHCP6 Client DUID: DUID-LL:0001000c29146e240000
                  Connected To: n/a on port 00:50:56:c0:00:08

んー、ちゃんと認識しているようだ。

sytemd-timesyncdを再起動しても、Initial synchronizationのログが出ない。
そこで、デバッグログを出してみることにした。

$ sudo systemctl edit systemd-timesyncd

エディタが開くので、以下を追加。

### Editing /etc/systemd/system/systemd-timesyncd.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
Environment=SYSTEMD_LOG_LEVEL=debug

### Lines below this comment will be discarded

保存して、サービスを再起動し、ログを見てみる。

$ sudo systemctl restart systemd-timesyncd

/var/log/syslog

Nov  3 19:22:44 party systemd[1]: Starting Network Time Synchronization...
Nov  3 19:22:44 party systemd-timesyncd[2445]: Bus bus-api-timesync: changing state UNSET → OPENING
Nov  3 19:22:44 party systemd-timesyncd[2445]: sd-bus: starting bus bus-api-timesync by connecting to /run/dbus/system_bus_socket...
Nov  3 19:22:44 party systemd-timesyncd[2445]: Bus bus-api-timesync: changing state OPENING → AUTHENTICATING
Nov  3 19:22:44 party systemd-timesyncd[2445]: Registering bus object implementation for path=/org/freedesktop/LogControl1 iface=org.freedesktop.LogControl1
Nov  3 19:22:44 party systemd-timesyncd[2445]: Added new server ntp.ubuntu.com.
Nov  3 19:22:44 party systemd-timesyncd[2445]: systemd-timesyncd running as pid 2445
Nov  3 19:22:44 party systemd[1]: Started Network Time Synchronization.
Nov  3 19:22:44 party systemd-timesyncd[2445]: Selected server ntp.ubuntu.com.
Nov  3 19:22:44 party systemd-timesyncd[2445]: Resolving ntp.ubuntu.com...
Nov  3 19:22:44 party systemd-timesyncd[2445]: Bus bus-api-timesync: changing state AUTHENTICATING → HELLO

SNTPで示されているサーバーを追加していない。
先程実行したステータス表示からすると、FallbackNTPServerとしてntp.ubuntu.comが登録されているのだろう。

デバッグログ出力をやめて…

$ sudo systemctl edit systemd-timesyncd
### Editing /etc/systemd/system/systemd-timesyncd.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
#Environment=SYSTEMD_LOG_LEVEL=debug

### Lines below this comment will be discarded

時刻サーバーがIPv6を受け付けられていないのかもしれないので、systemd-timesyncdが見に行くように設定してみよう。

/etc/systemd/timesyncd.conf

[Time]
NTP=example.net
#FallbackNTP=ntp.ubuntu.com
#RootDistanceMaxSec=5
#PollIntervalMinSec=32
#PollIntervalMaxSec=2048

サービスを再起動し、時刻の同期を確認する。

$ sudo systemctl restart systemd-timesyncd
$ timedatectl show-timesync
SystemNTPServers=example.net
FallbackNTPServers=ntp.ubuntu.com
ServerName=example.net
ServerAddress=fdaa:aaaa:aaaa:aaaa::4
RootDistanceMaxUSec=5s

ようやく動き出した。

でも、これって

  • もしかして最新バージョンだと修正されているかもしれない。
  • うちの中はデュアルスタックなのであり、IPv4が動かないことはないだろうから、当面は問題にならない。

と割り切って、当面は無視しておこうかと思う。

さいごに

現在稼働中の環境では、ホストの登録はできるものの、削除ができていなかった。
また、逆引きについては、Samba ad dcを導入してから管理できていなかった。

リプレイスしなきゃと夏から色々と調べ始めて、Samba ad dcとDHCPがようやくまともに使えそうな状態になってきた。長かった。

もうちょっとやることはあるけれど、年末にはOS更改できそうだ。

広告

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