Skip to content

Instantly share code, notes, and snippets.

@c0m4r
Last active September 29, 2024 07:26
Show Gist options
  • Save c0m4r/e38d41d0e31f6adda4b4c5a88ba0a453 to your computer and use it in GitHub Desktop.
Save c0m4r/e38d41d0e31f6adda4b4c5a88ba0a453 to your computer and use it in GitHub Desktop.
Alpine Linux installation on Hetzner Cloud VPS (Arm64/IPv6-only)

Alpine Linux installation on Hetzner Cloud VPS

(Arm64/IPv6-only)

This guide describes how to install Alpine Linux on Hetzner Cloud VPS CAX ARM64 Servers with IPv6-only setup.

image

Table of contents:

Introduction

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 knew I can just write an ISO to the disk and reboot, it will boot to RAM so I can just go on with the setup. But surprisingly under EFI I wasn't able to choose a block device during installation as I always did. After some trials and errors I eventually found this: https://lemonsh.moe/blog/alpine-hetzner/. Thanks to this recipe I successfully installed Alpine on my server.

Installation

Reboot into Rescue

Get the latest VIRTUAL/aarch64 https://alpinelinux.org/downloads/ ISO

Log into the rescue SSH and write the ISO to the disk:

ssh root@xxxx:xxxx:xxxx:xxxx::1
wipefs -a /dev/sda
wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.0-aarch64.iso
dd if=alpine-virt-3.19.0-aarch64.iso of=/dev/sda
reboot

Open the server Console, log into root (no password required) and proceed with:

cp -r /.modloop /root
cp -r /media/sda /root
umount /.modloop /media/sda
rm /lib/modules
mv /root/.modloop/modules /lib
mv /root/sda /media
setup-alpine
  • For the DHCP part set: none
  • Confirm that you want to add network configuration manually
  • DNS domain: leave empty
  • DNS server: 2606:4700:4700::1111
  • Choose the drive: sda
  • Installation type: sys or lvmsys depending on wheter you want to use lvm or not

Post-installation

Packages

Make sure both main and community are enabled in /etc/apk/repositories:

http://dl-cdn.alpinelinux.org/alpine/v3.19/main
http://dl-cdn.alpinelinux.org/alpine/v3.19/community

To install some useful tools:

apk add bind-tools binutils curl git htop iptraf-ng lsb-release-minimal lsof mc mtr nano ncdu neofetch procps p7zip rsync shadow screen sudo tcpdump unzip util-linux-misc vnstat vim wget whois xz

Network

Network configuration /etc/network//interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet6 static
	address xxxx:xxxx:xxxx:xxxx::1/64
	gateway fe80::1

SSH

sed -i 's/#ListenAddress ::/ListenAddress ::/g;' /etc/ssh/sshd_config
rc-service sshd restart

DNS

https://1.1.1.1/dns/

echo "nameserver 2606:4700:4700::1111" > /etc/resolv.conf
echo "nameserver 2606:4700:4700::1001" >> /etc/resolv.conf

Grub

Because of the EFI bullshit you'll not be able to see anything on the Console most of the time.

To get the console showing logs during boot you need to add console=tty1 boot parameter.

Edit /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="console=tty1 modules=sd-mod,usb-storage,ext4 rootfstype=ext4"

And then update grub:

update-grub

NTP

apk add chrony

Edit /etc/chrony/chrony.conf

Change pool.ntp.org to 2.pool.ntp.org (the only one that resolves with IPv6). Let's also make sure that chronyd starts fast and with only IPv6 enabled.

sed -i 's/ pool.ntp.org/ 2.pool.ntp.org/g;' /etc/chrony/chrony.conf
sed -i 's/FAST_STARTUP=no/FAST_STARTUP=yes/g;' /etc/conf.d/chronyd
sed -i 's/ARGS=""/ARGS="-6"/g;' /etc/conf.d/chronyd
rc-service chronyd restart

Wait a few seconds and check if sources appear:

chronyc sources

Bash

apk add bash bash-completion
chsh -s /bin/bash root
echo "PS1='[\u@\[\033[01;34m\]\h\[\033[00m\]:\w]\\$ '" > .bash_profile

fail2ban

apk add fail2ban
sed -i 's/#allowipv6/allowipv6/g;' /etc/fail2ban/fail2ban.conf
rc-update add fail2ban
rc-service fail2ban start

Default SSH jails: /etc/fail2ban/jail.d/alpine-ssh.conf

ip6tables

/etc/init.d/ip6tables save
rc-update add ip6tables
rc-service ip6tables start

Rules are being saved into /etc/iptables/rules6-save

Check out some good examples here: https://gist.github.com/jirutka/3742890#file-rules-ipv6-ip6tables

NAT64

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.

apk add dnsmasq
cat <<EOF > /etc/dnsmasq.d/nat64.conf
proxy-dnssec
no-resolv
no-poll
listen-address=::1
bind-interfaces
no-hosts
# Default DNS: cloudflare
server=2606:4700:4700::1111
server=2606:4700:4700::1001
# For specific hosts use Public NAT64 service: https://nat64.net/
server=/github.com/2a00:1098:2c::1
server=/github.com/2a00:1098:2b::1
server=/github.com/2a01:4f8:c2c:123f::1
server=/api.github.com/2a00:1098:2c::1
server=/api.github.com/2a00:1098:2b::1
server=/api.github.com/2a01:4f8:c2c:123f::1
server=/objects.githubusercontent.com/2a00:1098:2c::1
server=/objects.githubusercontent.com/2a00:1098:2b::1
server=/objects.githubusercontent.com/2a01:4f8:c2c:123f::1
EOF
rc-update add dnsmasq
rc-service dnsmasq start
echo "nameserver ::1" > /etc/resolv.conf

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 give the via-IPv6 service a try.

Custom stuff

Build essentials

apk add autoconf automake bison binutils build-base cargo cmake flex libtool m4 patch pkgconfig

Docker

apk add docker docker-cli-compose
rc-update add docker
rc-service docker start

See also: Docker inside IPv6-only host

nginx

OpenVPN

https://wiki.alpinelinux.org/wiki/Setting_up_a_OpenVPN_server

PHP

apk add php83-curl php83-ctype php83-fpm php83-gd php83-iconv php83-mbstring php83-openssl php83-phar

Example configuration for nginx + PHP-FPM:

cat <<EOF > /etc/php83/php-fpm.d/nginx.conf
[nginx]
user = nginx
group = nginx
listen = /run/php-fpm83/php-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
EOF
rc-service php-fpm83 restart

Check installed modules:

php83 -m

Minecraft Server

apk add openjdk21-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

safe-rm

https://launchpad.net/safe-rm

Safe-rm is a safety tool intended to prevent the accidental deletion of important files by placing a wrapper on top of /bin/rm, which checks the given arguments against a configurable list of exclusions for files and directories that should never be removed.

rm -rf /
safe-rm: Skipping /.

Keep in mind that we're not replacing /bin/rm by itself. We're just pointing to a different binary in /usr/local/bin. This way we will keep /bin/rm from coreutils so it can be updated or used to bypass safe-rm. You can also decide wheter you want to use /bin/rm or /bin/safe-rm depending on your use-case.

apk add cargo make wget
wget https://launchpad.net/safe-rm/trunk/1.1.0/+download/safe-rm-1.1.0.tar.gz
tar -xvf safe-rm-1.1.0.tar.gz
cd safe-rm-1.1.0
make
sudo cp target/release/safe-rm /usr/bin/
# Deploy example config
sudo echo "/" | sudo tee /etc/safe-rm.conf
sudo ls -1 / | sed 's/^/\//g;' | sudo tee -a /etc/safe-rm.conf
sudo ln -s /usr/bin/safe-rm /usr/local/bin/rm

OSSEC

apk add linux-headers bsd-compat-headers openssl-dev libevent-dev inotify-tools-dev findutils file zlib-dev pcre2-dev geoip-dev
wget https://github.com/ossec/ossec-hids/archive/3.7.0.tar.gz
tar xvf 3.7.0.tar.gz
cd ossec-hids-3.7.0
wget -O musl_lack_of_a_out_h.patch https://git.alpinelinux.org/aports/plain/community/ossec-hids-agent/musl_lack_of_a_out_h.patch?h=3.19-stable
patch src/rootcheck/os_string.c < musl_lack_of_a_out_h.patch
mkdir src/headers/asm
wget -O src/headers/asm/a.out.h https://raw.githubusercontent.com/torvalds/linux/master/arch/x86/include/uapi/asm/a.out.h
wget -O src/headers/a.out.h https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/a.out.h
USE_GEOIP=yes USE_INOTIFY=yes USE_SYSTEMD=no ./install.sh
/var/ossec/bin/ossec-control stop
rc-update add ossec
rc-service ossec start
ln -s /var/ossec/logs /var/log/ossec
ln -s /var/ossec/etc /etc/ossec

False positives

In Alpine many system utilities like ls, ps, df etc. are busybox symlinks. This will trigger many false positives due to the /var/ossec/etc/shared/rootkit_trojans.txt entries. You can either comment out individual lines or disable this check in /var/ossec/etc/ossec.conf.

  <rootcheck>
    <!--<rootkit_trojans>/var/ossec/etc/shared/rootkit_trojans.txt</rootkit_trojans>-->

Anti-malware

Consider paranoya - Simple IOC and YARA scanner

GRUB Fallout Theme

https://github.com/shvchk/fallout-grub-theme

image

mkdir /usr/share/grub/themes
cd /usr/share/grub/themes
git clone https://github.com/shvchk/fallout-grub-theme.git
rm /usr/share/grub/themes/fallout-grub-theme/install.sh
cd /usr/share/grub/themes/fallout-grub-theme/icons
echo -n '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' \
  | base64 -d > alpine.png
mv gnu-linux.png gnu-linux.png.bak
ln -s alpine.png gnu-linux.png
echo "GRUB_THEME=/usr/share/grub/themes/fallout-grub-theme/theme.txt" >> /etc/default/grub
update-grub

icon mirror1 mirror2 mirror3

Coreutils

If you're missing some core utils functionality, which in Alpine are provided by busybox, you can install coreutils.

However, some commands like w, last, who won't work by default

https://wiki.alpinelinux.org/wiki/GNU_core_utilities

There's a way to workaround this:

apk add coreutils utmps
setup-utmp

which will make last and who work, but w will be still unusable. To fix that, consider using:

alias w='uptime && who -a'

For bash simply add it to ~/.bash_profile


If you found this article helpful, please consider making a donation to a charity on my behalf. Thank you.

Enojy your Alpine Linux adventure!

Image source: Mont Blanc oct 2004.JPG

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment