Wednesday February 23 2022
I actually tried OPNsense before writing this article, I’ve also heavily used pfSense in the past professionally as well as personally. Though I haven’t installed pfSense recently, I don’t really like all of the added complexity and surface area for attack that they provide. Plus, they monopolize the underlying hardware, which while it isn’t so much of a concern for my immediate purposes is something worth noting.
So, with that out of the way, I’m going for a bare-bones system, that will run in RAM and have a couple of minimal shell scripts that drive configuration.
Any machine with two network cards should work. These days most machines have USB 3 ports, and using a USB 3 ethernet adapter is fine too. Though you could technically do this on a Raspberry Pi and probably get away with it the installation is going to be quite different. I’m simply using an old laptop personally, it’s nice to have a screen, keyboard, and battery backup all in a single package. I’ve noticed that the fans in laptops tend to last longer than the ones in many mini computers.
Beyond that, you’ll need a flash drive for the installation medium and something to install to, be it another flash drive or the system’s hard drive/ssd.
Head on over to the Alpine Linux
website and pick up the Extended ISO,
x86_64. You can use the other images
too, just note that it doesn’t come with the same packages I’ll be using
to partition the disk.
Burn the image to a flash drive
# dd if=alpine-extended-3.15.0-x86_64.iso of=/dev/sdX bs=4M
On Windows I recommend using Rufus. On MacOS you’ll also use
dd, look up
how to find the correct
Boot to the flash drive. Login as
root, there should be no password.
# apk add util-linux
The version of
util-linux supports GPT partition tables, which
we will be using here for UEFI boot.
sdX with the local hard drive,
lsblk may give you some hints as
to which one that is.
# fdisk /dev/sdX
Let’s create the GPT partition table:
Command (m for help): g
Now a new partition:
Command (m for help): n Partition number (1-128, default 1): <Enter> First sector (2048-30965726, default 2048): <Enter> Last sector, <...trimmed...>: +2G<Enter> # It may complain about signatures of old filesystems, answer `y` to wipe them.
Now lets set the type to UEFI:
Command (m for help): t Selected partition 1 Partition type or alias (type L to list all): 1<Enter>
One more partition for the rest of the disk: ( This will be used for
Command (m for help): n Partition number (1-128, default 2): <Enter> First sector (4196352-30965726, default 4196352): <Enter> Last sector, <...trimmed...>: <Enter> # It may complain about signatures of old filesystems, answer `y` to wipe them.
Lets save the new partition table and exit
Command (m for help): w
Create the filesystems:
# mkfs.vfat -F32 -n Alpine /dev/sdX1 # mkfs.xfs /dev/sdX2
Now lets mount the new UEFI partition:
# mount /dev/sdX1 /mnt
Now for a bit trickier part, we need to locate where the installation medium is,
on my system it’s
/media/sda. You may wish to check
a bit more information on where the installation media is mounted.
Now, to copy over the files:
# setup-bootable /media/sda /mnt
Now reboot, and remove the installation medium, you should have a bone stock Alpine Linux system.
Now that we have Alpine installed to the disk, we can use their convenient setup
script to further configure the system. Right now it operates in much the same
way that the installation medium did, it boots and loads everything into memory.
This is great, but we need a way to save our changes, this is where
come in, but first, let’s get logged in as
Since this is going to be a router, we’re going to take a different approach
to setting up the network interfaces,
nftables and such, for now let’s just
set up the link and grab an address via DHCP:
# ip link set up eth0 # udhcpc -i eth0
From there, let’s run the alpine setup script:
It will ask for network configuration, just type in
done straight away,
n for the “would you like to do any manual network configuration”
question. We’re going to cover this in a little bit.
The next few questions will be pretty straightforward. I used
chrony for NTP
openssh for my SSH daemon.
alpine-cdn for my mirror.
When you get to:
No disks available. Try boot media /media/sda1? (y/n) [n]
I just hit
Enter where to store configs ('floppy', 'sda1', 'usb' or 'none') [sda1]
sda1 is fine here.
Enter apk cache directory (or '?' or 'none') [/media/sda1/cache]
/media/sda1/cache Is fine here too.
Now that is all setup, type in
lbu ci and reboot. If done properly the system
should boot up and keep all of the configuration you just set. ( I’ll explain
this more in a little bit, if you find you’re able to login without a password
and the system appears unconfigured after a reboot, go back and check your work. )
Now lets setup the network again:
# ip link set up eth0 # udhcpc -i eth0
Get all of our packages upgraded:
# apk upgrade
Install packages for our basic router:
# apk add iptables ip6tables dnsmasq miniupnpd dhcpcd radvd curl
Now setup the
local service for all of our scripts:
# rc-update add local boot
From here we’re going to clone down the Alpine Home Router scripts I created, they’re small enough to be easily read in 20 minutes or so, and I encourage you to do so, for now let’s just get them installed:
# curl https://git.riedstra.dev/mitch/alpine-home-router/snapshot/alpine-home-router-1.0.tar.gz | tar -C /tmp -xzvf - # cp /tmp/alpine-home-router-1.0/etc/local.d/* /etc/local.d/
Now lets configure them:
You should be confronted with a file along the lines of:
# You can replace tty1 with ttyS0 for serial output exec >/dev/tty1 2>&1 printf '\033[1;32m' # Green output for our scripts, comment out to disable. echo "Starting script: $0" set -x wan=eth0 # WAN / ISP uplink interface, assumed ip is provided by DHCP wan_hwaddr=EA:5D:EA:DB:EE:FF lan=eth1 # LAN interface # `sipcalc` is a useful program here lan_ip=192.168.0.1 lan_net=192.168.0.0 lan_mask_bits=24 domain=router.local dhcp_range=192.168.0.30,192.168.0.230,24h # Specifically overrides upstream on `dnsmasq` for the DHCP clients dns_servers="220.127.116.11 18.104.22.168"
The first line simply ensures that any output is dumped to the main boot terminal, you can comment it out if you want a more quiet boot process or to get output from the scripts when you call them directly.
The second line colors it green, entirely optional, you can comment it out.
From there the rest of the configuration should be self explanatory if you have a little bit a networking background.
If not, simply adjust
wan to the interface that will have your modem plugged
into it, and
lan to the interface that will have your home network pugged into
it. ( You can tell which one is plugged in with
ip link, the ones unplugged
with say “NO-CARRIER” )
Because of how the system is loaded into ram on boot, you must explicitly
save to disk any changes, this is done with a little program called
that writes out a simple tarball with your configuration files. The Alpine
Linux initrd ( early boot ) will scan for an
apkovl ( the aforementioned
tarball ), if it finds one it will load it up into memory on top of what was
there, effectively restoring your changes.
There’s a lot of documentation on the Alpine Linux wiki about
lbu, and I encourage
you to read it.
As quick rundown though:
# lbu st # shows the current status, no output is no changes # lbu ci # commits current changes to disk # lbu diff # show specifically the differences between now and the last commit
Anyway, go ahead and run
lbu ci and reboot. When the system comes back up,
if you have the WAN and LAN interfaces plugged in properly, you will have your
very own Alpine Linux router.
Those familiar will know
SSH and probably be off to the races configuring it.
I’m not going to belabor and duplicate a bunch of the other high quality content
out there, I’m just going to say:
Use an SSH key, do not enable password logins for root.
Anyway, the default
lbu configuration does not include the
directory where you’d normally store the SSH keys, that’s easy enough to remedy:
# lbu include /root/.ssh
Then simply run a commit once you’ve added your SSH keys:
# lbu ci
From here if you want miniupnpd, simply enable the service:
# rc-update add miniupnpd default
Anything else you’d like add to the system should be straight forward enough
apk add <pkgname> configure it and run
lbu ci to have your changes
persist across reboots.
If you wish to configure a database, webserver, or something else with large
amounts of persistent storage, simply create an entry in
that XFS partition we created earlier. My advise? Copy the entire contents of
/var into it, and then mount it there.