(Arm64/IPv6-only)
This guide describes how to install Void Linux on ARM Hetzner Cloud instances with IPv6-only setup.
Table of contents:
Unfortunately Hetzner forces the use of EFI boot, which makes custom installations a pain in the ass. Fortunately there are workarounds that clever people comes up with. I'm not that clever though so it took me some time, but after a few hours of swearing on how crap up EFI is, I managed to get through the installation.
Reboot into Rescue
Get the latest aarch64 rootfs tarball from https://voidlinux.org/download/#arm
Log into the rescue SSH and follow all the steps.
First wipe the drive and create new partitions:
wipefs -a /dev/sda
fdisk /dev/sda
First 1 GB EFI and then linux partition on the remaining space:
Welcome to fdisk (util-linux 2.38.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS (MBR) disklabel with disk identifier 0xb643a8b6.
Command (m for help): g
Created a new GPT disklabel (GUID: 29add4d7-e644-4709-9b3c-209b68765ff2).
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-80003038, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-80003038, default 80001023): +1G
Created a new partition 1 of type 'Linux filesystem' and of size 1 GiB.
Partition #1 contains a vfat signature.
Do you want to remove the signature? [Y]es/[N]o: Y
The signature will be removed by a write command.
Command (m for help): t
Selected partition 1
Partition type or alias (type L to list all): 1
Changed type of partition 'Linux filesystem' to 'EFI System'.
Command (m for help): n
Partition number (2-128, default 2):
First sector (2099200-80003038, default 2099200):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-80003038, default 80001023):
Created a new partition 2 of type 'Linux filesystem' and of size 37.1 GiB.
Partition #2 contains a ext4 signature.
Do you want to remove the signature? [Y]es/[N]o: Y
The signature will be removed by a write command.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
fdisk -l
Disk /dev/sda: 38.15 GiB, 40961572864 bytes, 80003072 sectors
Disk model: QEMU HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 29add4d7-e644-4709-9b3c-209b68765ff2
Device Start End Sectors Size Type
/dev/sda1 2048 2099199 2097152 1G EFI System
/dev/sda2 2099200 80001023 77901824 37.1G Linux filesystem
Create required filesystems, deploy the rootfs and mount everything that wee need for chroot:
mkfs.vfat /dev/sda1
mkfs.ext4 /dev/sda2
mount /dev/sda2 /mnt
wget https://repo-default.voidlinux.org/live/20240314/void-aarch64-ROOTFS-20240314.tar.xz
tar xvf void-aarch64-ROOTFS-*.tar.xz -C /mnt
mkdir /mnt/boot/efi
mount /dev/sda1 /mnt/boot/efi
mount -t proc none /mnt/proc
mount -t sysfs none /mnt/sys
mount --rbind /dev /mnt/dev
mount --rbind /run /mnt/run
mount -t efivarfs /sys/firmware/efi/efivars /mnt/sys/firmware/efi/efivars
To proceed make sure the DNS is set inside mounted environment. Also copy ip configuration for later.
resolvectl | grep "::" | head -n 1 | awk '{print "nameserver " $4}' > /mnt/etc/resolv.conf
echo "/usr/bin/ip link set eth0 up" >> /mnt/etc/rc.local
echo "/usr/bin/ip -6 a a $(ip a s | grep "::1\/64" | awk '{print $2}') dev eth0" >> /mnt/etc/rc.local
echo "/usr/bin/ip -6 r a $(ip -6 r s | grep "default via" | awk '{print $3}')/64 dev eth0" >> /mnt/etc/rc.local
echo "/usr/bin/ip -6 r a default via $(ip -6 r s | grep "default via" | awk '{print $3}') dev eth0" >> /mnt/etc/rc.local
Now chroot into /mnt and proceed with all the installation steps:
chroot /mnt
echo "example" > /etc/hostname
sed -i 's/localhost.localdomain/example/g;' /etc/hosts
passwd
echo "Europe/Warsaw" > /etc/timezone
ln -s /usr/share/zoneinfo/Europe/Warsaw /etc/localtime
xbps-install -Su xbps
xbps-install -u
xbps-install base-system
xbps-remove base-voidstrap
xbps-install grub-arm64-efi linux linux-base linux-headers linux-tools
grub-install /dev/sda
cp -r /boot/efi/EFI/void /boot/efi/EFI/boot
mv /boot/efi/EFI/boot/grubaa64.efi /boot/efi/EFI/boot/bootx64.efi
xbps-reconfigure -fa
ln -s /etc/sv/sshd /etc/runit/runsvdir/default/
sed -i 's/#ListenAddress ::/ListenAddress ::/g;' /etc/ssh/sshd_config
sed -i 's/loglevel=4/loglevel=7 console=tty1/g;' /etc/default/grub
update-grub
exit
cp -r /root/.ssh /mnt/root/
rm -f /mnt/root/.ssh/authorized_keys2
umount -R /mnt # you can safely ignore errors about /dev/pts being in-use
reboot
xbps-install chrony
sed -i 's/pool.ntp.org/2.pool.ntp.org/g;' /etc/chrony.conf
sed -i 's/exec chronyd/exec chronyd -6/g;' /etc/sv/chronyd/run
ln -s /etc/sv/chronyd /etc/runit/runsvdir/default/
xbps-install -S bash-completion bind-utils binutils curl cronie git htop iftop logrotate lsof lynis nano ncdu neofetch net-tools mc mtr unzip rsync rsyslog screen tcpdump vim wget whois xz
xbps-install autoconf autogen automake bc binutils bison cargo cmake flex gcc gettext libtool m4 make patch pkg-config unzip xz
xbps-install ncurses-devel openssl-devel pcre-devel pcre2-devel
ln -s /etc/sv/rsyslogd /var/service
ln -s /etc/sv/cronie /var/service
cp /etc/skel/.* ./
chsh -s /bin/bash root
bash
For use-cases where a specific domain doesn't resolve on IPv6 (f.e. github.com) we can use a public NAT64 service. However, we don't necessarily want all traffic being routed through that service. Therfore, we're using dnsmasq to use NAT64 only for domains we need.
xbps-install dnsmasq
cat <<EOF > /etc/dnsmasq.conf
proxy-dnssec
no-resolv
no-poll
listen-address=::1
bind-interfaces
no-hosts
# Default DNS: quad9
server=2620:fe::fe
server=2620:fe::9
# Alternative DNS: https://meta.wikimedia.org/wiki/Wikimedia_DNS
server=2001:67c:930::1
# For specific hosts use Public NAT64 service: https://nat64.net/
server=/github.com/2a00:1098:2c::1
server=/github.com/2a01:4f9:c010:3f02::1
server=/github.com/2a01:4f8:c2c:123f::1
server=/api.github.com/2a00:1098:2c::1
server=/api.github.com/2a01:4f9:c010:3f02::1
server=/api.github.com/2a01:4f8:c2c:123f::1
server=/objects.githubusercontent.com/2a00:1098:2c::1
server=/objects.githubusercontent.com/2a01:4f9:c010:3f02::1
server=/objects.githubusercontent.com/2a01:4f8:c2c:123f::1
EOF
Be aware that the public NAT64 solution isn't perfect and may slow down or not work as expected at times due to rate limiting. As an alternative, you can look for IPv6 proxy services, but be careful because they can be used for MITM attacks.
ln -s /etc/sv/dnsmasq /var/service
echo "nameserver ::1" > /etc/resolv.conf
Public NAT64 services are listed here: https://nat64.net/public-providers and here: https://nat64.xyz/
xbps-install docker docker-compose
ln -s /etc/sv/docker /var/service
See also: Docker inside IPv6-only host
xbps-install openjdk17-jre
wget https://download.getbukkit.org/spigot/spigot-1.20.4.jar
java -Xms1G -Xmx1G -XX:+UseG1GC -Djava.net.preferIPv6Addresses=true -jar spigot-1.20.4.jar --nogui
Consider paranoya - Simple IOC and YARA scanner
If you're looking for more info, head over to https://docs.voidlinux.org/.
If you found this article helpful, please consider making a donation to a charity on my behalf. Thank you.
Enjoy your Void Linux adventure!
Thank you so much for this comprehensive tutorial.