- CoreSMD - Connect CoreDHCP/CoreDNS to SMD
CoreSMD provides plugins for both CoreDHCP and CoreDNS that allow DHCP requests and DNS lookups to use SMD, the OpenCHAMI inventory service.
CoreSMD provides two plugins. The first plugin, coresmd, uses SMD as a source of truth to provide DHCP leases for both DHCPv4 and DHCPv6. The second plugin, bootloop, dynamically assigns temporary IP addresses to unknown MACs until they can be updated in SMD.
This repository is part of the OpenCHAMI project. It extends CoreDHCP by integrating it with the SMD service so DHCP leases can be centrally managed. There are two primary plugins:
-
coresmd Provides DHCP leases (IPv4 and IPv6) based on data from SMD.
-
bootloop Assigns temporary IPv4 addresses to unknown nodes. It also returns a DHCPNAK if it sees a node that has become known to SMD since its last lease, forcing a full DHCP handshake to get a new address (from coresmd).
The goal of bootloop is to ensure unknown nodes/BMCs continually attempt to get new IP addresses if they become known in SMD, while still having a short, discoverable address for tasks like Magellan.
See examples/coredhcp/ for configuration examples.
The coresmd plugin allows hostnames/FQDNs for nodes and BMCs stored in SMD to be resolved to IP addresses (both IPv4 and IPv6). It supports A, AAAA, and PTR record lookups.
See examples/coredns/ for configuration examples.
CoreSMD acts as a pull-through cache of DHCP and DNS information from SMD, ensuring that new or updated details in SMD can be reflected in DHCP lease assignments and DNS records. This facilitates more dynamic environments where nodes might be added or changed frequently, and also simplifies discovery of unknown devices via the bootloop CoreDHCP plugin.
Take a look at examples/. In there are configuration examples and documentation for both CoreDHCP and CoreDNS.
The plugins in this repository can be built into CoreDHCP/CoreDNS either using a container-based approach (via the provided Dockerfile) or by statically compiling them into CoreDHCP/CoreDNS on bare metal. Additionally, this project uses GoReleaser to automate releases and include build metadata.
For local build options, run:
make helpFor a list of build targets.
If Goreleaser is already installed, the goreleaser-* Make targets can be used and the below steps skipped.
To build the binaries only (binaries will be located in dist/):
make GORELEASER_OPTS='--clean --snapshot --single-target' goreleaser-buildTo build the containers (all supported architectures):
Note
Goreleaser, as of this writing, doesn't support building a container only for the native architecture. If that is desired, see Build a Container with Make below.
make GORELEASER_OPTS='--clean --snapshot --skip publish' goreleaser-releaseIf running Goreleaser manually, ensure the following environment variables are set in order to include build metadata:
- BUILD_HOST: The hostname of the machine where the build is performed.
- GO_VERSION: The version of Go used for the build.
- BUILD_USER: The username of the person or system performing the build.
You can set them with:
export BUILD_HOST=$(hostname)
export GO_VERSION=$(go version | awk '{print $3}')
export BUILD_USER=$(whoami)To build binaries and containers for all supported architectures:
goreleaser release --clean --snapshot --skip publishTo build just the binaries for the native architecture:
goreleaser build --clean --snapshot --single-targetCheck the dist/ directory for the built binaries, which will include the embedded metadata.
Both binaries can be built with:
make
There are also Make targets for each separate binary:
make coredhcp
make coredns
These will put coredhcp and coredns binaries in the repository root which can be used for building a container in the next step.
Verify that CoreDHCP contains the coresmd and bootloop plugins:
$ ./coredhcp --plugins | grep -E 'coresmd|bootloop'
bootloop
coresmd
...and that CoreDNS contains the coresmd plugin:
$ ./coredns --plugins | grep coresmd
coresmd
To build a container that contains both CoreDHCP and CoreDNS, run:
make container
Note
The container runtime is Docker by default, but is configurable. For example, to use Podman:
make CONTAINER_PROG="$(which podman)" containerNote
Container tags are also configurable. Use make ... CONTAINER_TAG=$TAG container to change it.
The container contains dhcping that can be used as a health check for CoreDHCP.
To test the DHCP handshake, iproute2 tools can be used to create a virtual interface in a separate network namespace that can be used to perform DORA.
# Create the network namespace called "dhcptest"
sudo ip netns add dhcptest
# Create veth pair
# veth-srv: virtual interface in parent namespace ("server")
# veth-cli: virtual interface in child namespace ("client")
sudo ip link add veth-srv type veth peer name veth-cli
sudo ip link set veth-cli netns dhcptest
# Bring up both interfaces
sudo ip link set veth-srv up
sudo ip netns exec dhcptest ip link set lo up
sudo ip netns exec dhcptest ip link set veth-cli up
# Optionally set specific MAC address on test interface
sudo ip netns exec dhcptest ip link set dev veth-cli address de:ad:c0:de:ca:feEnsure CoreDHCP can listen on veth-srv. If restricting the interfaces CoreDHCP listens on, ensure there's a proper entry under listen, e.g:
server4:
listen:
- '%veth-srv'Then, monitor the interface for the DORA handshake:
sudo tcpdump -nnni veth-srv -vvv 'udp port 67 or udp port 68'Initiate the DHCP handshake from the namespaced virtual network interface:
# Using dhclient
sudo ip netns exec dhcptest dhclient -4 -v -d -i veth-cli
# Using BusyBox udhcpc
sudo ip netns exec dhcptest udhcpc -i veth-cli -f -vvMonitor the output of tcpdump for the DORA handshake.
Cleaning up:
# OPTIONAL: Kill processes inside child network namespace
sudo ip netns pids dhcptest | xargs -r sudo kill
# OPTIONAL: Release lease if dhclient was used
sudo ip netns exec dhcptest dhclient -4 -r veth-cli
# Delete namespace and veth-cli interface inside it
sudo ip netns del dhcptest
# Delete parent namespace veth if it still exists
sudo ip link del veth-srv
# Ensure namespace and interfaces are gone (output should be empty)
sudo ip netns list | grep dhcptest
ip link show | grep -e veth-srv -e veth-cliSee: https://github.com/OpenCHAMI/coresmd/tree/main/examples/coredns#testing
CoreDHCP requires a config file to run. See examples/coredhcp/coredhcp.yaml for an example with detailed comments on how to enable and configure coresmd and bootloop.
CoreDNS similarly has a Corefile to use. See examples/coredns/ for examples of Corefiles.
Before running CoreDHCP/CoreDNS, ensure the OpenCHAMI services (notably BSS and SMD) are configured and running. Their URLs should match what you configure in the CoreDHCP config file.
By default, coresmd includes a built-in TFTP server with iPXE binaries for 32-/64-bit x86/ARM (EFI) and legacy x86. If you use the bootloop plugin and set the iPXE boot script path to "default", it will serve a built-in reboot script to unknown nodes. Alternatively, you can point this to a custom TFTP path if different functionality is desired.
Once all prerequisites are set, you can run CoreDHCP or CoreDNS.
-
Podman CLI Use host networking and mount your config file (this example mounts in system certificate bundle):
podman run \ --rm \ --name=coresmd-coredhcp \ --hostname=coresmd-coredhcp \ --cap-add=NET_ADMIN,NET_RAW \ --volume=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:/root_ca/root_ca.crt:ro,Z \ --volume=/etc/openchami/configs/coredhcp.yaml:/etc/coredhcp/config.yaml:ro,Z \ --network=host \ ghcr.io/openchami/coresmd:latest
[!NOTE]
--cap-addmay or may not be needed on some distros. -
Podman Quadlet:
[Unit] Description=The CoreSMD CoreDHCP container [Container] ContainerName=coresmd-coredhcp HostName=coresmd-coredhcp Image=ghcr.io/openchami/coresmd:latest AddCapability=NET_ADMIN AddCapability=NET_RAW Volume=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:/root_ca/root_ca.crt:ro,Z Volume=/etc/openchami/configs/coredhcp.yaml:/etc/coredhcp/config.yaml:ro,Z Network=host PodmanArgs=--http-proxy=false [Service] Restart=always
-
Bare Metal Execute the locally built binary:
./coredhcp -conf /path/to/config.yaml
-
Podman CLI Use host networking and mount your config file (this example mounts in system certificate bundle):
podman run \ --rm \ --name=coresmd-coredns \ --hostname=coresmd-coredns \ --cap-add=NET_ADMIN,NET_RAW \ --volume=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:/root_ca/root_ca.crt:ro,Z \ --volume=/etc/openchami/configs/Corefile:/etc/coredhcp/Corefile:ro,Z \ --network=host \ ghcr.io/openchami/coresmd:latest \ /coredns
[!NOTE]
--cap-addmay or may not be needed on some distros. -
Podman Quadlet:
[Unit] Description=The CoreSMD CoreDNS container [Container] ContainerName=coresmd-coredns HostName=coresmd-coredhcp Image=ghcr.io/openchami/coredns:latest Exec=/coredns AddCapability=NET_ADMIN AddCapability=NET_RAW Volume=/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem:/root_ca/root_ca.crt:ro,Z Volume=/etc/openchami/configs/Corefile Network=host PodmanArgs=--http-proxy=false [Service] Restart=always
-
Bare Metal Execute the locally built binary:
./cored-conf /path/to/config.yaml