Requirments / Necessities
DHCP (Juniper SRX)
HTTP (Synology)
microSD cards (I have 4 or 8GB)
(optional) Power-over-Ethernet Switch and PiHats
Raspberry Pi 4(s)
Setup
IP
172.20.10.11
is the IP for the Synology.IP
172.16.1.11
is the IP for a Raspberry Pi 4.
DHCP
The following Juniper SRX configuration (snipet) is what I use in my network for iPXE:
[edit access address-assignment pool lease-default family inet]
network 172.16.0.0/16;
dhcp-attributes {
server-identifier 172.16.0.1;
name-server {
172.16.1.14;
172.16.1.13;
172.16.1.11;
1.1.1.1;
}
router {
172.16.0.1;
}
boot-file http://172.20.10.11/ipxe/fcos.ipxe; (1)
boot-server 172.20.10.11; (2)
tftp-server 172.20.10.11; (3)
}
host rpi1 {
hardware-address dc:a6:32:f0:d4:b3;
ip-address 172.16.1.11;
}
Set to the location of the PXE bootloader/filename. Also known in ISC DHCPD:
filename
(Option 67).Set to the server of the PXE/TFTP server. Otherwise, the client might try to download the boot-file from the SRX.
(Also) Set to the server of the PXE/TFTP server. Also known in ISC DHCPD:
next-server
(Option 66).
Now when a Raspberry Pi dies I just have to replace the DHCP Reservation and the new Raspberry Pi will boot into my config
HTTP
I use a HTTP Server to speed up the downloading of Fedora CoreOS, but TFTP works too. The benefits of using HTTP is the protocol is much faster than TFTP.
MicroSD Boot
I tried to use the Raspberry Pi 4’s native capability to network boot without a microSD card, but since we are booting a UEFI firmware then the iPXE bootloader, which boots Fedora CoreOS; the complexities are too great to boot everything from network. Using a iPXE image on microSD avoids having to deal with the chainloading issues.
Configuration Files
iPXE
As seen in the boot-file/filename, the fcos.ipxe
is a simple script that gets run by the iPXE bootloader. The following code is for the iPXE script.
#!ipxe
iseq ${net0/ip} 172.16.1.10 && set CONFIGURL http://172.20.10.11/coreos/rpi0.ign ||
iseq ${net0/ip} 172.16.1.11 && set CONFIGURL http://172.20.10.11/coreos/rpi1.ign ||
iseq ${net0/ip} 172.16.1.12 && set CONFIGURL http://172.20.10.11/coreos/rpi2.ign ||
iseq ${net0/ip} 172.16.1.13 && set CONFIGURL http://172.20.10.11/coreos/rpi3.ign ||
iseq ${net0/ip} 172.16.1.14 && set CONFIGURL http://172.20.10.11/coreos/rpi4.ign ||
iseq ${net0/ip} 172.16.1.15 && set CONFIGURL http://172.20.10.11/coreos/rpi5.ign ||
iseq ${net0/ip} 172.16.1.16 && set CONFIGURL http://172.20.10.11/coreos/rpi6.ign ||
iseq ${net0/ip} 172.16.1.17 && set CONFIGURL http://172.20.10.11/coreos/rpi7.ign ||
set KERNELFILE kernel-aarch64.gz
set INITRDFILE initramfs.aarch64.img
set ROOTFSFILE rootfs.aarch64.img
set BASEURL http://172.20.10.11/fedora-coreos
set ROOTFSURL ${BASEURL}/${ROOTFSFILE}
set KERNELOPT console=tty1 rw
set COREOSOPT coreos.live.rootfs_url=${ROOTFSURL}
set IGNITNOPT ignition.firstboot ignition.platform.id=metal ignition.config.url=${CONFIGURL}
kernel ${BASEURL}/${KERNELFILE} initrd=main ${COREOSOPT} ${IGNITNOPT} ${KERNELOPT}
initrd --name main ${BASEURL}/${INITRDFILE}
boot
iPXE should have a dhcp ip before running this script. In my environment, the boot will error out at various points before this script is executed.
Butane
variant: fcos
version: 1.4.0
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-rsa <SSH_KEY> snelson@example.com
storage:
directories:
- overwrite: true
path: /opt/pihole/etc
- overwrite: true
path: /opt/pihole/dnsmasq.d
files:
- path: /etc/NetworkManager/conf.d/dns.conf
contents:
inline: |
[main]
dns=none
group:
id: 0
mode: 420
user:
id: 0
- path: /etc/resolv.conf
contents:
inline: |
nameserver 1.1.1.1
group:
id: 0
mode: 420
overwrite: true
user:
id: 0
- path: /etc/hostname
contents:
inline: rpi1.<BASE_DOMAIN>
mode: 420
- path: /opt/pihole/etc/environment
contents:
inline: |
TZ=America/Denver
WEBPASSWORD=<PASSWORD>
mode: 420
systemd:
units:
- name: coreos-migrate-to-systemd-resolved.service
enabled: false
mask: true
- name: systemd-resolved.service
enabled: false
mask: true
- name: pihole.service
enabled: true
contents: |
[Unit]
Description=Pi-hole
After=network-online.target
Wants=network-online.target
[Service]
TimeoutStartSec=0
ExecStop=/bin/podman stop pihole
ExecStartPre=mkdir -p /opt/pihole
ExecStartPre=-/bin/podman kill pihole
ExecStartPre=-/bin/podman rm pihole
ExecStartPre=-/bin/podman pull docker.io/pihole/pihole:latest
ExecStart=/bin/podman run \
--net=host \
--name pihole \
--volume /opt/pihole/etc/:/etc/pihole/:z \
--volume /opt/pihole/dnsmasq.d/:/etc/dnsmasq.d/:z \
--env-file /opt/pihole/etc/environment \
--cap-add=NET_ADMIN \
docker.io/pihole/pihole:latest
[Install]
WantedBy=multi-user.target
The two big pieces for getting Pihole to run on CoreOS is the pihole.service
systemd service file and /opt/pihole/etc/environment
, which sets important container environment variables like the Time Zone and the WebUI admin password. Within the pihole.service
, you could add specific port exposures to limit access as Fedora CoreOS doesn’t currently (CY22Q1) enable the firewall.
For this blog post, the CoreOS Butane configuration has been combined. In reality (now), my butnane configuration are separate butane configs that get compiled into individual igntion configs which get "merged" via the ignition executor in CoreOS on boot. The decomposition is done by function (hostname, network config, etc) with separate butane configs for the container configuration and one config for the SystemD service that starts the container.
Compiling Butane
Please read the official Documentation for how to use Fedora CoreOS and Butane for configuration.
butane -s -d . config.bu -o rpi1.ign
Conclusion
Even though the Raspberry Pi still needs microSD cards to boot, the longevity of the microSD cards should be much longer as only a portion of the card is written to and read from. The bulk of the OS is loaded into and ran from RAM, so if the application needs the bulk of available RAM, this method wouldn’t be suitable.
Using Butane to merge multiple Ignition configs via CoreOS Boot
variant: fcos
version: 1.4.0
ignition:
config:
merge:
- source: http://172.20.10.11/coreos/base/host/users/core.ign
- source: http://172.20.10.11/coreos/base/host/network/bootstrap.ign
- source: http://172.20.10.11/coreos/base/host/network/fqdn/hostname-rpi1.ign
- source: http://172.20.10.11/coreos/base/apps/pihole/pihole-environment.ign
- source: http://172.20.10.11/coreos/base/apps/pihole/pihole-service.ign