add packer for hetzner image

This commit is contained in:
vincent 2024-01-28 09:31:39 +01:00
parent 394dbaf6cb
commit abc88f0074
13 changed files with 954 additions and 0 deletions

155
packer/README.md Normal file
View File

@ -0,0 +1,155 @@
# hcloud-packer-templates
This repo is used to build linux images (as snapshots) for use with
[Hetzner Cloud](https://www.hetzner.de/cloud) by means of HashiCorp's
[Packer](https://packer.io/).
Templates for the following distros are currently provided:
- archlinux
- nixos
I recommend the use of Hetzner's
[hcloud](https://github.com/hetznercloud/cli/tree/master/cli) command
line tool to manage the resulting images. Hetzner also provides a dedicated
[Terraform Provider](https://www.terraform.io/docs/providers/hcloud/index.html)
that you can use to build servers from these images. Please note that
your images cannot yet be (easily) exported from Hetzner's Cloud.
## Building Images using this Repo
Please ensure that you have done the following:
- installed `packer` on your development machine
- set the `HCLOUD_TOKEN` environment variable to your API token
- reviewed/overriden the templates' variables (as necessary)
### Getting Started
To build VM images:
- `$ packer build templates/archlinux.pkr.hcl`
- `$ packer build templates/nixos.pkr.hcl`
To view info about past builds:
- `$ less packer-manifest.json`
To debug a build:
- `$ packer build -debug -on-error=ask packer/nixos.pkr.hcl`
- `$ ssh -F/dev/null -i ssh_key_hcloud.pem root@XXX.XXX.XXX.XXX -o StrictHostKeyChecking=no`
### Internals
The resulting images are intended to support a Terraform-based (or
custom) workflow that feels close to the one of native Hetzner VMs.
Hetzner's server infrastructure (mirrors, repos, DNS, NTP, DHCP) and
configuration endpoints are used where possible. This necessarily
involves some analysis of their (partially undocumented) setups and
translations of these to our images, so this may become outdated, may
break, or may not work completely as expected. Error handling is also
pretty bare-bones.
In particular, support for the following features available on
standard Hetzner VMs is desired:
- dynamic hostname
- dynamic root ssh keys
- free-form cloud-init userdata
- full IPv6/IPv4 support
- Hetzner Cloud Networks
- Hetzner Cloud Volumes
The following features are notably unsupported:
- dynamic initial root passwords (please prefer ssh keys)
- automatic server resizing (use rescue mode, or a new server)
A general problem is that much of the data necessary for the features
in the lists above is only allocated after a server is instantiated
from a given image and thus can't be taken into account at image
built-time. Hetzer VMs use an hcloud-specific `cloud-init` provider
for this initialization after their instantiation.
However, the current state of `cloud-init` on Archlinux is less than
ideal, and NixOS has a workflow that's not really compatible. Thus,
these images instead use `hcloud-dl-metadata.service`, which
aggregates and outputs the data normally available to Hetzner VMs to
`/etc/hcloud-metadata.json`, which can then be used in further
distro-specific mechanisms (or directly by you).
Finally, your custom `cloud-init` userdata, which the Hetzner VMs
happen to treat as an execute-on-boot script, is instead handled by
`hcloud-dl-userdata.service`, which only transcribes it into
`/etc/hcloud-userdata` and nothing else.
#### Archlinux
Archlinux images use the file `/etc/hcloud-metadata.json` to drive a
few systemd services, which in turn implement the dynamic features
mentioned above:
- hcloud-hostname.service (sets hostname)
- hcloud-network.service (configures primary and attached networks)
- hcloud-ssh-keys.service (sets ssh root keys)
Any further configuration is up to your provisioning tool.
#### NixOS
NixOS images export the metadata from `/etc/hcloud-metadata.json` as
the `config.hcloud.*` hierarchy. Since not all `config.hcloud.*` data
is known at snapshop build-time, the system configuration is initially
partially stubbed out at built-time, and the freshly instantiated
server runs `nix-channel --update` and `nixos-rebuild` after
`hcloud-dl-metadata.service` has finished.
The dynamic features mentioned above are implemented with a few nix
expressions in `/etc/nixos/` using these `config.hcloud.*`
attributes. These settings use the `mkDefaultOption` mechanism, so
you're free to override them as you see fit.
In general, you can provide the `nix-config-path` packer variable to
point to a directory of nix expression and other data, like the one
you would place in `/etc/nixos`, which is then baked into the built
image. Note that the whole directory is included in this, including
any `.git/` folder and other data, and that it uses the file
`configuration.nix` as its entrypoint. You do not need to manage
`hardware-configuration.nix` here.
This `nix-config-path` mechanism allows both small customizations to
the barebones image (producing images primarily intended for
additional provisioning), while also enabling fully baked system
images (for rapid deployment / autoscaling).
It is planned to transition some or all of the above NixOS workflow
to use flakes instead, but this isn't implemented yet.
### Known Issues
- The upstream archlinux bootstrap image's filename is derived from
its release day. I know of no good way to automatically get this
date. Set `-var arch-image=archlinux-bootstrap-20XX.XX.XX-x86_64.tar.gz`
if your builds are failing because of this issue.
- Verifying the archlinux bootstrap image is relatively complex due to
the trust setup the archlinux team uses. We don't properly derive
developer key trust from the master key(s), but instead pin the key of
the developer that usually signs the releases.
## GPG Keys
The upstream for the GPG keys used by the installation scripts can be
found on these pages:
- Archlinux: https://www.archlinux.org/master-keys/
- Nixos: https://nixos.org/nix/download.html
## License
You can redistribute and/or modify these files unter the terms of the
GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any
later version. See the LICENSE file for details.

106
packer/archlinux.pkr.hcl Normal file
View File

@ -0,0 +1,106 @@
variable "extra-packages" {
type = list(string)
default = []
}
variable "hcloud-servertype" {
type = string
default = "cx11"
}
variable "hcloud-token" {
type = string
default = "${env("HCLOUD_TOKEN")}"
sensitive = true
}
variable "system-keymap" {
type = string
default = "fr"
}
variable "system-locale" {
type = string
default = "fr_FR.UTF-8"
}
variable "system-timezone" {
type = string
default = "UTC"
}
locals {
arch-release = "${ legacy_isotime("2006-01") }-01"
build-id = "${ uuidv4() }"
build-labels = {
os-flavor = "archlinux"
"archlinux/iso.release" = "${ local.arch-release }"
"packer.io/build.id" = "${ local.build-id }"
"packer.io/build.time" = "{{ timestamp }}"
"packer.io/version" = "{{ packer_version }}"
}
}
packer {
required_plugins {
hcloud = {
version = ">= 1.1.1"
source = "github.com/hetznercloud/hcloud"
}
}
}
source "hcloud" "archlinux" {
server_type = "${ var.hcloud-servertype }"
image = "debian-11"
#image_filter = {
# with_selector = [ "os_flavor=debian" ]
# most_recent = true
#}
rescue = "linux64"
location = "hel1"
snapshot_name = "archlinux-{{ timestamp }}"
snapshot_labels = local.build-labels
ssh_username = "root"
token = "${ var.hcloud-token }"
}
build {
sources = [ "source.hcloud.archlinux" ]
provisioner "shell" {
script = "files/filesystem.sh"
environment_vars = [ "LABEL=${local.build-id}" ]
}
provisioner "file" {
destination = "/mnt/"
source = "files/archlinux/root/"
}
provisioner "file" {
destination = "/tmp/key-${local.build-id}.gpg"
source = "files/archlinux/key.gpg"
}
provisioner "shell" {
inline = [
"gpg --batch --import /tmp/key-${local.build-id}.gpg",
"chmod --recursive u=rwX,g=rX,o=rX /mnt",
"chmod --recursive u=rwx,g=rx,o=rx /mnt/usr/local/bin/*",
]
}
provisioner "shell" {
script = "files/archlinux/install.sh"
environment_vars = [
"ARCH_RELEASE=${local.arch-release}",
"EXTRA_PACKAGES=${join(" ", var.extra-packages)}",
"KEYMAP=${var.system-keymap}",
"LOCALE=${var.system-locale}",
"TIMEZONE=${var.system-timezone}",
]
}
post-processor "manifest" {
custom_data = local.build-labels
}
}

View File

@ -0,0 +1,88 @@
#!/bin/bash
# required env:
# - ARCH_RELEASE
# - KEYMAP
# - LOCALE
# - TIMEZONE
#
# optional env
# - EXTRA_PACKAGES
set -euo pipefail
readonly ARCH_MIRROR='https://mirror.hetzner.de/archlinux'
readonly ARCH_ISO="archlinux-bootstrap-${ARCH_RELEASE//-/.}-x86_64.tar.gz"
# obtain arch tools
curl --fail -o "${ARCH_ISO}" "${ARCH_MIRROR}/iso/${ARCH_RELEASE//-/.}/${ARCH_ISO}"
curl --fail -o "${ARCH_ISO}.sig" "${ARCH_MIRROR}/iso/${ARCH_RELEASE//-/.}/${ARCH_ISO}.sig"
gpg --verify "./${ARCH_ISO}.sig" "./${ARCH_ISO}"
tar xzf "./${ARCH_ISO}"
rm "./${ARCH_ISO}" # save ramfs memory
# prepare mounts
readonly iso='/root/root.x86_64'
mount --bind "$iso" "$iso" # XXX arch-chroot needs / to be a mountpoint
mount --bind /mnt "$iso/mnt"
# install base
"${iso}/bin/arch-chroot" "$iso" <<EOF
set -euo pipefail
# pacstrap
echo 'Server = ${ARCH_MIRROR}/\$repo/os/\$arch' > /etc/pacman.d/mirrorlist
pacman-key --init
pacman-key --populate archlinux
pacstrap /mnt base linux grub nano btrfs-progs openssh curl jq python-yaml $EXTRA_PACKAGES
# fstab
genfstab -U /mnt > /mnt/etc/fstab
echo 'proc /proc proc defaults,hidepid=2 0 0' >> /mnt/etc/fstab
EOF
# configure base
"${iso}/bin/arch-chroot" /mnt <<EOF
set -euo pipefail
# time
systemctl enable systemd-timesyncd
ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime
hwclock --systohc
# locale
echo 'KEYMAP=${KEYMAP}' > /etc/vconsole.conf
echo '${LOCALE} UTF-8' > /etc/locale.gen
echo 'LANG=${LOCALE}' > /etc/locale.conf
locale-gen
# network
mkdir -p /root/.ssh/
systemctl enable systemd-networkd systemd-resolved sshd
cat > /etc/systemd/network/default.network <<EOF2
[Match]
Name=en*
[Network]
DHCP=yes
EOF2
# grub
grub-install /dev/sda
grub-mkconfig -o /boot/grub/grub.cfg /dev/sda
# hcloud
# these services were uploaded by packer beforehand
for i in /etc/systemd/system/hcloud*.service; do
systemctl enable "\$i"
done
# misc
systemctl set-default multi-user.target
usermod -L root
echo 'archlinux' > /etc/hostname
EOF
# clean up
rm /mnt/root/.bash_history
rm -r /mnt/var/cache/*

View File

@ -0,0 +1,445 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
WeUKJv3rncMRi7cKEJ/oNJQ+RaaTDyC0yBv/////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
/////////////////////////////////4kCHAQQAQIABgUCU0UnugAKCRCHLmcU
6vXsRHSTD/0fR8mHPnnQo+Psl2Iyr4CX1HyS+9ReheyXKN/MJ4pME1NGL4vYqAbN
ZI0ihpnKwb6MHwU0snq8C8U/Iv2GMa9H5C01z847zoiN4xmnxXCZmUaBgBiKrER4
M6sdHqQGHX4LfnRGZ4P5vICKOOTJYI9JtaHefHZSrfq0FsOwOB9I/G2ubCMiOFhS
IzzADmUoHD6t7eY62d9La+iXO40D92ZlnFzOCewYXMsXQzM7lz+LyF6lnu5FP6Gy
gVeQzCXryq9x0Ip3AJOa+0DZ1H02P6XiJgaN0RvAEyqMbKDcJb9uSmuA9IUjNpP+
3Ti4sq+XqiCyTjwHpaqmzDT48CXpMIV0Jeg+jl57k2oXvwu/UIz2NFRluvt727Ao
FRNpSramobtYqEQZF2QHy43S3VC5UoFPvrEUZQjTw+k1QTh9YZBuRypqxHPMJJrM
dpUeJo3J8KsAjc3hja5bgAshCAv+UXtK5ZkyoQ6AuCwVVtXoRLTNUGWmGU4i1NcH
i6c9Qo3aCssT6pFYavRsV6ZeM6FsHq6vO8+lN8LIhulsGsAod5EO5c+Bgb4VMOqT
S2Ki11hA+M/+O/BO2WR/dR/J0H+CAu2X5PzsI5E6xDUsSuOLDlsgFiG3MLvZDF5d
UERDsPa65eqcLXU26KoQJhPq1YFlTLGxenmbo93XnzQACB/1+FW8a4kCHAQQAQgA
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
ggc=
=xOvq
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -0,0 +1,14 @@
[Unit]
Description=Download cloud-init & hetzner network metadata
Requires=network-online.target
After=network-online.target
ConditionPathExists=!/etc/hcloud-metadata.json
[Service]
Type=oneshot
DynamicUser=yes
ExecStart=/usr/local/bin/hcloud-metadata
StandardOutput=truncate:/etc/hcloud-metadata.json
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=Download cloud-init userdata
Requires=network-online.target
After=network-online.target
ConditionPathExists=!/etc/hcloud-userdata
[Service]
Type=oneshot
DynamicUser=yes
ExecStart=/usr/bin/curl --fail -s http://169.254.169.254/hetzner/v1/userdata
StandardOutput=truncate:/etc/hcloud-userdata
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,11 @@
[Unit]
Description=Import hcloud hostname
Requires=hcloud-dl-metadata.service
After=hcloud-dl-metadata.service
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'hostnamectl set-hostname $(jq -r .hostname /etc/hcloud-metadata.json)'
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=Import hcloud network config
Requires=hcloud-dl-metadata.service
After=hcloud-dl-metadata.service
ConditionPathExists=/etc/systemd/network/default.network
[Service]
Type=oneshot
DynamicUser=yes
ExecStart=/usr/local/bin/hcloud-network
StandardOutput=truncate:/etc/systemd/network/default.network
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=Import hcloud root ssh keys
Requires=hcloud-dl-metadata.service
After=hcloud-dl-metadata.service
ConditionPathExists=!/root/.ssh/authorized_keys
[Service]
Type=oneshot
DynamicUser=yes
ExecStart=/usr/bin/jq -r '.ssh_keys|join("\n")' /etc/hcloud-metadata.json
StandardOutput=truncate:/root/.ssh/authorized_keys
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1 @@
../../../../../hcloud-metadata

View File

@ -0,0 +1,29 @@
#!/bin/bash
readonly f=/etc/hcloud-metadata.json
cat <<EOF
[Match]
MACAddress=$(jq -r '.network.mac_address' "$f")
[Network]
Address=$(jq -r '.network.ipv6_address' "$f")/64
Gateway=$(jq -r '.network.ipv4_gateway' "$f")
Gateway=$(jq -r '.network.ipv6_gateway' "$f")
NTP=ntp1.hetzner.de
NTP=ntp2.hetzner.com
NTP=ntp3.hetzner.net
EOF
for nameserver in $(jq -r '.network.nameservers|join(" ")' "$f"); do
echo "DNS=${nameserver}"
done
cat <<EOF
[Address]
Address=$(jq -r '.network.ipv4_address' "$f")/32
Peer=172.31.1.1/32
EOF

View File

@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
# required env
# - LABEL filestystem label
# partitions
dd if=/dev/zero of=/dev/sda bs=1MiB count=1 status=none
xargs -L1 parted --script /dev/sda -- <<EOF
mklabel msdos
mkpart primary linux-swap 1MiB 2GiB
mkpart primary btrfs 2GiB 100%
set 1 boot on
EOF
# filesystems
mkswap -L swap /dev/sda1
swapon /dev/sda1
mkfs.btrfs --force --label "${LABEL}" /dev/sda2
mount -o compress=lzo,commit=90,autodefrag /dev/sda2 /mnt

42
packer/files/hcloud-metadata Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import yaml, json, sys
from collections import defaultdict
from urllib.request import urlopen
metadata = 'http://169.254.169.254/hetzner/v1/metadata'
networks = 'http://169.254.169.254/hetzner/v1/metadata/private-networks'
with urlopen(metadata) as metadata:
metadata = yaml.safe_load(metadata)
metadata_net = metadata['network-config']['config'][0]
with urlopen(networks) as networks:
networks = yaml.safe_load(networks)
data = {
'network': {
'nameservers': [],
'mac_address': metadata_net['mac_address'],
'ipv4_address': metadata['public-ipv4'],
'ipv4_subnet': metadata['public-ipv4'] + '/32',
'ipv4_gateway': '172.31.1.1',
'private': networks,
},
'hostname': metadata['hostname'],
'instance_id': metadata['instance-id'],
'ssh_keys': [x.rstrip() for x in metadata['public-keys']],
}
for subnet in metadata_net['subnets']:
if 'dns_nameservers' in subnet:
data['network']['nameservers'] += subnet['dns_nameservers']
if 'ipv6' in subnet:
if 'address' in subnet:
data['network']['ipv6_subnet'] = subnet['address']
data['network']['ipv6_address'] = subnet['address'][:-3]
if 'gateway' in subnet:
data['network']['ipv6_gateway'] = subnet['gateway']
json.dump(data, sys.stdout)