Initial commit: DHCP whitelist service for direct link connections

Features:
- Docker-based DHCP server with MAC address whitelisting
- Binds to specific ethernet interface only
- NO DNS/gateway advertised (direct link only, not a router)
- Configurable network parameters (subnet, DHCP range, lease times)
- Systemd service integration for Arch/Manjaro
- Test environment with isolated network (172.20.0.0/24)
- Auto-configuration script to detect network settings
- Complete Makefile with management targets

Security:
- Only responds to whitelisted MAC addresses
- deny unknown-clients configuration
- Runs in Docker container for isolation

Configuration:
- Copy .example files to create your config
- interface.conf: Network interface to bind to
- whitelist.conf: Allowed MAC addresses
- network.conf: Network parameters (optional)
This commit is contained in:
Marcus Penate 2025-08-27 18:55:32 -04:00
commit 141ac1c9dd
19 changed files with 942 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
*.swp
*.swo
.DS_Store
.env
docker-compose.override.yml
*.log
*.pid
/data/
/tmp/
__pycache__/
*.pyc
.vscode/
.idea/
# User configuration files (keep examples only)
config/interface.conf
config/whitelist.conf
config/network.conf

18
Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM alpine:3.19
RUN apk add --no-cache \
dhcp \
bash \
iproute2
RUN mkdir -p /var/lib/dhcp /etc/dhcp /run/dhcp
COPY dhcp/entrypoint.sh /entrypoint.sh
COPY dhcp/dhcpd.conf.template /etc/dhcp/dhcpd.conf.template
RUN chmod +x /entrypoint.sh && \
touch /var/lib/dhcp/dhcpd.leases
EXPOSE 67/udp
ENTRYPOINT ["/entrypoint.sh"]

78
Makefile Normal file
View File

@ -0,0 +1,78 @@
.PHONY: up down install service_up service_down test autoconfig clean help
# Default target
help:
@echo "Available targets:"
@echo " up - Start DHCP server in userland mode"
@echo " down - Stop DHCP server"
@echo " install - Install as systemd service (requires sudo)"
@echo " service_up - Enable and start systemd service"
@echo " service_down - Disable and stop systemd service"
@echo " test - Run test environment with virtual network (172.20.0.0/24)"
@echo " test-clean - Clean up test environment"
@echo " autoconfig - Auto-generate configuration from current network"
@echo " clean - Remove all containers and generated files"
# Start DHCP server in userland mode
up:
@echo "Building DHCP server image..."
@docker-compose build
@echo "Starting DHCP server..."
@docker-compose up -d
@echo "DHCP server started. Check logs with: docker-compose logs -f"
# Stop DHCP server
down:
@echo "Stopping DHCP server..."
@docker-compose down
@echo "DHCP server stopped."
# Install as systemd service (requires sudo)
install:
@echo "Installing systemd service..."
@bash scripts/install-service.sh
@echo "Service installed. Use 'make service_up' to start."
# Enable and start systemd service
service_up:
@echo "Enabling and starting systemd service..."
@systemctl --user enable dhcp-whitelist.service 2>/dev/null || sudo systemctl enable dhcp-whitelist.service
@systemctl --user start dhcp-whitelist.service 2>/dev/null || sudo systemctl start dhcp-whitelist.service
@echo "Service started. Check status with: systemctl status dhcp-whitelist"
# Disable and stop systemd service
service_down:
@echo "Stopping and disabling systemd service..."
@systemctl --user stop dhcp-whitelist.service 2>/dev/null || sudo systemctl stop dhcp-whitelist.service
@systemctl --user disable dhcp-whitelist.service 2>/dev/null || sudo systemctl disable dhcp-whitelist.service
@echo "Service stopped."
# Run test environment
test:
@echo "Building test environment..."
@docker-compose -f docker-compose.test.yml build
@echo "Running tests..."
@docker-compose -f docker-compose.test.yml up --abort-on-container-exit
@$(MAKE) test-clean
@echo "Tests completed."
# Clean up test environment (always runs, even on test failure)
test-clean:
@echo "Cleaning up test environment..."
@docker-compose -f docker-compose.test.yml down -v --remove-orphans 2>/dev/null || true
@docker network rm service-dhcp-direct-link-only_test-net 2>/dev/null || true
@echo "Test cleanup complete."
# Auto-generate configuration
autoconfig:
@echo "Auto-generating configuration..."
@bash scripts/autoconfig.sh
@echo "Configuration generated. Review config/ directory before starting."
# Clean up
clean:
@echo "Cleaning up..."
@docker-compose down 2>/dev/null || true
@$(MAKE) test-clean
@rm -f config/*.tmp
@echo "Cleanup complete."

136
README.md Normal file
View File

@ -0,0 +1,136 @@
# DHCP Direct Link Only Service
A Docker-based DHCP server that only serves IP addresses to whitelisted MAC addresses on a specific ethernet interface. Designed for secure, controlled network environments where only authorized devices should receive DHCP leases.
## Features
- **MAC Address Whitelisting**: Only responds to DHCP requests from pre-authorized MAC addresses
- **Interface Binding**: Binds to a specific ethernet interface only
- **Docker-Based**: Runs in an isolated container environment
- **Systemd Integration**: Can be installed as a system service on Arch/Manjaro
- **Auto-Configuration**: Automatically detects network settings and connected devices
- **Testing Environment**: Includes isolated test environment with virtual networks
## Requirements
- Docker
- Docker Compose
- Make
- systemd (for service installation)
- sudo (for service installation)
## Quick Start
1. Auto-configure for current network:
```bash
make autoconfig
```
2. Start the DHCP server:
```bash
make up
```
3. Stop the server:
```bash
make down
```
## Configuration
### Manual Configuration
Edit the following files in the `config/` directory:
- `interface.conf`: Specify the ethernet interface to bind to
- `whitelist.conf`: List MAC addresses (one per line) that should be served
### Auto Configuration
Run `make autoconfig` to automatically:
- Detect your ethernet interface
- Find connected devices on the network
- Generate configuration files
## Make Targets
| Target | Description | Requires sudo |
|--------|-------------|---------------|
| `up` | Start DHCP server in userland mode | No |
| `down` | Stop DHCP server | No |
| `install` | Install as systemd service | Yes |
| `service_up` | Enable and start systemd service | No |
| `service_down` | Disable and stop systemd service | No |
| `test` | Run isolated test environment | No |
| `autoconfig` | Auto-generate configuration | No |
| `clean` | Remove generated files | No |
## Installation as System Service
To install and run as a systemd service:
```bash
# Install the service (requires sudo)
sudo make install
# Start the service
make service_up
# Check service status
systemctl status dhcp-whitelist
# Stop the service
make service_down
```
The service configuration will be stored in `/etc/dhcp-whitelist/` and will persist across system reboots.
## Testing
Run the test environment with virtual networks:
```bash
make test
```
This creates an isolated Docker network with:
- A DHCP server with test whitelist
- Test clients with different MAC addresses
- Validation of whitelist enforcement
## Network Configuration
Default DHCP settings:
- Subnet: 192.168.75.0/24
- Range: 192.168.75.10 - 192.168.75.100
- Gateway: 192.168.75.1
- DNS: 8.8.8.8, 8.8.4.4
- Lease time: 12 hours
## Troubleshooting
### DHCP server not responding
- Check that the interface in `config/interface.conf` is correct
- Verify the MAC address is in `config/whitelist.conf`
- Check Docker logs: `docker-compose logs dhcp-server`
### Permission denied errors
- Service installation requires sudo: `sudo make install`
- Ensure Docker daemon is running
- Check that your user is in the docker group
### Service won't start
- Check systemd logs: `journalctl -u dhcp-whitelist -f`
- Verify Docker and docker-compose are installed
- Check configuration files in `/etc/dhcp-whitelist/`
## Security Considerations
- This server uses MAC address filtering as the primary security mechanism
- MAC addresses can be spoofed; use additional security measures in production
- The server runs with host networking to access the physical interface
- Consider firewall rules to restrict DHCP traffic further
## License
MIT

57
claude.md Normal file
View File

@ -0,0 +1,57 @@
# DHCP Direct Link Only Service - Development Notes
## Project Overview
This project creates a DHCP server that only responds to whitelisted MAC addresses on a specific ethernet interface. It's designed for direct link connections where only authorized devices should receive IP addresses, without any routing or DNS services.
## Configuration Features
- Binds to specific ethernet interface (configurable)
- MAC address whitelisting (configurable)
- NO DNS servers advertised (direct link only)
- NO gateway/router advertised (not a NAT setup)
- Configurable network parameters (subnet, range, lease times)
## Architecture Decisions
### Why Docker?
- Isolation from host system
- Easy deployment and management
- Consistent environment across different systems
- Simple cleanup and removal
### Why ISC DHCP Server?
- Mature, stable DHCP implementation
- Extensive configuration options
- Good documentation
- Supports MAC address filtering natively
### Network Mode: Host
The container uses host network mode because:
- DHCP requires direct access to the physical network interface
- DHCP uses raw sockets that don't work well with Docker's bridge networking
- We need to bind to a specific physical interface
## Implementation Details
### Whitelist Implementation
The whitelist is implemented using ISC DHCP's host declarations with a deny unknown-clients directive. This ensures only explicitly defined MAC addresses receive leases.
### Configuration Management
- Local configs in `config/` for development
- System configs in `/etc/dhcp-whitelist/` for production service
- Auto-config script detects network settings automatically
### Testing Strategy
- Isolated Docker network for testing
- Separate test client containers
- Tests both allowed and denied MAC scenarios
## Known Limitations
1. MAC addresses can be spoofed - this is not a security solution by itself
2. Requires host network mode which reduces container isolation
3. Only one instance can run per interface
## Future Enhancements
- Web UI for managing whitelist
- Logging and monitoring
- Multiple interface support
- Integration with network authentication systems

View File

@ -0,0 +1,3 @@
# Example: Ethernet interface name
# Find your interface with: ip link show
eth0

View File

@ -0,0 +1,24 @@
# Network configuration for DHCP server
# These values override automatic detection
# Network subnet (leave empty for auto-detection from interface)
# Example: SUBNET=192.168.1.0
SUBNET=
# Netmask in dotted notation (leave empty for auto-detection)
# Example: NETMASK=255.255.255.0
NETMASK=
# DHCP range start offset from network base (default: 10)
# For example, if network is 192.168.1.0, start will be 192.168.1.10
RANGE_START_OFFSET=10
# DHCP range end offset from network base (default: 100)
# For example, if network is 192.168.1.0, end will be 192.168.1.100
RANGE_END_OFFSET=100
# Lease time in seconds (default: 43200 = 12 hours)
LEASE_TIME=43200
# Max lease time in seconds (default: 86400 = 24 hours)
MAX_LEASE_TIME=86400

View File

@ -0,0 +1,5 @@
# MAC addresses to whitelist (one per line)
# Format: aa:bb:cc:dd:ee:ff
# Example:
# 00:11:22:33:44:55
# aa:bb:cc:dd:ee:ff

18
dhcp/dhcpd.conf.template Normal file
View File

@ -0,0 +1,18 @@
authoritative;
default-lease-time __LEASE_TIME__;
max-lease-time __MAX_LEASE_TIME__;
# Deny all clients by default
deny unknown-clients;
# Network configuration - Direct link only, no routing
subnet __SUBNET__ netmask __NETMASK__ {
range __RANGE_START__ __RANGE_END__;
# No gateway - this is a direct link only
# No DNS - clients should use their own DNS from other interfaces
option subnet-mask __NETMASK__;
option broadcast-address __BROADCAST__;
}
# Whitelisted MAC addresses
__HOST_ENTRIES__

176
dhcp/entrypoint.sh Executable file
View File

@ -0,0 +1,176 @@
#!/bin/bash
set -e
INTERFACE_FILE="/config/interface.conf"
WHITELIST_FILE="/config/whitelist.conf"
NETWORK_FILE="/config/network.conf"
DHCPD_CONF="/etc/dhcp/dhcpd.conf"
if [ ! -f "$INTERFACE_FILE" ]; then
echo "Error: Interface configuration file not found: $INTERFACE_FILE"
exit 1
fi
if [ ! -f "$WHITELIST_FILE" ]; then
echo "Error: Whitelist configuration file not found: $WHITELIST_FILE"
exit 1
fi
INTERFACE=$(cat "$INTERFACE_FILE" | tr -d '\n\r ')
echo "Using interface: $INTERFACE"
# Wait for interface to be available
for i in {1..30}; do
if ip link show "$INTERFACE" &>/dev/null; then
break
fi
echo "Waiting for interface $INTERFACE..."
sleep 1
done
if ! ip link show "$INTERFACE" &>/dev/null; then
echo "Error: Interface $INTERFACE not found"
exit 1
fi
# Load network configuration if available
if [ -f "$NETWORK_FILE" ]; then
echo "Loading network configuration from $NETWORK_FILE"
source "$NETWORK_FILE"
else
echo "No network configuration file found, using defaults"
fi
# Set defaults for unset variables
RANGE_START_OFFSET=${RANGE_START_OFFSET:-10}
RANGE_END_OFFSET=${RANGE_END_OFFSET:-100}
LEASE_TIME=${LEASE_TIME:-43200}
MAX_LEASE_TIME=${MAX_LEASE_TIME:-86400}
# Get interface IP and network info
INTERFACE_IP=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f1)
INTERFACE_CIDR=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f2)
# Check if subnet is manually configured
if [ -n "$SUBNET" ] && [ -n "$NETMASK" ]; then
echo "Using manual network configuration: $SUBNET/$NETMASK"
INTERFACE_NETWORK="$SUBNET"
# Calculate broadcast from subnet and netmask
if [ "$NETMASK" = "255.255.255.0" ]; then
BROADCAST="${SUBNET%.*}.255"
elif [ "$NETMASK" = "255.255.0.0" ]; then
BROADCAST="${SUBNET%.*.*}.255.255"
elif [ "$NETMASK" = "255.0.0.0" ]; then
BROADCAST="${SUBNET%.*.*.*}.255.255.255"
else
BROADCAST="${SUBNET%.*}.255"
fi
else
echo "Auto-detecting network configuration from interface"
# For test environment, handle Docker's internal network properly
if [ "$INTERFACE" = "eth0" ] && [ -n "$INTERFACE_IP" ]; then
# Extract network base from the actual IP
INTERFACE_NETWORK=$(echo "$INTERFACE_IP" | sed 's/\.[0-9]*$/\.0/')
else
# Fallback for physical interfaces
INTERFACE_NETWORK=$(ip -4 addr show "$INTERFACE" | grep "inet " | awk '{print $2}' | cut -d'/' -f1 | sed 's/\.[0-9]*$/\.0/')
fi
# Convert CIDR to netmask and calculate broadcast
case $INTERFACE_CIDR in
24)
NETMASK="255.255.255.0"
BROADCAST="${INTERFACE_NETWORK%.*}.255"
;;
16)
NETMASK="255.255.0.0"
BROADCAST="${INTERFACE_NETWORK%.*.*}.255.255"
;;
8)
NETMASK="255.0.0.0"
BROADCAST="${INTERFACE_NETWORK%.*.*.*}.255.255.255"
;;
*)
NETMASK="255.255.255.0"
BROADCAST="${INTERFACE_NETWORK%.*}.255"
;;
esac
fi
echo "Interface IP: $INTERFACE_IP"
echo "Network: $INTERFACE_NETWORK/$NETMASK"
echo "DHCP Range: ${INTERFACE_NETWORK%.*}.$RANGE_START_OFFSET - ${INTERFACE_NETWORK%.*}.$RANGE_END_OFFSET"
echo "Lease Time: $LEASE_TIME seconds"
# Generate dhcpd.conf from template
cp /etc/dhcp/dhcpd.conf.template "$DHCPD_CONF"
# Generate host entries for whitelisted MACs
HOST_ENTRIES=""
HOST_NUM=1
echo "Reading whitelist from $WHITELIST_FILE"
# Check if file exists and is readable
if [ ! -f "$WHITELIST_FILE" ]; then
echo "ERROR: Whitelist file does not exist!"
exit 1
fi
if [ ! -r "$WHITELIST_FILE" ]; then
echo "ERROR: Whitelist file is not readable!"
exit 1
fi
echo "File size: $(stat -c%s "$WHITELIST_FILE") bytes"
echo "File contents (hex dump):"
hexdump -C "$WHITELIST_FILE" | head -5
echo "File contents (raw):"
cat "$WHITELIST_FILE"
echo ""
echo "--- Processing MACs ---"
# Read file line by line without subshell
while IFS= read -r MAC || [ -n "$MAC" ]; do
# Remove any whitespace, carriage returns, and convert to lowercase
ORIG_MAC="$MAC"
MAC=$(echo "$MAC" | tr -d '\r\n\t ' | tr '[:upper:]' '[:lower:]')
echo "Processing: original='$ORIG_MAC' cleaned='$MAC'"
if [ -n "$MAC" ]; then
# Generate IP based on host number (starting from .10)
LAST_OCTET=$((10 + HOST_NUM - 1))
HOST_IP="${INTERFACE_NETWORK%.*}.$LAST_OCTET"
NEW_ENTRY="
host client$HOST_NUM {
hardware ethernet $MAC;
fixed-address $HOST_IP;
}"
HOST_ENTRIES="${HOST_ENTRIES}${NEW_ENTRY}"
echo "Added whitelist entry: $MAC -> $HOST_IP"
HOST_NUM=$((HOST_NUM + 1))
fi
done < "$WHITELIST_FILE"
if [ -z "$HOST_ENTRIES" ]; then
echo "Warning: No MAC addresses found in whitelist!"
else
echo "Total whitelisted MACs: $((HOST_NUM - 1))"
fi
# Replace placeholders in config
sed -i "s#__SUBNET__#$INTERFACE_NETWORK#g" "$DHCPD_CONF"
sed -i "s#__NETMASK__#$NETMASK#g" "$DHCPD_CONF"
sed -i "s#__RANGE_START__#${INTERFACE_NETWORK%.*}.$RANGE_START_OFFSET#g" "$DHCPD_CONF"
sed -i "s#__RANGE_END__#${INTERFACE_NETWORK%.*}.$RANGE_END_OFFSET#g" "$DHCPD_CONF"
sed -i "s#__BROADCAST__#$BROADCAST#g" "$DHCPD_CONF"
sed -i "s#__LEASE_TIME__#$LEASE_TIME#g" "$DHCPD_CONF"
sed -i "s#__MAX_LEASE_TIME__#$MAX_LEASE_TIME#g" "$DHCPD_CONF"
# Use a different approach for multiline HOST_ENTRIES
awk -v entries="$HOST_ENTRIES" '{gsub(/__HOST_ENTRIES__/, entries); print}' "$DHCPD_CONF" > "$DHCPD_CONF.tmp" && mv "$DHCPD_CONF.tmp" "$DHCPD_CONF"
echo "Starting DHCP server on interface $INTERFACE..."
echo "Configuration:"
cat "$DHCPD_CONF"
# Start DHCP server
exec dhcpd -4 -f -d -cf "$DHCPD_CONF" "$INTERFACE"

45
docker-compose.test.yml Normal file
View File

@ -0,0 +1,45 @@
version: '3.8'
networks:
test-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
gateway: 172.20.0.1
services:
test-dhcp-server:
build: .
image: dhcp-whitelist:test
container_name: test-dhcp-server
networks:
test-net:
ipv4_address: 172.20.0.2
volumes:
- ./test/test-config:/config:ro
environment:
- TZ=UTC
cap_add:
- NET_ADMIN
- NET_RAW
test-client-allowed:
build: ./test/test-client
container_name: test-client-allowed
networks:
test-net:
mac_address: "02:42:ac:11:00:02"
depends_on:
- test-dhcp-server
command: ["/bin/sh", "-c", "sleep 5 && udhcpc -i eth0 -n -q && ip addr show eth0"]
test-client-denied:
build: ./test/test-client
container_name: test-client-denied
networks:
test-net:
mac_address: "02:42:ac:11:00:99"
depends_on:
- test-dhcp-server
command: ["/bin/sh", "-c", "sleep 5 && timeout 10 udhcpc -i eth0 -n -q || echo 'DHCP request denied as expected'"]

17
docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
version: '3.8'
services:
dhcp-server:
build: .
image: dhcp-whitelist:latest
container_name: dhcp-whitelist-server
network_mode: host
privileged: true
volumes:
- ./config:/config:ro
restart: unless-stopped
environment:
- TZ=UTC
cap_add:
- NET_ADMIN
- NET_RAW

155
scripts/autoconfig.sh Executable file
View File

@ -0,0 +1,155 @@
#!/bin/bash
set -e
CONFIG_DIR="./config"
INTERFACE_CONF="${CONFIG_DIR}/interface.conf"
WHITELIST_CONF="${CONFIG_DIR}/whitelist.conf"
NETWORK_CONF="${CONFIG_DIR}/network.conf"
echo "=== DHCP Whitelist Auto-Configuration ==="
echo
# Create config directory if it doesn't exist
mkdir -p "${CONFIG_DIR}"
# Find ethernet interfaces (excluding loopback and virtual interfaces)
echo "Detecting ethernet interfaces..."
INTERFACES=$(ip link show | grep -E "^[0-9]+: en" | awk -F': ' '{print $2}' | head -1)
if [ -z "$INTERFACES" ]; then
echo "Error: No ethernet interface found"
echo "Please configure manually by editing ${INTERFACE_CONF}"
exit 1
fi
echo "Found ethernet interface: $INTERFACES"
# Get the interface with an IP in 192.168.x.x range (common for local networks)
SELECTED_INTERFACE=""
for IFACE in $INTERFACES; do
IP_INFO=$(ip -4 addr show "$IFACE" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' || true)
if [ -n "$IP_INFO" ]; then
echo " Interface $IFACE has IP: $IP_INFO"
if [[ "$IP_INFO" =~ ^192\.168\. ]] || [[ "$IP_INFO" =~ ^10\. ]] || [[ "$IP_INFO" =~ ^172\. ]]; then
SELECTED_INTERFACE="$IFACE"
break
fi
fi
done
if [ -z "$SELECTED_INTERFACE" ]; then
# Fall back to first interface with any IP
for IFACE in $INTERFACES; do
if ip -4 addr show "$IFACE" 2>/dev/null | grep -q "inet "; then
SELECTED_INTERFACE="$IFACE"
break
fi
done
fi
if [ -z "$SELECTED_INTERFACE" ]; then
echo "Error: No ethernet interface with IP address found"
echo "Using first available interface: $INTERFACES"
SELECTED_INTERFACE="$INTERFACES"
fi
echo
echo "Selected interface: $SELECTED_INTERFACE"
echo "$SELECTED_INTERFACE" > "${INTERFACE_CONF}"
# Find MAC addresses of devices on the network
echo
echo "Scanning for devices on the network..."
# Get the network range
NETWORK_INFO=$(ip -4 addr show "$SELECTED_INTERFACE" | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+' | head -1)
if [ -z "$NETWORK_INFO" ]; then
echo "Warning: Could not determine network range for $SELECTED_INTERFACE"
echo "Creating empty whitelist file. Please add MAC addresses manually."
touch "${WHITELIST_CONF}"
else
NETWORK=$(echo "$NETWORK_INFO" | cut -d'/' -f1 | sed 's/\.[0-9]*$/\.0/')
NETMASK=$(echo "$NETWORK_INFO" | cut -d'/' -f2)
echo "Network: ${NETWORK}/${NETMASK}"
# Try to ping the network to populate ARP table (just a few addresses)
echo "Discovering devices (this may take a moment)..."
BASE_NET=$(echo "$NETWORK" | sed 's/\.0$//')
for i in {1..10}; do
ping -c 1 -W 1 "${BASE_NET}.${i}" >/dev/null 2>&1 &
done
wait
# Get MAC addresses from ARP table
echo
echo "Found devices:"
MACS=$(ip neigh show dev "$SELECTED_INTERFACE" | grep -E "lladdr" | awk '{print $5}' | sort -u)
if [ -z "$MACS" ]; then
echo " No devices found in ARP table"
echo " Creating empty whitelist. Please add MAC addresses manually."
touch "${WHITELIST_CONF}"
else
echo "$MACS" | while read -r MAC; do
IP=$(ip neigh show dev "$SELECTED_INTERFACE" | grep "$MAC" | awk '{print $1}')
echo " $MAC (IP: $IP)"
done
echo
echo "Writing MAC addresses to whitelist..."
echo "$MACS" > "${WHITELIST_CONF}"
echo "Added $(echo "$MACS" | wc -l) MAC address(es) to whitelist"
fi
fi
echo
echo "=== Configuration Summary ==="
echo "Interface: $(cat "${INTERFACE_CONF}")"
echo "Whitelist entries:"
if [ -f "${WHITELIST_CONF}" ] && [ -s "${WHITELIST_CONF}" ]; then
cat "${WHITELIST_CONF}" | sed 's/^/ /'
else
echo " (empty - please add MAC addresses manually)"
fi
echo
# Create or update network.conf with defaults (can be customized)
if [ ! -f "${NETWORK_CONF}" ]; then
echo
echo "Creating default network configuration..."
cat > "${NETWORK_CONF}" << EOF
# Network configuration for DHCP server
# These values override automatic detection
# Network subnet (leave empty for auto-detection from interface)
SUBNET=
# Netmask in dotted notation (leave empty for auto-detection)
NETMASK=
# DHCP range start offset from network base (default: 10)
RANGE_START_OFFSET=10
# DHCP range end offset from network base (default: 100)
RANGE_END_OFFSET=100
# Lease time in seconds (default: 43200 = 12 hours)
LEASE_TIME=43200
# Max lease time in seconds (default: 86400 = 24 hours)
MAX_LEASE_TIME=86400
EOF
echo "Network configuration created with defaults"
else
echo "Network configuration already exists, keeping current settings"
fi
echo
echo "Configuration files created:"
echo " ${INTERFACE_CONF}"
echo " ${WHITELIST_CONF}"
echo " ${NETWORK_CONF}"
echo
echo "You can now start the DHCP server with: make up"
echo "Or install as a service with: sudo make install"

134
scripts/install-service.sh Executable file
View File

@ -0,0 +1,134 @@
#!/bin/bash
set -e
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo "This script must be run with sudo"
echo "Usage: sudo make install"
exit 1
fi
SERVICE_NAME="dhcp-whitelist"
SERVICE_FILE="systemd/${SERVICE_NAME}.service"
SYSTEMD_DIR="/etc/systemd/system"
CONFIG_DIR="/etc/dhcp-whitelist"
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
echo "=== Installing DHCP Whitelist Service ==="
echo
# Check if Docker is installed
if ! command -v docker &> /dev/null; then
echo "Error: Docker is not installed"
echo "Please install Docker first"
exit 1
fi
# Check if docker-compose is installed
if ! command -v docker-compose &> /dev/null; then
echo "Error: docker-compose is not installed"
echo "Please install docker-compose first"
exit 1
fi
# Create config directory
echo "Creating configuration directory: ${CONFIG_DIR}"
mkdir -p "${CONFIG_DIR}"
# Copy configuration files
echo "Copying configuration files..."
if [ -f "${PROJECT_DIR}/config/interface.conf" ]; then
cp "${PROJECT_DIR}/config/interface.conf" "${CONFIG_DIR}/"
echo " Copied interface.conf"
else
echo "Warning: config/interface.conf not found"
echo " Creating default interface.conf"
echo "enp0s13f0u3" > "${CONFIG_DIR}/interface.conf"
fi
if [ -f "${PROJECT_DIR}/config/whitelist.conf" ]; then
cp "${PROJECT_DIR}/config/whitelist.conf" "${CONFIG_DIR}/"
echo " Copied whitelist.conf"
else
echo "Warning: config/whitelist.conf not found"
echo " Creating empty whitelist.conf"
touch "${CONFIG_DIR}/whitelist.conf"
fi
# Set proper permissions
chmod 644 "${CONFIG_DIR}"/*.conf
echo "Configuration files installed to: ${CONFIG_DIR}"
# Create service file from template
echo
echo "Creating systemd service file..."
cat > "${SYSTEMD_DIR}/${SERVICE_NAME}.service" << EOF
[Unit]
Description=DHCP Whitelist Service
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
Restart=always
RestartSec=10
WorkingDirectory=${PROJECT_DIR}
Environment="CONFIG_DIR=${CONFIG_DIR}"
# Pre-start: Build the image
ExecStartPre=/usr/bin/docker-compose build
# Start the service
ExecStart=/usr/bin/docker-compose up
# Stop the service
ExecStop=/usr/bin/docker-compose down
# Reload config by restarting containers
ExecReload=/usr/bin/docker-compose restart
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
echo "Service file created: ${SYSTEMD_DIR}/${SERVICE_NAME}.service"
# Create docker-compose override for service mode
echo
echo "Creating docker-compose override for service mode..."
cat > "${PROJECT_DIR}/docker-compose.override.yml" << EOF
version: '3.8'
services:
dhcp-server:
volumes:
- ${CONFIG_DIR}:/config:ro
EOF
# Reload systemd
echo
echo "Reloading systemd daemon..."
systemctl daemon-reload
echo
echo "=== Installation Complete ==="
echo
echo "Configuration files location: ${CONFIG_DIR}"
echo " - ${CONFIG_DIR}/interface.conf"
echo " - ${CONFIG_DIR}/whitelist.conf"
echo
echo "Service management commands:"
echo " Start service: systemctl start ${SERVICE_NAME}"
echo " Stop service: systemctl stop ${SERVICE_NAME}"
echo " Enable on boot: systemctl enable ${SERVICE_NAME}"
echo " Check status: systemctl status ${SERVICE_NAME}"
echo " View logs: journalctl -u ${SERVICE_NAME} -f"
echo
echo "Or use make targets:"
echo " make service_up - Enable and start service"
echo " make service_down - Stop and disable service"
echo
echo "To start the service now, run: make service_up"

View File

@ -0,0 +1,29 @@
[Unit]
Description=DHCP Whitelist Service
After=network.target docker.service
Requires=docker.service
[Service]
Type=simple
Restart=always
RestartSec=10
WorkingDirectory=/opt/dhcp-whitelist
Environment="CONFIG_DIR=/etc/dhcp-whitelist"
# Pre-start: Build the image
ExecStartPre=/usr/bin/docker-compose build
# Start the service
ExecStart=/usr/bin/docker-compose up
# Stop the service
ExecStop=/usr/bin/docker-compose down
# Reload config by restarting containers
ExecReload=/usr/bin/docker-compose restart
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,7 @@
FROM alpine:3.19
RUN apk add --no-cache \
iproute2 \
iputils
CMD ["/bin/sh"]

View File

@ -0,0 +1 @@
eth0

View File

@ -0,0 +1,20 @@
# Network configuration for TEST DHCP server
# Uses 172.20.0.0/24 to avoid conflicts with common networks
# Network subnet (overrides auto-detection)
SUBNET=172.20.0.0
# Netmask in dotted notation
NETMASK=255.255.255.0
# DHCP range start offset from network base
RANGE_START_OFFSET=10
# DHCP range end offset from network base
RANGE_END_OFFSET=100
# Lease time in seconds (shorter for testing)
LEASE_TIME=300
# Max lease time in seconds
MAX_LEASE_TIME=600

View File

@ -0,0 +1 @@
02:42:ac:11:00:02