Skip to content

Instantly share code, notes, and snippets.

@strfry
Last active Apr 30, 2022
Embed
What would you like to do?
Deploys FreeBSD on a Hetzner cloud server

Deploys FreeBSD on a Hetzner cloud server

Prerequisites:

  • A Hetzner Cloud API Token (Pass in via APIKEY)
  • jq
  • sshpass

Usage

Replace server_id variable with your hetzner server Id

./hetzner.sh # Boot into rescue mode and install FreeBSD
./hetzner.sh poweron|poweroff|reboot # Various control commands
./hetzner.sh ip # get public v4 IP

Backlog

  • Implement creating a server...
DISTRIBUTIONS="kernel.txz base.txz"
ZFSBOOT_DISKS="da0"
ZFSBOOT_VDEV_TYPE=stripe
export nonInteractive="YES"
RELEASE="12.0-RELEASE"
BSDINSTALL_DISTSITE=http://ftp.de.freebsd.org/pub/FreeBSD/releases/amd64/$RELEASE
BSDINSTALL_DISTDIR=/var/cache/pkg
# bsdinstall's distfetch doesn't like being run in a non-interactive environment, so we do it's job by hand
cd $BSDINSTALL_DISTDIR
for dist in $DISTRIBUTIONS ; do
echo "Downloading $dist..." # Impatience ruined my install several times
ftp $BSDINSTALL_DISTSITE/$dist
done
cd -
export SSHKEY="$(cat /root/.ssh/authorized_keys)"
#!/bin/sh
sysrc ifconfig_vtnet0=DHCP
sysrc sshd_enable=YES
# Copy over key used for install...
mkdir -p /root/.ssh/
echo "$SSHKEY" > /root/.ssh/authorized_keys
sed -i '' 's/.*PermitRootLogin.*/PermitRootLogin without-password/g' /etc/ssh/sshd_config
echo 'beastie_disable="YES"' >> /boot/loader.conf
#!/bin/sh
# hetzner.sh - Deploy a FreeBSD installation through the Hetzner Cloud API
# Usage: ./hetzner.sh # Just breaks a fixed server
# Backlog/TODO:
# - Creating the server / means to pass the ID
# - Command line argument support (serverid, apikey, ...)
# - Print summary at the end, with information like IP address, host key etc.
# - IPv6 Configuration
# - Copy rescue image's ssh host key to installation???
# - self-test, check for utilities
# - error handling/trap in general
if [ -s apikey ]; then
APIKEY="$(cat apikey)"
fi
if [ -z $APIKEY ]; then
echo Set APIKEY via environment variable, or write to file 'apikey'
exit
fi
# TODO: Check for authentication success
APIURL='https://api.hetzner.cloud/v1'
server_id=3294474 #TODO
request() {
url="$APIURL/$1"
shift
curl -s -H "Authorization: Bearer $APIKEY" \
-H "Content-Type: application/json" \
"$url" $@
}
post() {
request $@ -X POST
}
create_server() {
post
}
enable_rescue() {
root_pw=$(request servers/$server_id/actions/enable_rescue -d '{"type":"freebsd64"}' | jq -r .root_password)
echo root pw: $root_pw
}
disable_rescue() {
post servers/$server_id/actions/disable_rescue
}
get_ipv4() {
request servers/$server_id | jq -r .server.public_net.ipv4.ip
}
ssh_target=root@"$(get_ipv4)"
ssh_options="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
scp_target=$ssh_target:
DISTSITE=ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/12.0-RELEASE
bsdinstall() {
ssh $ssh_options $ssh_target bsdinstall -D install.log script bsdinstall.script
}
command() {
case $1 in
poweron)
post servers/$server_id/actions/poweron
;;
poweroff)
post servers/$server_id/actions/poweroff
;;
reboot)
post servers/$server_id/actions/reboot
;;
info)
request servers/$server_id
;;
ip)
get_ipv4
;;
ssh)
ssh $ssh_target
;;
bsdinstall)
bsdinstall
;;
servers)
request servers
;;
enable_rescue)
enable_rescue
;;
*)
echo error Unknown Command $1!
exit
;;
esac
sleep 2 # Workaround for 'locking' commands
}
# Allow a bunch of explicit commands
if [ -n "$1" ]; then
command $1
exit
fi
command poweroff
enable_rescue
command poweron
echo "Waiting for rescue boot to come up..."
sleep 250
for i in 1 2 3; do
sshpass -p $root_pw ssh-copy-id $ssh_options $ssh_target && break
s=$(expr 30 '*' $i)
echo "SSH failed, retrying in $s seconds..."
sleep $s
done
scp $ssh_options bsdinstall.script "$scp_target"
bsdinstall
ssh $ssh_options "$ssh_target" poweroff
disable_rescue
sleep 10
post servers/$server_id/actions/reboot
# TODO: Print summary
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment