Showing posts with label dhcp. Show all posts
Showing posts with label dhcp. Show all posts

Saturday, March 8, 2025

Building a Service Container Image - Fedora Base - ISC DHCP

ISC DHCP Server - Fedora Base

The simplest way to create a new container image is to start with a base image from a well-known distribution. The base images include package management software. Populating a new image is a matter of installing the desired packages. The rest of the package creation is defining the process start command and any volumes or ports that the container instance will use.

Creating a Container Image with buildah

buildah is a container management tool produced by Red Hat as an alternative to Docker. It produces OCI compliant container images.

buildah operates differently from Docker. It composes a container image as a series of CLI steps that allow for very precise container image composition. While it can consume a Dockerfile, it can also be used to write a shell script that composes and optionally publishes the image.

The process below defines a container based on the fedora-minimal image. It installs the dhcp-server package and then cleans the yum cache. The next three lines define the volumes that provide the configuration and database from the OS and the network ports for the service. The final four configuration lines provide metadata for the image. The last line saves the new image to local storage.

The Base Image

All container images start with a base image.[1]. The new image consists of a layering of the base image and new layers created during the image build process.

Many linux distributions provide a couple of OS base images, usually one that looks like a conventional OS deployment and another minimal one that has been stripped down to basically a shell and package manager.

To start a new container image build, use the buildah from command. This command takes the identifier for a base image and returns a container identifier string that is used for the rest of the operations on the new container.

The container created here uses the fedora-minimal base image. It doesn’t specify the version so it will always use the latest.

# Create a new container image
CONTAINER=$(buildah from registry.fedoraproject.org/fedora-minimal)

When this command completes the CONTAINER environment variable contains the new image identifier.

Installing Software

With a distribution base image you can use the standard package management tools to install new software.

# Install the DHCP server package and then remove any cached files
buildah run $CONTAINER dnf install -y --nodocs --setopt install_weak_deps=False dhcp-server
buildah run $CONTAINER dnf clean all -y

The package management software typically caches metadata and the package file so it’s common to clean the cache after all software is installed. Using docker, each line creates a new image layer. This means you have to create a single entry that installs all the software then cleans the cache, leading to long run-on install commands. With buildah you can run those two commands separately or any number of commands and create only a single layer.

Opening Holes

A software container service that doesn’t communicate with the outside world isn’t very useful. Both files and network communication can be allowed to pass through a container boundary.

Configuration Files

The container process is configured by mapping files or directories from the host into the container file system. For the DHCP daemon responding to IPv4 requests there is a single configuration file and a single database file.

# add a volume to include the configuration file
# Leave the files in the default locations
buildah config --volume /etc/dhcp/dhcpd.conf $CONTAINER
buildah config --volume /var/lib/dhcpd/dhcp.leases $CONTAINER

It is possible to create volumes on whole directories. If this container is extended to serve IPv6 it may be preferred to create these volumes on the directories to avoid repetitive volume declarations.

Network Ports

Containers often need to communicate with other processes or servers. If the process listens on a particular network port, that must be declared for the container.

# open ports for listening
buildah config --port 68/udp --port 69/udp ${CONTAINER}

The DHCP service listens on two UDP ports, for the bootp and dhcp services.

Process Invocation

Every container image defines a single process and its runtime environment. The last thing to do once the context has been defined is to invoke the binary that runs the process.

# Define the startup command
buildah config --cmd "/usr/sbin/dhcpd -d --no-pid" $CONTAINER

The placement of the files in the volumes defined above allow the process to run using the default locations for the configuration and for the lease database. If it was useful to move those files inside the container, the invocation can be modified.

Metadata

A container image can contain information about the contents. The author and builder metadata have pre-defined labels, but it is also possible to apply arbitrary key-value pairs to provide additional information.

# Identify the container source
buildah config --author "Mark Lamourine <markllama@gmail.com>" $CONTAINER
buildah config --created-by "Mark Lamourine <markllama@gmail.com>" $CONTAINER
# Indicate the software and the license terms
buildah config --annotation description="ISC DHCPD 4.4.3" $CONTAINER
buildah config --annotation license="MPL-2.0" $CONTAINER

The annotations might also include the base image distro information and the git repository for the build script.

Commit and Tag

When the container image has been properly defined it must be committed and tagged for release.

# Save the container to an image
buildah commit --squash $CONTAINER dhcpd-fedora

The commit step closes the image and applies a local label so that the image can be examined and tested. The --squash directive causes all of the steps to be committed as a single layer.

Once the developer is satisfied, they can apply a global identification tag and push the image to the appropriate repository. This step is not included in the build script to allow for that testing.

# To tag and publish the image
buildah tag localhost/dhcpd-fedora quay.io/markllama/dhcpd-fedora
buildah push quay.io/markllama/dhcpd-fedora

Public repositories require authentication before allowing a user to push a new image, so this must be done once for a build/publish cycle

buildah login quay.io --username markllama
Password: ********

TL;DR - The Container Build Script

The script below is made up of the lines detailed above.

fedora-dhcpd.sh
#!/bin/bash
#
# Create a new container image
CONTAINER=$(buildah from registry.fedoraproject.org/fedora-minimal)

# Install the DHCP server package and then remove any cached files
buildah run $CONTAINER dnf install -y --nodocs --setopt install_weak_deps=False dhcp-server
buildah run $CONTAINER dnf clean all -y

# add a volume to include the configuration file
# Leave the files in the default locations
buildah config --volume /etc/dhcp/dhcpd.conf $CONTAINER
buildah config --volume /var/lib/dhcpd/dhcp.leases $CONTAINER

# open ports for listening
buildah config --port 68/udp --port 69/udp ${CONTAINER}

# Define the startup command
buildah config --cmd "/usr/sbin/dhcpd -d --no-pid" $CONTAINER

buildah config --author "Mark Lamourine <markllama@gmail.com>" $CONTAINER
buildah config --created-by "Mark Lamourine <markllama@gmail.com>" $CONTAINER

buildah config --annotation description="ISC DHCPD 4.4.3" $CONTAINER
buildah config --annotation license="MPL-2.0" $CONTAINER

# Save the container to an image
buildah commit --squash $CONTAINER dhcpd-fedora

Summary

When combined with the dhcpd.container systemd container unit file, this container provides the same DHCP service that you would get by running the service from a package installed on the host OS. You can update the DHCP server and revert it by modifying the dhcpd.container file and specifying a previous release tag.[2] You can update to the current version merely by restarting the service or rebooting the system.

The base image is 146MB and is made up of 100 packages and over 30,000 files. The installation request for the single dhcp-server package results in the installation of 10 package dependencies making up another 27MB.

Altogether the new service image is over 170MB on aarch64. It contains all of the files of the base image plus all of the files of the dhcp-server package and dependencies. But it only runs one binary. The rest of the files in the image are unneeded for operation.

There is another way: Create a minimal container image from scratch.

References


1. The base image can be scratch, an empty image
2. I need to see if Environment and EnvironmentFile values can apply to the Image option.

Friday, January 10, 2025

The Case for CoreOS - Network Infrastructure on an Immutable OS

The Lifetime of Silent Services

For small and medium sized organizations, a local network requires the creation of and management of local network services such as DNS, NTP, DHCP, monitoring and user access controls.  These are the ante needed to get in the game but when they work properly they become invisible. This is good, but it means they can be neglected from the standpoint of management and maintenance. As long as they work it's easy to ignore them until they do break. There is a tendency to treat maintenance is a risk rather than a benefit, the fear of service interruption and downtime leading to neglect and a sense that these services are somehow fragile and precious.

For these silent services, the neglect usually manifests when the admins discover that the OS has gone end-of-life or a bug is discovered in the current version of a service or there are 200 CVEs to apply because the last reboot was 700 days ago. The problem is that accumulated updates required and unfamiliarity with the services and the maintenance history makes admins gun-shy of updates. Time only makes the fear and the debt worse.

What are you afraid of, Really?

The modern alternate is the cliche "Fail Fast", which, when thrown about without comprehension, is correctly scorned.  I prefer to say "Find the scariest thing you have to do, and do it repeatedly until it stops being scary. Then find the next scariest thing.".

The real fear and risk is of downtime without a recovery plan.  In a corporate environment the tendency of management is to CYA by avoiding any downtime by avoiding any change. While this can provide the illusion of stability, it treats the infrastructure as a static monolith. It ignores the facts that failures and updates are inevitable and sets the operations teams up for failure. It restricts their ability to practice the very update and mitigation processes that would allow them to create a robust reliable service.

The real solution is to create a system where any change can be rolled back quickly, reliably and completely.  Fedora CoreOS provides that.

Git for Filesystems?

Fedora CoreOS is a distribution of Fedora Linux that is created specifically to run software containers.  Red Hat promotes it for cloud use and only supports it as a base for OpenShift.  It is a minimal distribution with no GUI only a simple installer that writes the initial state to a bootable storage device and a simple configuration file that is applied on first boot. This by itself is unremarkable. The feature that makes CoreOS significant is that the file and package systems are based on rpm-ostree. This is an integrated file and package management system. It presents to users as an XFS filesystem, but it is mounted read-only. The filesystem is immutable. To install packages you must use the rpm-ostree command to layer the package into a new image version and then reboot to the new image. Installing application packages is discouraged in favor of running services in containers.

Did you get that? The filesystem is read only. To see updated packages you have to reboot. Wait, there's more.

The Turtle or the Frog?

Most distributions provide updates through online package repositories. Admins must periodically poll the repository, pull down any new packages, and then overlay them into the running system. At that point it becomes extremely difficult to reliably roll back. If anything fails, the only recourse is to recover the system from backups, which is understandably an extreme and time-consuming process.  This leads to a "slow and steady" approach to updates. Updates are applied to a few test systems. If no problems are discovered they are rolled forward to a set of staging systems.  Finally the updates are deployed to production.

This is an expensive, time consuming system, suited only to large organizations with the resources to implement them. It's also error prone, as it is often difficult to adequately simulate the production operating conditions in a small test environment.  More commonly in smaller organizations, updates are shunted to backlog work and neglected in favor of feature requests or helpdesk issues until some outside event brings the problem to the attention of management, when it becomes an emergency.
To compound the problems, it is common to run package updates without rebooting the system. This can result in failures that don't appear until long after the actual change is applied. All together this makes IT management very averse to regular updates and reboots because they see these as introducing problems and risking downtime with long recovery periods.

Until recently (well ages in Internet Time) this "frog in the pot" approach was really the only option. The fact that it was impossible to reliably roll back changes rightly made management and operations averse to any change to a system that was "working". 

Double-Buffered Operating System

CoreOS updates are atomic. That is, updates are published as a unit.  The stable stream is updated approximately every two weeks. There are also test and "next" streams that update more often but aren't meant for regular use.  CoreOS runs a service called Zincati. This service polls the release streams for new images and will apply them and reboot when needed. Zincati can be tuned to create staged roll-outs, applying updates first to a set of canary systems before moving on to more critical systems. It can also be tuned to restrict reboots to specific days of the week and times of day.

By conventional standards, read-only systems that update automatically and require reboot every two weeks provides the opposite of stability and reliability. But the risks posed when this is implemented on a conventional Linux distribution are mitigated when presented using rpm-ostree, zincati and software containers.  The benefits of atomic rollback and application decoupling mean that it is possible to keep systems up to date and to respond instantly to any update-induced problems. In essence the operating system is double-buffered and the current system is preserved perfectly across updates. You don't have to worry about losing the working configuration because it's still there.

For The Best Services, Don't Install Any

On CoreOS you're discouraged from installing application or service software on the system.  CoreOS is designed to run software containers. The only major service component integrated into the OS is podman, while all of the network services run on Linux as systemd services.

In 2021, a project called Quadlets was created to allow containers to be managed as first-class services under systemd. In 2022 quadlets were merged into the systemd project and as of 2024 they are available on any systemd based Linux. This means that your system services no longer are tightly coupled to the OS updates.  They don't even need to be based on the same OS distribution.

Using Quadlets, deploying a network service is a matter of defining a systemd container spec, providing the service configuration files and enabling and starting the service. No service software needs to be installed or updated ever.  Updating the service software is a matter of updating the container image path and tag and restarting the systemd service.  Reverting is just as simple. It becomes possible to basically ignore the OS when updating system services and vice-versa.  The loose coupling means that changes to one are very unlikely to affect the other and that any change can be trivially and reliably reverted without affecting the other components.

Do it again! Do it again!

The simplicity and minimalism of using CoreOS with software containers enables one last element for providing stable reliable network services. CoreOS can be installed with a simple DHCP/PXE boot and, once installed, it can be configured with a small set of Ansible scripts. These aren't remarkable by themselves but the simplicity of and compartmentalization that the immutable OS are somewhat novel in the on-premise hardware environment.  These are usually thought of as features of cloud-based services, but are perfectly applicable for small and medium organizations with limited resources.

As a matter of practice I tend not to say I can do something until I can do it 100 times with the push of a single button. With some simple automation the infrastructure can be restored in a matter of minutes on the old hardware or new.  These services tend to be small and light-weight, so they can run on inexpensive redundant hardware.

So You Say, But How?

Well, I plan to show you.  This first post is a long pontification on some thoughts I've had over the last couple of years. I've put it into practice for my home network and at one employer.  It falls under a larger theme of adapting cloud networking practices for on-premise network services.  After all, Red Hat now only supports their CoreOS stream as the base for OpenShift, Red Hat's extended Kubernetes offering. Red Hat recommends the very practices I'm going to detail to maintain the underpinnings of their enterprise distributed application service. I suspect that part of the reason they don't support it for general use is that serious adoption would undercut their revenue stream from RHEL, and I can tell you from personal experience that matters to them a lot.

This isn't a perfect strategy for all purposes either.  Unless your application is extremely simple and has already been designed and implemented for containers it doesn't make sense to shoehorn it in.  Large distributed applications are better supported on a proper Kubernetes or OpenShift deployment, whether on-premise or on a cloud service. Heavy-weight monolithic services (I'm looking at you JBoss/Tomcat apps) aren't well suited to containers, despite the trend to push them in.

In following posts I mean to walk through the deployment of Fedora CoreOS, preparation for automated configuration management and the deployment of service containers. I'm not actually sure where this will end but I mean to see just how far I can push it.  Come along if it seems like your kind of fun.

Resources

  • Fedora Linux - An extremely popular and well managed Linux distribution
  • Fedora CoreOS - A spin of Fedora that is designed to run software containers
  • libostree - A checkpointed filesystem that allows atomic rollback of file changes
  • rpm-ostree - An extension of libostree that integrates RPM package management
  • butane - YAML schema to define OS configurations for CoreOS
  • ignition - JSON schema to define OS configurations for CoreOS
  • zincati - A service to control and tune updates from CoreOS image streams
  • Quadlets - Software containers as systemd services
  • Ansible - System configuration language and toolset
  • OpenShift - Red Hat's enterprise extended version of Kubernetes
  • Kubernetes - A computing cluster system for running applications in software containers



Monday, May 19, 2014

Robust and Flexable DHCP and provisioning: An LDAP backed DHCP service.

In the last post I created an empty LDAP database ready to accept content. In this one I mean to add a DHCP service configuration for a single subnet and a test host entry.

This section is a long argument describing the advantages of using a backing database for DHCP. You can skip it if you're already convinced.

Why use a database?


There are significant reasons to use a proper database (yes, LDAP is a database) for DHCP management.

  • Update without restart
  • Avoid ad hoc file parsing or generation
  • Reduce configuration sites

The use of a flat file for configuration and data, the use of an inaccessible in-memory database and the network limitations of the DHCP protocol all pose problems for all but the smallest networks.  Backing the DHCP services with a database can address all three.

Testing: Emit and Collect Test DHCP Queries - dhtest


It turns out that there aren't many tools for testing DHCP responses. I found several but they were only in source code. The one I decided to use is called dhtest and it's available from Github:
https://github.com/saravana815/dhtest

It builds cleanly on Fedora 19 and 20.
git clone https://github.com/saravana815/dhtest
Cloning into 'dhtest'...
remote: Reusing existing pack: 53, done.
remote: Total 53 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (53/53), done.
cd dhtest
make
gcc    -c -o dhtest.o dhtest.c
gcc    -c -o functions.o functions.c
gcc dhtest.o functions.o -o dhtest

When it runs successfully this is what it looks like

sudo ./dhtest --mac 0a:00:00:00:00:01 \
  --interface p2p1 --server 10.0.2.15 --verbose
DHCP discover sent  - Client MAC : 0a:00:00:00:00:01
DHCP offer received  - Offered IP : 10.0.2.16

DHCP offer details
----------------------------------------------------------
DHCP offered IP from server - 10.0.2.16
Next server IP(Probably TFTP server) - 10.0.2.4
Subnet mask - 255.255.255.0
Router/gateway - 10.0.2.2
DNS server - 10.0.2.3
Lease time - 1 Days 0 Hours 0 Minutes
DHCP server  - 10.0.2.2
----------------------------------------------------------

DHCP request sent  - Client MAC : 0a:00:00:00:00:01
DHCP ack received  - Acquired IP: 10.0.2.16

DHCP ack details
----------------------------------------------------------
DHCP offered IP from server - 10.0.2.16
Next server IP(Probably TFTP server) - 10.0.2.4
Subnet mask - 255.255.255.0
Router/gateway - 10.0.2.2
DNS server - 10.0.2.3
Lease time - 1 Days 0 Hours 0 Minutes
DHCP server  - 10.0.2.2
----------------------------------------------------------

Procedure


Finally I get to the actual process of creating the DHCP service.  First the ingredients and a summary of the process. Then the details.

Ingredients


Before starting there are a set of parameters that should be defined.  The DHCP server will need to gain access to the LDAP service and the DHCP server configuration in the LDAP database must reflect the network on which the DHCP server resides.  I also add one dummy test host that I can use for validation.

LDAP Server
LDAP Server Hostnameldap.example.com
Database DNdc=example,dc=com
Admin Username (DN)dc=Manager,dc=example,dc=com
Admin Passwordchangeme

Subnet  Specification
Base Address10.0.2.0
Netmask/24
Gateway10.0.2.2
DNS Servers10.0.2.3
NTP Servers10.0.2.3

Host Entry
MAC Address0a:00:00:00:00:01
IP Address10.0.2.16

Recipe


Running DHCP with LDAP (conceptually) requires two different servers. You can run them both on the same host if you want. Adjust your IP addresses and hostnames to your environment.
  1. On the LDAP server
    1. Prepare the LDAP database for DHCP configuration
      1. Convert the DHCP schema file to LDIF
      2. Import the DHCP schema (as LDIF) into the cn=config database
    2. Convert the DHCP config to LDIF and load it into the database
      1. dhcpServer
      2. dhcpService
      3. dhcpSubnet
      4. dhcpHost
  2. On the DHCP server
    1. Prepare logging
    2. Verify LDAP connectivity
    3. Configure DHCP service
    4. Start DHCP service
    5. Test DHCP service

LDAP Server Host

Convert DHCP Schema to LDIF

The DHCP schema for LDAP isn't part of the standard OpenLDAP server packages. On Fedora it's part of the DHCP package. On Debian it's part of a special package which includes the DHCP server with LDAP integration: isc-dhcp-ldap. Because the LDAP schema file is provided as part of the DHCP server packaging, it must be transferred to the LDAP server to be loaded into the database schema set.

Even then the schema is provided in the older LDAP schema format. I need it in LDIF format so that I can load it like the others. Fortunately it's possible to load the older schema into memory and then write them out as LDIF using slapcat. The trick is to convince it to use a special alternate configuration file which just imports the old form schema and then dump the config as LDIF. There are a couple of tweaks to make on the resulting LDIF. The schema object is created with an array index of zero (0). That has to be removed. Slapcat also adds a CRC, and some reference and time stamp information that won't apply to the schema definition when it is loaded into a new database.

The section of code below will produce a file named dhcp.ldif. It takes the dhcp.schema file as input. It uses a temporary file for the LDAP configuration which only loads the DHCP schema and a temporary directory to contain the resulting LDIF config tree which slapcat produces as a matter of course.

#!/bin/sh
# Create the required tmp file/directory
mkdir slapd.d
echo 'include /etc/openldap/schema/dhcp.schema' > slapd.conf
# load the schema and then dump it in LDIF format
slapcat -f slapd.conf -F slapd.d -n0 -l dhcp.ldif \
  -H ldap:///cn={0}dhcp,cn=schema,cn=config
# remove the CRC, array index and timestamp/UUID entries
sed -i -e '/CRC32/d ; s/{0}dhcp/dhcp/ ; /structuralObjectClass/,$d' \
  dhcp.ldif
# remove the tmp file/directory
rm -rf slapd.d
rm slapd.conf
sudo cp dhcp.ldif /etc/openldap/schema/dhcp.ldif

(remember, this runs on the LDAP server host)

Import DHCP schema into configuration database


Once I have a the DHCP schema in LDIF format I can load it the same way I loaded the stock schema. This will be the last command which must run as root on the LDAP server and uses local authentication.

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// /etc/openldap/schema/dhcp.ldif

From this point on I'll be adding things not to the config database but to the hdb database using the RootDN and RootPW account.

Load the DHCP configuration into the LDAP server


The DHCP service configuration (as expressed in LDIF) requires three objects to describe a minimal working DHCP service:

  1. dhcpServer - The host on which the DHCP service will run
  2. dhcpService - The global settings which control the behavior of the DHCP service
  3. dhcpSubnet - A description of a subnet to which the DHCP server is connected
Making changes to any of these objects will require a restart of the affected DHCP daemon processes.

DHCP Server


The LDAP dhcpServer object is the hook to which the dhcpd process will attach when it starts up. This object contains the DN of the top of the DHCP service configuration.

LDAP object classes are additive. That is, a single entry in the database will commonly have more than one objectClass attribute. The objectClass attributes declare the set of attributes which the object can have and
there is no limit (other than conflict) to the combinations.

I believe that the dhcpServer objectClass can be combined with the NIS host class so that information about particular hosts can be unified under a single object.

#
# Define the DHCP host entry which will be used by the DHCP service on startup
# This is the configuration entry hook
#
dn: cn=dhcp-host,dc=example,dc=com
cn: dhcp-host
objectClass: top
objectClass: dhcpServer
dhcpServiceDN: cn=dhcp-service,dc=example,dc=com


DHCP Service


The dhcpService object is the root of the DHCP daemon configuration information. All of the objects which define a DHCP service configuration will be children of this object. That is, the DN of the dhcpService object will be the suffix for the rest of the objects that define the configuration.

There are two types of attribute which all objects in the DHCP configuration can have. These are the dhcpStatement and dhcpOption attributes. These correspond to normal statement lines and option lines in the traditional dhcpd.conf file.

The dhcpService attributes define the deamon behavior and any global options which would apply to all query responses.

# The root object of the DHCP service
# All elements of the DHCP configuration will use this DN for a suffix.
# 
dn: cn=dhcp-service,dc=example,dc=com
cn: dhcp-service
objectClass: top
objectClass: dhcpService
objectClass: dhcpOptions
dhcpPrimaryDN: cn=dhcp-host, dc=example,dc=com
dhcpStatements: authoritative
dhcpStatements: ddns-update-style none
dhcpStatements: max-lease-time 43200
dhcpStatements: default-lease-time 3600
dhcpStatements: allow booting
dhcpStatements: allow bootp
dhcpOption: domain-name "example.com"
dhcpOption: domain-name-servers 10.0.2.3


DHCP Subnet


The DHCP service needs a subnet definition so that it knows what interface(s) to bind to. A DHCP server listens for discovery requests. There's no point in listening if there are no networks to listen on, so the daemon will exit.

# DHCP Subnet object
# 
dn: cn=10.0.2.0, cn=dhcp-service,dc=example,dc=com
cn: 10.0.2.0
objectClass: top
objectClass: dhcpSubnet
dhcpNetMask: 24
dhcpOption: routers 10.0.2.2


Test DHCP Lease Reservation



# A Test Host Lease Reservation
# The definition of a host: name, MAC, IP address
# Additional options can control PXE boot and OS installation
#
dn: cn=testhost, cn=dhcp-service,dc=example,dc=com
cn: testhost
objectClass: top
objectClass: dhcpHost
objectClass: dhcpOptions
dhcpHWAddress: ethernet 0a:00:00:00:00:01
dhcpStatements: fixed-address 10.0.2.16
dhcpOption: host-name "testhost"


DHCP Server Host

These operations configure the DHCP server host and the dhcp daemon.

Prepare Logging (Optional)


I like to be able to view the logs for critical services separately from the rest of the system logs. This can make it easier. For this I'll add a config file for rsyslog which filters the dhcpd log entries to a file of their own. This doesn''t change the behavior at all, it just makes viewing the logs simpler.
First, create an empty log file (rsyslog doesn't like to create files that don't exist)
sudo touch /var/log/dhcpd.log

Then create the rsyslog config entry in /etc/rsyslog.d

cat <<EOF >/etc/rsyslog.d/dhcpd.conf
if $programname == "dhcpd" then /var/log/dhcpd.log
EOF

Finally, restart the rsyslog daemon

sudo systemctl restart rsyslog

Verify LDAP access


Before trying to connect the DHCP server to the LDAP service, I need to verify that the DHCP host can make the required connection and retrieve the dhcpServer entry which is the anchor for the configuration data.

ldapsearch -H ldap://ldap.example.com \
    -x -w changeme \
    -D cn=Manager,dc=example,dc=com \
    -b dc=example,dc=com \
    objectClass=dhcpServer

Set the DHCP server configuration - use LDAP server

When the dhcpd is configured for an LDAP database, the configuration file is a lot smaller than is typical.  It merely identifies where to find the configuration.  It can also indicate whether the daemon should read the configuration once and load it into memory, or resolve each query with a check of the database. Finally, it can write a copy of the configuration in the traditional format for verification.

# DHCP Host Location
ldap-server "ldap.example.com" ;
ldap-port 389 ;

# A user with read/write access to the database
ldap-username "cn=Manager,dc=example,dc=com" ;
ldap-password "changeme" ;

# Identify the root object of the config
ldap-base-dn "dc=example,dc=com" ;
ldap-dhcp-server-cn "dhcp-host" ;

# All queries check the database
ldap-method dynamic ;

# Write the DHCP config for validation
#   An empty file must exist before starting the daemon
#   And it must be writable by the dhcpd user
#ldap-debug-file "/var/log/dhcp-ldap-startup.log" ;


Start the DHCP server


sudo systemctl start dhcpd

Verify that the daemon has started and is serving queries for the subnet

May 16 20:06:53 fedora-20-x64 dhcpd: Internet Systems Consortium DHCP Server 4.2
.6
May 16 20:06:53 fedora-20-x64 dhcpd: Copyright 2004-2014 Internet Systems Consor
tium.
May 16 20:06:53 fedora-20-x64 dhcpd: All rights reserved.
May 16 20:06:53 fedora-20-x64 dhcpd: For info, please visit https://www.isc.org/
software/dhcp/
May 16 20:06:53 fedora-20-x64 dhcpd: Wrote 0 leases to leases file.
May 16 20:06:53 fedora-20-x64 dhcpd: Listening on LPF/p2p1/08:00:27:35:3b:b0/10.
0.2.0/24
May 16 20:06:53 fedora-20-x64 dhcpd: Sending on   LPF/p2p1/08:00:27:35:3b:b0/10.
0.2.0/24
May 16 20:06:53 fedora-20-x64 dhcpd: Sending on   Socket/fallback/fallback-net

Verify Operation


sudo dhtest --verbose --mac 0a:00:00:00:00:01 --interface eth0 --server 10.0.2.15
...
May 16 20:11:49 fedora-20-x64 dhcpd: DHCPDISCOVER from 0a:00:00:00:00:01 via eth-
May 16 20:11:49 fedora-20-x64 dhcpd: DHCPOFFER on 10.0.2.16 to 0a:00:00:00:00:01
 via eth0
May 16 20:11:49 fedora-20-x64 dhcpd: DHCPREQUEST for 10.0.2.16 (10.0.2.2) from 0
a:00:00:00:00:01 via eth0
May 16 20:11:49 fedora-20-x64 dhcpd: DHCPACK on 10.0.2.16 to 0a:00:00:00:00:01 v
ia eth0

Additional Work


This is a very simple example. There is considerable work that is still needed for a production system.
  1. Security - LDAP over SSL
  2. Security - Add LDAP users for access control
  3. Security - SASL or Kerberos authentication
  4. Security - Database access controls (user ACLs)
  5. HA - LDAP database replication

References

  • DHCP LDAP Patch
    https://github.com/dcantrell/ldap-for-dhcp/wiki
  • An Early example:
    https://skalyanasundaram.wordpress.com/dhcp/dhcp-with-ldap-support/
  • dhtest - DHCP emitter/responder
    https://github.com/saravana815/dhtest