Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/build-netbsd10.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Build NetBSD 10.1 Vagrant Box

on:
workflow_dispatch:
push:
paths:
- 'netbsd10/**'
- '.github/workflows/build-netbsd10.yml'

jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: netbsd10

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2

- name: Install dependencies
run: |
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt-get update && sudo apt-get install -y qemu-system-x86 qemu-utils vagrant packer hcp python3 python3-pexpect genisoimage
sudo chmod 666 /dev/kvm
# Anita's pypi namespace is owned by an unrelated package, and its
# setup.py uses distutils (gone in py3.12). Clone and fix the py2
# shebang instead of pip-installing.
git clone --depth 1 https://github.com/gson1703/anita.git "$HOME/anita"
sed -i '1c#!/usr/bin/env python3' "$HOME/anita/anita"
echo "$HOME/anita" >> "$GITHUB_PATH"

- name: Init Packer plugins
run: packer init netbsd10.pkr.hcl

- name: Cache anita workdir
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae #v5.0.5
with:
path: netbsd10/anita-work
key: netbsd-10.1-anita-v2

- name: Run anita install
run: ./scripts/anita-install.sh

- name: Build box
run: PACKER_LOG=1 packer build netbsd10.pkr.hcl

- name: Upload box as artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1
with:
name: netbsd10-libvirt
path: netbsd10/netbsd10-libvirt.box

- name: Publish to HCP Vagrant Registry
if: github.ref == 'refs/heads/main'
env:
HCP_CLIENT_ID: ${{ secrets.HCP_CLIENT_ID }}
HCP_CLIENT_SECRET: ${{ secrets.HCP_CLIENT_SECRET }}
run: |
hcp auth login --client-id="$HCP_CLIENT_ID" --client-secret="$HCP_CLIENT_SECRET"
vagrant cloud publish \
--force \
--release \
DefinedNet/netbsd10 \
1.0.${{ github.run_number }} \
libvirt \
netbsd10-libvirt.box
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ packer build openbsd78.pkr.hcl

Produces `openbsd78-libvirt.box`.

### NetBSD 10.1 (amd64, libvirt)

Minimal NetBSD 10.1 box with vagrant user, sudo, and SSH configured.

NetBSD has no native install autoresponder, so install is driven by [anita](https://www.gson.org/netbsd/anita/) (the canonical NetBSD test-install tool). `anita-install.sh` produces a base disk image and bootstraps SSH access for root; packer then takes over to configure the vagrant user.

```sh
cd netbsd10
pip install anita
packer init netbsd10.pkr.hcl
./scripts/anita-install.sh
packer build netbsd10.pkr.hcl
```

Produces `netbsd10-libvirt.box`.

## Building locally

Requires KVM. Both boxes use QEMU with KVM acceleration.
Expand Down
64 changes: 64 additions & 0 deletions netbsd10/netbsd10.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
packer {
required_plugins {
qemu = {
version = ">= 1.1.0"
source = "github.com/hashicorp/qemu"
}
vagrant = {
version = ">= 1.1.0"
source = "github.com/hashicorp/vagrant"
}
}
}

variable "version" {
type = string
default = "10.1"
}

# Disk image produced by scripts/anita-install.sh. Anita drives sysinst over
# the console to install NetBSD into wd0.img, then boots the result once with
# --persist to install the vagrant insecure public key for root and enable
# sshd. Packer takes over from there over SSH.
variable "base_image" {
type = string
default = "anita-work/wd0.qcow2"
}

variable "ssh_private_key_file" {
type = string
default = "anita-work/vagrant"
}

source "qemu" "netbsd10" {
iso_url = var.base_image
iso_checksum = "none"
disk_image = true
use_backing_file = true
ssh_username = "root"
ssh_private_key_file = var.ssh_private_key_file
ssh_timeout = "15m"
shutdown_command = "/sbin/shutdown -p now"
disk_size = "20G"
memory = 2048
cpus = 2
headless = true
vnc_port_min = 5950
vnc_port_max = 5950
accelerator = "kvm"
format = "qcow2"

boot_wait = "30s"
}

build {
sources = ["source.qemu.netbsd10"]

provisioner "shell" {
script = "scripts/provision.sh"
}

post-processor "vagrant" {
output = "netbsd10-libvirt.box"
}
}
58 changes: 58 additions & 0 deletions netbsd10/scripts/anita-install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh
# Drive anita to produce a NetBSD disk image that packer can boot and SSH into.
#
# Two stages:
# 1. anita install: runs sysinst over the QEMU console to lay down a base
# install at $WORKDIR/wd0.img.
# 2. anita boot --persist: boots the install once, drops the vagrant insecure
# public key into /root/.ssh/authorized_keys and enables sshd, then halts.
#
# After this script finishes, packer can use $WORKDIR/wd0.img as a base disk
# and SSH in as root with $WORKDIR/vagrant to run scripts/provision.sh.

set -eux

WORKDIR="${WORKDIR:-anita-work}"
RELEASE_URL="${RELEASE_URL:-https://cdn.netbsd.org/pub/NetBSD/NetBSD-10.1/amd64/}"

mkdir -p "$WORKDIR"

# Vagrant's insecure ssh keypair. Public on github, baked into every vagrant
# install. We need both halves: the public key to trust on the box, the private
# key for packer to ssh in with.
if [ ! -f "$WORKDIR/vagrant.pub" ]; then
curl -fsSL -o "$WORKDIR/vagrant.pub" \
https://raw.githubusercontent.com/hashicorp/vagrant/main/keys/vagrant.pub
fi
if [ ! -f "$WORKDIR/vagrant" ]; then
curl -fsSL -o "$WORKDIR/vagrant" \
https://raw.githubusercontent.com/hashicorp/vagrant/main/keys/vagrant
chmod 600 "$WORKDIR/vagrant"
fi

if [ ! -f "$WORKDIR/wd0.img" ]; then
anita --workdir="$WORKDIR" install "$RELEASE_URL"
fi

if [ ! -f "$WORKDIR/.bootstrapped" ]; then
PUBKEY=$(cat "$WORKDIR/vagrant.pub")
BOOTSTRAP="
mkdir -p /root/.ssh
chmod 700 /root/.ssh
echo '$PUBKEY' > /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
echo sshd=YES >> /etc/rc.conf
echo dhcpcd=YES >> /etc/rc.conf
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
"
anita --workdir="$WORKDIR" boot --no-install --persist \
--run "$BOOTSTRAP" "$RELEASE_URL"
touch "$WORKDIR/.bootstrapped"
fi

# Anita writes a raw image, but packer's qemu builder uses -F qcow2 when
# use_backing_file is set. Produce a qcow2 copy for packer to back from.
if [ ! -f "$WORKDIR/wd0.qcow2" ] || [ "$WORKDIR/wd0.img" -nt "$WORKDIR/wd0.qcow2" ]; then
qemu-img convert -O qcow2 "$WORKDIR/wd0.img" "$WORKDIR/wd0.qcow2.tmp"
mv "$WORKDIR/wd0.qcow2.tmp" "$WORKDIR/wd0.qcow2"
fi
58 changes: 58 additions & 0 deletions netbsd10/scripts/provision.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/sh
set -eux

# Non-interactive SSH skips /etc/profile, so PATH is sshd's minimal default.
# Set it explicitly so we find pkg_add, useradd, chown, etc.
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/pkg/sbin:/usr/pkg/bin:/usr/local/sbin:/usr/local/bin

# QEMU user-mode advertises an IPv6 prefix that doesn't actually route to the
# public internet. libfetch tries v6 first, waits through retries, then falls
# back to v4 -- compounded across pkg_add's dep chain that's minutes of waste.
# Strip non-link-local v6 addresses and the v6 default route. Don't bring the
# interface down, that kills SSH.
for a in $(ifconfig vioif0 | awk '/inet6/ && !/fe80::/ {sub(/\/.*/,"",$2); print $2}'); do
ifconfig vioif0 inet6 "$a" delete || true
done
route delete -inet6 default 2>/dev/null || true

# Pull binary packages from the matching NetBSD release repo. ftp.netbsd.org no
# longer carries 9.x packages, which is what triggered building this box in the
# first place.
export PKG_PATH="https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/"

# Network sanity. Hard-timeout each probe so we never silently hang.
echo "=== sanity ==="
ifconfig vioif0 | grep -E 'inet |status' || true
cat /etc/resolv.conf || true
echo "--- https probe ---"
timeout 20 ftp -V -o - "https://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/" 2>&1 | head -10 || echo "https probe exit $?"
echo "--- http probe ---"
timeout 20 ftp -V -o - "http://cdn.netbsd.org/pub/pkgsrc/packages/NetBSD/amd64/10.1/All/" 2>&1 | head -10 || echo "http probe exit $?"
echo "=== /sanity ==="

# Install rsync (the reason this box exists) plus the usual vagrant box bits.
pkg_add -v sudo rsync bash curl

# Sudo for the vagrant user.
cat >> /usr/pkg/etc/sudoers <<'EOF'

Defaults:vagrant !requiretty
vagrant ALL=(ALL) NOPASSWD: ALL
EOF

# Vagrant user. No password set, key-only login. NetBSD's useradd defaults
# primary group to the username, so the group has to exist first.
groupadd vagrant
useradd -m -g vagrant -G wheel -s /usr/pkg/bin/bash vagrant

mkdir -p /home/vagrant/.ssh
chmod 700 /home/vagrant/.ssh
ftp -o /home/vagrant/.ssh/authorized_keys \
https://raw.githubusercontent.com/hashicorp/vagrant/main/keys/vagrant.pub
chmod 600 /home/vagrant/.ssh/authorized_keys
chown -R vagrant:vagrant /home/vagrant/.ssh

# Shrink the disk image post-processor will see.
rm -rf /tmp/* /var/tmp/*
dd if=/dev/zero of=/tmp/zero bs=1m 2>/dev/null || true
rm -f /tmp/zero