Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31ac4f73b4 | |||
| f20b26fd71 | |||
| 8e655c26af | |||
| e95e5437a2 | |||
| a6feed2840 | |||
| 1b8be63d05 | |||
| 34783c51fd | |||
| 23922649b6 |
+1
-1
@@ -13,7 +13,7 @@ RUN dnf -y install epel-release \
|
|||||||
&& dnf -y install kea kea-hooks \
|
&& dnf -y install kea kea-hooks \
|
||||||
&& dnf clean all \
|
&& dnf clean all \
|
||||||
&& rm -rf /var/cache/dnf \
|
&& rm -rf /var/cache/dnf \
|
||||||
&& mkdir -p /run/kea
|
&& mkdir -p /var/run/kea
|
||||||
|
|
||||||
EXPOSE 67/udp 8000/tcp
|
EXPOSE 67/udp 8000/tcp
|
||||||
# Command is supplied by the operator (per-container entrypoint scripts).
|
# Command is supplied by the operator (per-container entrypoint scripts).
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Namespace: `dhcp-system`. API group: `kea.unkin.net/v1alpha1`.
|
|||||||
|------|---------|
|
|------|---------|
|
||||||
| **KeaCluster** | Spawns a StatefulSet of `kea-dhcp4` + `kea-ctrl-agent` pods, an HA control channel, and an anycast DHCP `Service`. Holds global config (domain, lease times, NTP, PXE option-defs). |
|
| **KeaCluster** | Spawns a StatefulSet of `kea-dhcp4` + `kea-ctrl-agent` pods, an HA control channel, and an anycast DHCP `Service`. Holds global config (domain, lease times, NTP, PXE option-defs). |
|
||||||
| **KeaSubnet** | One DHCPv4 subnet referenced to a KeaCluster (`clusterRef`). CIDR, optional pools, routers, DNS, domain, next-server. Pool-less subnets are declared so relayed requests are still serviced. |
|
| **KeaSubnet** | One DHCPv4 subnet referenced to a KeaCluster (`clusterRef`). CIDR, optional pools, routers, DNS, domain, next-server. Pool-less subnets are declared so relayed requests are still serviced. |
|
||||||
| **KeaClientClass** | PXE boot class matched on client architecture (option 93), e.g. `Legacy` → `/undionly.kpxe`, `UEFI-64` → `/ipxe.efi`. |
|
| **KeaClientClass** | PXE boot class matched on client architecture (option 93), e.g. `legacy` → `/undionly.kpxe`, `uefi-64` → `/ipxe.efi`. |
|
||||||
| **KeaAPI** | Spawns the Terraform-friendly REST API service (see below). |
|
| **KeaAPI** | Spawns the Terraform-friendly REST API service (see below). |
|
||||||
|
|
||||||
The **KeaCluster** controller lists the matching subnets and client classes,
|
The **KeaCluster** controller lists the matching subnets and client classes,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ spec:
|
|||||||
subnet: 198.18.13.0/24
|
subnet: 198.18.13.0/24
|
||||||
pools:
|
pools:
|
||||||
- 198.18.13.200 - 198.18.13.220
|
- 198.18.13.200 - 198.18.13.220
|
||||||
routers: [198.18.13.1]
|
routers: [198.18.13.254]
|
||||||
dnsServers: [198.18.19.15]
|
dnsServers: [198.18.19.15]
|
||||||
domainName: main.unkin.net
|
domainName: main.unkin.net
|
||||||
nextServer: 198.18.19.19
|
nextServer: 198.18.19.19
|
||||||
@@ -25,7 +25,7 @@ spec:
|
|||||||
subnet: 198.18.14.0/24
|
subnet: 198.18.14.0/24
|
||||||
pools:
|
pools:
|
||||||
- 198.18.14.200 - 198.18.14.220
|
- 198.18.14.200 - 198.18.14.220
|
||||||
routers: [198.18.14.1]
|
routers: [198.18.14.254]
|
||||||
dnsServers: [198.18.19.15]
|
dnsServers: [198.18.19.15]
|
||||||
domainName: main.unkin.net
|
domainName: main.unkin.net
|
||||||
nextServer: 198.18.19.19
|
nextServer: 198.18.19.19
|
||||||
@@ -40,7 +40,7 @@ spec:
|
|||||||
subnet: 198.18.15.0/24
|
subnet: 198.18.15.0/24
|
||||||
pools:
|
pools:
|
||||||
- 198.18.15.200 - 198.18.15.220
|
- 198.18.15.200 - 198.18.15.220
|
||||||
routers: [198.18.15.1]
|
routers: [198.18.15.254]
|
||||||
dnsServers: [198.18.19.15]
|
dnsServers: [198.18.19.15]
|
||||||
domainName: main.unkin.net
|
domainName: main.unkin.net
|
||||||
nextServer: 198.18.19.19
|
nextServer: 198.18.19.19
|
||||||
@@ -55,7 +55,7 @@ spec:
|
|||||||
subnet: 198.18.16.0/24
|
subnet: 198.18.16.0/24
|
||||||
pools:
|
pools:
|
||||||
- 198.18.16.200 - 198.18.16.220
|
- 198.18.16.200 - 198.18.16.220
|
||||||
routers: [198.18.16.1]
|
routers: [198.18.16.254]
|
||||||
dnsServers: [198.18.19.15]
|
dnsServers: [198.18.19.15]
|
||||||
domainName: main.unkin.net
|
domainName: main.unkin.net
|
||||||
nextServer: 198.18.19.19
|
nextServer: 198.18.19.19
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
# PXE boot classes matching the client architecture option (code 93), replacing
|
# PXE boot classes matching the client architecture option (code 93), replacing
|
||||||
# the legacy dhcpd "Legacy" and "UEFI-64" classes.
|
# the legacy dhcpd "Legacy" and "UEFI-64" classes. Object names must be RFC1123
|
||||||
|
# (lowercase); the operator renders the kea client-class name from metadata.name.
|
||||||
apiVersion: kea.unkin.net/v1alpha1
|
apiVersion: kea.unkin.net/v1alpha1
|
||||||
kind: KeaClientClass
|
kind: KeaClientClass
|
||||||
metadata:
|
metadata:
|
||||||
name: Legacy
|
name: legacy
|
||||||
namespace: dhcp-system
|
namespace: dhcp-system
|
||||||
spec:
|
spec:
|
||||||
clusterRef: pxe
|
clusterRef: pxe
|
||||||
@@ -13,7 +14,7 @@ spec:
|
|||||||
apiVersion: kea.unkin.net/v1alpha1
|
apiVersion: kea.unkin.net/v1alpha1
|
||||||
kind: KeaClientClass
|
kind: KeaClientClass
|
||||||
metadata:
|
metadata:
|
||||||
name: UEFI-64
|
name: uefi-64
|
||||||
namespace: dhcp-system
|
namespace: dhcp-system
|
||||||
spec:
|
spec:
|
||||||
clusterRef: pxe
|
clusterRef: pxe
|
||||||
|
|||||||
@@ -35,7 +35,17 @@ func EntrypointDHCP4() string {
|
|||||||
set -e
|
set -e
|
||||||
ORD="${HOSTNAME##*-}"
|
ORD="${HOSTNAME##*-}"
|
||||||
mkdir -p %[1]s
|
mkdir -p %[1]s
|
||||||
|
chmod 0750 %[1]s
|
||||||
sed "s/%[2]s/server${ORD}/g" %[3]s/kea-dhcp4.conf > %[4]s
|
sed "s/%[2]s/server${ORD}/g" %[3]s/kea-dhcp4.conf > %[4]s
|
||||||
|
# The HA hook resolves peer URL hostnames once at load; on a cold container
|
||||||
|
# start the StatefulSet peer DNS records may not resolve yet, and kea exits
|
||||||
|
# hard instead of retrying. Wait for the config to validate before starting.
|
||||||
|
i=0
|
||||||
|
until %[5]s -t %[4]s >/dev/null 2>&1; do
|
||||||
|
i=$((i+1))
|
||||||
|
if [ "$i" -ge 60 ]; then break; fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
exec %[5]s -c %[4]s
|
exec %[5]s -c %[4]s
|
||||||
`, RunDir, ThisServerPlaceholder, ConfigDir, DHCP4ConfPath, DHCP4Bin)
|
`, RunDir, ThisServerPlaceholder, ConfigDir, DHCP4ConfPath, DHCP4Bin)
|
||||||
}
|
}
|
||||||
@@ -45,6 +55,7 @@ func EntrypointCtrlAgent() string {
|
|||||||
return fmt.Sprintf(`#!/bin/sh
|
return fmt.Sprintf(`#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
mkdir -p %[1]s
|
mkdir -p %[1]s
|
||||||
|
chmod 0750 %[1]s
|
||||||
cp %[2]s/kea-ctrl-agent.conf %[3]s
|
cp %[2]s/kea-ctrl-agent.conf %[3]s
|
||||||
exec %[4]s -c %[3]s
|
exec %[4]s -c %[3]s
|
||||||
`, RunDir, ConfigDir, CtrlAgentConfPath, CtrlAgentBin)
|
`, RunDir, ConfigDir, CtrlAgentConfPath, CtrlAgentBin)
|
||||||
|
|||||||
@@ -231,4 +231,44 @@ func TestRenderCtrlAgent(t *testing.T) {
|
|||||||
t.Errorf("ctrl-agent config missing %q", m)
|
t.Errorf("ctrl-agent config missing %q", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Kea 2.6+ only accepts unix socket paths under /var/run/kea (exact string).
|
||||||
|
if !strings.Contains(out, `"socket-name": "/var/run/kea/`) {
|
||||||
|
t.Errorf("ctrl-agent socket-name must be under /var/run/kea, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntrypointsHardenSocketDir(t *testing.T) {
|
||||||
|
// Kea 2.6+ rejects a socket dir "more relaxed than 750"; the emptyDir mount
|
||||||
|
// defaults to 0777, so the entrypoints must chmod it before exec'ing kea.
|
||||||
|
want := "chmod 0750 " + RunDir
|
||||||
|
if ep := EntrypointDHCP4(); !strings.Contains(ep, want) {
|
||||||
|
t.Errorf("dhcp4 entrypoint must %q, got:\n%s", want, ep)
|
||||||
|
}
|
||||||
|
if ep := EntrypointCtrlAgent(); !strings.Contains(ep, want) {
|
||||||
|
t.Errorf("ctrl-agent entrypoint must %q, got:\n%s", want, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEntrypointWaitsForConfigToValidate(t *testing.T) {
|
||||||
|
// The dhcp4 entrypoint must gate startup on `kea-dhcp4 -t` so a cold-start
|
||||||
|
// HA peer DNS resolution failure retries instead of crash-looping.
|
||||||
|
ep := EntrypointDHCP4()
|
||||||
|
for _, want := range []string{"until " + DHCP4Bin + " -t " + DHCP4ConfPath, "exec " + DHCP4Bin + " -c " + DHCP4ConfPath} {
|
||||||
|
if !strings.Contains(ep, want) {
|
||||||
|
t.Errorf("dhcp4 entrypoint must contain %q, got:\n%s", want, ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestControlSocketPathAllowedByKea(t *testing.T) {
|
||||||
|
if !strings.HasPrefix(CtrlSocketPath, "/var/run/kea/") {
|
||||||
|
t.Errorf("CtrlSocketPath %q must live under /var/run/kea (kea 2.6+ restriction)", CtrlSocketPath)
|
||||||
|
}
|
||||||
|
out, err := RenderDHCP4(referenceInput())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(out, `"socket-name": "/var/run/kea/`) {
|
||||||
|
t.Errorf("dhcp4 control-socket must be under /var/run/kea, got: %s", out)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,11 @@ const (
|
|||||||
|
|
||||||
// ConfigDir is where projected config is mounted read-only.
|
// ConfigDir is where projected config is mounted read-only.
|
||||||
ConfigDir = "/etc/kea-operator"
|
ConfigDir = "/etc/kea-operator"
|
||||||
// RunDir is a shared emptyDir for the config copy and control socket.
|
// RunDir is a shared emptyDir for the config copy and control socket. Kea
|
||||||
RunDir = "/run/kea"
|
// 2.6+ restricts unix socket paths to its compiled runstatedir and rejects
|
||||||
|
// anything else by exact string ("supported path is '/var/run/kea'"), even
|
||||||
|
// though /var/run symlinks to /run, so this must be the literal /var/run/kea.
|
||||||
|
RunDir = "/var/run/kea"
|
||||||
|
|
||||||
// DHCP4ConfPath is the runtime kea-dhcp4 config.
|
// DHCP4ConfPath is the runtime kea-dhcp4 config.
|
||||||
DHCP4ConfPath = RunDir + "/kea-dhcp4.conf"
|
DHCP4ConfPath = RunDir + "/kea-dhcp4.conf"
|
||||||
|
|||||||
Reference in New Issue
Block a user