Michael Shriver Senior Computer Specialist - College of the Environment

Configuring a simple router in initrd

Target: Debian Bookworm box with an encrypted root fs

Goal: Enable Remote Unlock but allow the computer to continue to act as a router while it is waiting for someone to unlock the disk

The basic router functions require DHCPd and nftables to be installed and configured in the initramfs prior to a full system boot. It may also require killing the services once the disk has been unlocked so that the primary system services can take over.

Prerequisites

sudo apt install initramfs-tools isc-dhcp-server nftables

Configure initramfs-tools

Configuring the network interfaces

Configure the internal network interface with a static ip address via kernal command line:

GRUB_CMDLINE_LINUX_DEFAULT="ip=192.168.21.2:::::enp0s25:on panic=30"

This will configure the internal interface with a static ip. For more information on configuring the wifi interface see here. Note that the kernel doesn’t seem to handle multiple ‘ip=’ directives well, at least when one is a wireless device. We will have to add some extras in order to manually bring up the wifi below.

/etc/initramfs-tools/hooks/enable_wireless

# !/bin/sh
set -e
PREREQ=""
prereqs()
{
    echo "${PREREQ}"
}
case "${1}" in
    prereqs)
        prereqs
        exit 0
        ;;
esac

. /usr/share/initramfs-tools/hook-functions

# CHANGE HERE for your correct modules.
manual_add_modules 88XXau cfg80211 usbcore
copy_exec /sbin/wpa_supplicant
copy_exec /sbin/wpa_cli

# Required to manually configure the wifi interface:
copy_exec /usr/sbin/dhclient
copy_exec /sbin/dhclient-script

copy_file config /etc/initramfs-tools/wpa_supplicant.conf /etc/wpa_supplicant.conf

Note: I am copying dhclient and the dhclient-script into my initramfs because the default ipconfig command to retrieve a dhcp lease is not working when I connect to an xfinitywifi hotspot. This is probably due to some anti-spoofing measures on the router. You may be able to omit these lines and use a command such as ‘ipconfig -t 8 -c dhcp -d wlan0’ instead.

/etc/initramfs-tools/scripts/init-premount/a_enable_wireless

#!/bin/sh
PREREQ=""
prereqs()
{
    echo "$PREREQ"
}
    
case $1 in
prereqs)
    prereqs
    exit 0
    ;;
esac
    
. /scripts/functions
    
AUTH_LIMIT=30
INTERFACE="wlan0"
HOSTNAME="servername"
alias WPACLI="/sbin/wpa_cli -p/tmp/wpa_supplicant -i$INTERFACE "
    
log_begin_msg "Starting WLAN connection"
/sbin/wpa_supplicant  -i$INTERFACE -c/etc/wpa_supplicant.conf -P/run/initram-wpa_supplicant.pid -B -f /tmp/wpa_supplicant.log
    
# Wait for AUTH_LIMIT seconds, then check the status
limit=${AUTH_LIMIT}
    
echo -n "Waiting for connection (max ${AUTH_LIMIT} seconds)"
while [ $limit -ge 0 -a `WPACLI status | grep wpa_state` != "wpa_state=COMPLETED" ]
do
    sleep 1
    echo -n "."
    limit=`expr $limit - 1`
done
echo ""
    
if [ `WPACLI status | grep wpa_state` != "wpa_state=COMPLETED" ]; then
  ONLINE=0
  log_failure_msg "WLAN offline after timeout"
  panic
else
  ONLINE=1
  log_success_msg "WLAN online"
fi
    
# configure_networking
# Do this manually instead:
    
hostname $HOSTNAME
/sbin/dhclient -4 -pf /run/initram-dhclient.pid -lf /tmp/dhclient.leases $INTERFACE

Add nftables and all of the required kernel modules to the initramfs:

/etc/initramfs-tools/hooks/enable-nftables

# !/bin/sh
set -e
PREREQ=""
prereqs()
{
    echo "${PREREQ}"
}
case "${1}" in
    prereqs)
        prereqs
        exit 0
        ;;
esac
    
. /usr/share/initramfs-tools/hook-functions
    
manual_add_modules nf_tables libcrc32 nfnetlink nft_chain_nat nft_nat nft_masq nft_conntrack nft_defrag_ipv4 nft_defrag_ipv6
copy_exec /usr/sbin/nft
copy_file config /etc/initramfs-tools/nftables.conf /etc/nftables.conf

/etc/initramfs-tools/nftables.conf

#!/sbin/nft -f
    
flush ruleset
    
table ip nat {
    chain prerouting {
	    type nat hook prerouting priority dstnat; policy accept;
    }
    
    chain postrouting {
	    type nat hook postrouting priority srcnat; policy accept;
	    oifname "wlan0" masquerade
    }
}

/etc/initramft-tools/scripts/local-top/enable-nftables

#!/bin/sh
PREREQ=""
prereqs()
{
    echo "$PREREQ"
}

case $1 in
prereqs)
    prereqs
    exit 0
    ;;
esac

. /scripts/functions

# Required or the kernel will refuse to forward packets:
log_begin_msg "Enabling IPv4 forwarding"

sysctl net.ipv4.ip_forward=1

log_begin_msg "Applying NAT rules"

/sbin/nft -f /etc/nftables.conf

EXITCODE=$?

if [ $EXITCODE -ne 0 ]; then
    log_failure_message "nft failed with exit status of $EXITCODE"
else
    log_success_message "NAT rules applied"
fi

log_end_msg

Configure the dhcpd server:

/etc/initramfs-tools/hooks/enable-dhcpd

# !/bin/sh
set -e
PREREQ=""
prereqs()
{
    echo "${PREREQ}"
}
case "${1}" in
    prereqs)
        prereqs
        exit 0
        ;;
esac
    
. /usr/share/initramfs-tools/hook-functions
    
copy_exec /usr/sbin/dhcpd
copy_file config /etc/initramfs-tools/dhcpd.conf /etc/dhcpd.conf

/etc/initramfs-tools/scripts/local-top/enable-dhcpd

#!/bin/sh
PREREQ=""
prereqs()
{
    echo "$PREREQ"
}
    
case $1 in
prereqs)
    prereqs
    exit 0
    ;;
esac
    
. /scripts/functions
    
log_begin_msg "Starting DHCP Server"
    
touch /tmp/initram-dhcpd.leases
sleep 5 # A workaround because the dhcp server was not starting properly
/sbin/dhcpd -4 -q -cf /etc/dhcpd.conf -lf /tmp/initram-dhcpd.leases -pf /run/initram-dhcpd.pid
    
EXITCODE=$?
    
if [ $EXITCODE -ne 0 ]; then
    log_failure_message "DHCP Server failed with exit status of $EXITCODE"
else
    log_success_message "DHCP Server started"
fi
    
log_end_msg

/etc/initramfs-tools/scripts/local-bottom/kill-dhcpd

#!/bin/sh
PREREQ=""
prereqs()
{
    echo "$PREREQ"
}
    
case $1 in
prereqs)
    prereqs
    exit 0
    ;;
esac
    
echo "Killing initrd DHCP server"
kill `cat /run/initram-dhcpd.pid`

/etc/initramfs-tools/dhcpd.conf

option domain-name "shrikeaero.com";
option domain-name-servers 94.140.14.14, 94.140.15.15;

default-lease-time 600;
max-lease-time 1800; # Use a relatively short lease time

authoritative;
log-facility local7;

subnet 192.168.21.0 netmask 255.255.255.0 {
  range 192.168.21.200 192.168.21.254; # Use a different range than the primary service
                                       # to prevent race condition IP address collisions
  option routers 192.168.21.1;
}