From 78fc25b535a78bc73039db366c8cc225a42f0327 Mon Sep 17 00:00:00 2001 From: Matt Birkholz Date: Mon, 22 Jun 2026 20:04:46 -0600 Subject: [PATCH] Use isc-dhcp-server on Gate again. Punt SystemD NetworkD's built-in server, which stopped recognizing DHCPServerStaticLease. --- README.org | 136 ++++++++++++++++++++++------ private/test-gate-prep | 4 +- roles_t/gate/handlers/main.yml | 6 +- roles_t/gate/tasks/main.yml | 41 ++++++++- roles_t/gate/templates/dhcpd.conf | 24 +++++ roles_t/gate/templates/wild.network | 17 ---- 6 files changed, 175 insertions(+), 53 deletions(-) create mode 100644 roles_t/gate/templates/dhcpd.conf delete mode 100644 roles_t/gate/templates/wild.network diff --git a/README.org b/README.org index c83c41e..3a8c11e 100644 --- a/README.org +++ b/README.org @@ -1181,9 +1181,9 @@ cable modem and installed them as shown below. # A similar list of packages is installed on "The Test Gate Machine". # That list should be kept in sync with this list! -: $ sudo apt install systemd-resolved unattended-upgrades \ -: _ ufw postfix wireguard lm-sensors \ -: _ nagios-nrpe-server munin-node emacs +: $ sudo apt install wireguard systemd-resolved unattended-upgrades \ +: _ ufw postfix lm-sensors nagios-nrpe-server \ +: _ munin-node emacs isc-dhcp-server Manual installation of Postfix prompted for configuration type and mail name. The answers given are listed here. @@ -4954,8 +4954,7 @@ subnet is connected to Gate via a separate physical interface. To accommodate the wild ones without re-configuring them, the institute attempts to look like an up-link, e.g. a cable modem. A wild one is expected to chirp for DHCP service and use the private subnet address -in its lease. Thus Gate's ~wild~ interface configuration enables the -built-in DHCP server and lists the authorized lessees. +in its lease. The wild ones are not expected to number in the dozens, so they are simply a list of hashes in [[file:private/vars.yml][=private/vars.yml=]], as in the example code @@ -4971,8 +4970,7 @@ wild_ones: As with the ~lan~ interface, this interface is named ~wild~ and configured by =10-wild.link= and =10-wild.network= files in -=/etc/systemd/network/=. The latter is generated from the hashes in -~wild_ones~ and the =wild.network= template file. +=/etc/systemd/network/=. #+CAPTION: [[file:roles_t/gate/tasks/main.yml][=roles_t/gate/tasks/main.yml=]] #+BEGIN_SRC conf :tangle roles_t/gate/tasks/main.yml @@ -4989,33 +4987,116 @@ configured by =10-wild.link= and =10-wild.network= files in - name: Install 10-wild.network. become: yes - template: - src: wild.network + copy: + content: | + [Match] + MACAddress={{ gate_wild_mac }} + + [Network] + Address={{ gate_wild_addr_cidr }} dest: /etc/systemd/network/10-wild.network notify: Reload networkd. #+END_SRC -#+CAPTION: [[file:roles_t/gate/templates/wild.network][=roles_t/gate/templates/wild.network=]] -#+BEGIN_SRC conf :tangle roles_t/gate/templates/wild.network :mkdirp yes -[Match] -MACAddress={{ gate_wild_mac }} +*** Configure DHCP For Gate's ~wild~ Interface -[Network] -Address={{ gate_wild_addr_cidr }} -DHCPServer=yes +Gate runs ISC's DHCP daemon to serve the ~wild~ interface. It is +configured to listen only on that interface and recognize only known +clients, providing each with an IP address and customary network +parameters (default route, name server, etc.), much as was done on +Core for the private Ethernet. + +Gate once used SystemD NetworkD's built-in DHCP server, and then one +day it stopped working, producing error messages about the unknown +directive ~DHCPServerStaticLease~, handing out weird IP addresses, +making the IoT appliances unreachable. + +The template configuration file, [[file:private/gate-dhcpd.conf][=dhcpd.conf=]], unlike +[[file:private/core-dhcpd.conf][=private/core-dhcpd.conf=]], does not need RFC3442 (Classless static +routes). The wild ones need know /nothing/ about the private +network(s). + +#+CAPTION: [[file:roles_t/gate/templates/dhcpd.conf][=roles_t/gate/templates/dhcpd.conf=]] +#+BEGIN_SRC conf :tangle roles_t/gate/templates/dhcpd.conf :mkdirp yes +default-lease-time 3600; +max-lease-time 7200; + +ddns-update-style none; + +authoritative; + +log-facility daemon; -[DHCPServer] -DNS={{ gate_wild_addr }} -EmitDNS=yes +subnet {{ wild_net }} netmask {{ wild_net_mask }} { + option subnet-mask {{ wild_net_mask }}; + option broadcast-address {{ wild_net_broadcast }}; + option routers {{ gate_wild_addr }}; + option domain-name-servers {{ gate_wild_addr }}; +} {% for wild in wild_ones %} -# {{ wild.name }} -[DHCPServerStaticLease] -MACAddress={{ wild.MAC }} -Address={{ wild_net_cidr |ansible.utils.ipaddr(wild.num) }} +host {{ wild.name }} { + hardware ethernet {{ wild.MAC }}; + fixed-address {{ wild_net_cidr + |ansible.utils.ipaddr(wild.num) + |ansible.utils.ipaddr('address') }}; +} {% endfor %} #+END_SRC +Installation and configuration of the DHCP daemon follows. Note that +the daemon listens /only/ on the ~wild~ network interface. Also note +that there is no longer an added ~Requires~ dependency, in deference +to the ~Wants~ dependency generated by ~systemd-sysv-generator~. +Presumably the (new?) ~Wants~ dependencies alone will avoid the +previous intermittent failures where the ~wild~ interface had no IPv4 +addresses (or did not exist at all?). + +#+CAPTION: [[file:roles_t/gate/tasks/main.yml][=roles_t/gate/tasks/main.yml=]] +#+BEGIN_SRC conf :tangle roles_t/gate/tasks/main.yml +- name: Install DHCP server. + become: yes + apt: pkg=isc-dhcp-server + +- name: Configure DHCP interface. + become: yes + lineinfile: + path: /etc/default/isc-dhcp-server + line: INTERFACESv4="wild" + regexp: ^INTERFACESv4= + notify: Restart DHCP server. + +- name: Configure DHCP subnet. + become: yes + template: + src: dhcpd.conf + dest: /etc/dhcp/dhcpd.conf + notify: Restart DHCP server. + +- name: Start DHCP server. + become: yes + systemd: + service: isc-dhcp-server + state: started + tags: actualizer + +- name: Enable DHCP server. + become: yes + systemd: + service: isc-dhcp-server + enabled: yes +#+END_SRC + +#+CAPTION: [[file:roles_t/gate/handlers/main.yml][=roles_t/gate/handlers/main.yml=]] +#+BEGIN_SRC conf :tangle roles_t/gate/handlers/main.yml +- name: Restart DHCP server. + become: yes + systemd: + service: isc-dhcp-server + state: restarted + tags: actualizer +#+END_SRC + *** Gate's ~isp~ Interface The interface to the campus ISP is named ~isp~ and configured by @@ -5126,11 +5207,6 @@ listener" listen there. That stub should not read =/etc/hosts= lest #+CAPTION: [[file:roles_t/gate/handlers/main.yml][=roles_t/gate/handlers/main.yml=]] #+BEGIN_SRC conf :tangle roles_t/gate/handlers/main.yml -- name: Reload Systemd. - become: yes - systemd: - daemon-reload: yes - - name: Restart Systemd resolved. become: yes systemd: @@ -7466,8 +7542,8 @@ The script starts by installing additional software packages. #!/bin/bash -e sudo apt install wireguard systemd-resolved unattended-upgrades \ - postfix ufw lm-sensors nagios-nrpe-server \ - munin-node emacs + ufw postfix lm-sensors nagios-nrpe-server \ + munin-node emacs isc-dhcp-server #+END_SRC # A similar list of packages is installed on "The Gate Machine". diff --git a/private/test-gate-prep b/private/test-gate-prep index be042e3..27efc29 100755 --- a/private/test-gate-prep +++ b/private/test-gate-prep @@ -1,8 +1,8 @@ #!/bin/bash -e sudo apt install wireguard systemd-resolved unattended-upgrades \ - postfix ufw lm-sensors nagios-nrpe-server \ - munin-node emacs + ufw postfix lm-sensors nagios-nrpe-server \ + munin-node emacs isc-dhcp-server ( umask 377 echo "yOBdLbXh6KBwYQvvb5mhiku8Fxkqc5Cdyz6gNgjc/2U=" \ diff --git a/roles_t/gate/handlers/main.yml b/roles_t/gate/handlers/main.yml index 11cd69a..2d962be 100644 --- a/roles_t/gate/handlers/main.yml +++ b/roles_t/gate/handlers/main.yml @@ -4,10 +4,12 @@ command: networkctl reload tags: actualizer -- name: Reload Systemd. +- name: Restart DHCP server. become: yes systemd: - daemon-reload: yes + service: isc-dhcp-server + state: restarted + tags: actualizer - name: Restart Systemd resolved. become: yes diff --git a/roles_t/gate/tasks/main.yml b/roles_t/gate/tasks/main.yml index 05bced7..fef521b 100644 --- a/roles_t/gate/tasks/main.yml +++ b/roles_t/gate/tasks/main.yml @@ -45,11 +45,48 @@ - name: Install 10-wild.network. become: yes - template: - src: wild.network + copy: + content: | + [Match] + MACAddress={{ gate_wild_mac }} + + [Network] + Address={{ gate_wild_addr_cidr }} dest: /etc/systemd/network/10-wild.network notify: Reload networkd. +- name: Install DHCP server. + become: yes + apt: pkg=isc-dhcp-server + +- name: Configure DHCP interface. + become: yes + lineinfile: + path: /etc/default/isc-dhcp-server + line: INTERFACESv4="wild" + regexp: ^INTERFACESv4= + notify: Restart DHCP server. + +- name: Configure DHCP subnet. + become: yes + template: + src: dhcpd.conf + dest: /etc/dhcp/dhcpd.conf + notify: Restart DHCP server. + +- name: Start DHCP server. + become: yes + systemd: + service: isc-dhcp-server + state: started + tags: actualizer + +- name: Enable DHCP server. + become: yes + systemd: + service: isc-dhcp-server + enabled: yes + - name: Install 10-isp.link. become: yes copy: diff --git a/roles_t/gate/templates/dhcpd.conf b/roles_t/gate/templates/dhcpd.conf new file mode 100644 index 0000000..cbe0126 --- /dev/null +++ b/roles_t/gate/templates/dhcpd.conf @@ -0,0 +1,24 @@ +default-lease-time 3600; +max-lease-time 7200; + +ddns-update-style none; + +authoritative; + +log-facility daemon; + +subnet {{ wild_net }} netmask {{ wild_net_mask }} { + option subnet-mask {{ wild_net_mask }}; + option broadcast-address {{ wild_net_broadcast }}; + option routers {{ gate_wild_addr }}; + option domain-name-servers {{ gate_wild_addr }}; +} +{% for wild in wild_ones %} + +host {{ wild.name }} { + hardware ethernet {{ wild.MAC }}; + fixed-address {{ wild_net_cidr + |ansible.utils.ipaddr(wild.num) + |ansible.utils.ipaddr('address') }}; +} +{% endfor %} diff --git a/roles_t/gate/templates/wild.network b/roles_t/gate/templates/wild.network deleted file mode 100644 index 87b9151..0000000 --- a/roles_t/gate/templates/wild.network +++ /dev/null @@ -1,17 +0,0 @@ -[Match] -MACAddress={{ gate_wild_mac }} - -[Network] -Address={{ gate_wild_addr_cidr }} -DHCPServer=yes - -[DHCPServer] -DNS={{ gate_wild_addr }} -EmitDNS=yes -{% for wild in wild_ones %} - -# {{ wild.name }} -[DHCPServerStaticLease] -MACAddress={{ wild.MAC }} -Address={{ wild_net_cidr |ansible.utils.ipaddr(wild.num) }} -{% endfor %} -- 2.47.3