This is a repository containing Packer templates to create a hardened Ubuntu server.
There are templates available for creating a
- Azure virtual machine image
- QEMU
.qcow2disk image
Ubuntu 26.04 LTS (Resolute Raccoon) is supported.
The Ansible role used to make the server a bit more secure is available in the konstruktoid/ansible-role-hardening repository.
The role is installed and configured using config/local.yml.
See https://www.packer.io/docs/builders and https://www.packer.io/docs/post-processors on how to rewrite the template if you want to use it for another platforms.
Requires Packer and a Microsoft Azure account.
Ensure the correct values are set in ubuntu-azure-vars.json before
validating the configuration and building the image.
azure_vars_export is a script that will create or reset the service principal, export the necessary environment variables to authenticate with Azure, and detect the caller's public IP address so the build VM's network security group only allows inbound SSH from that address.
{
"image_offer": "ubuntu-26_04-lts",
"image_sku": "server",
"principal_name": "PackerPrincipal",
"resource_group": "PackerGroup",
"vm_size": "Standard_D2s_v3"
}packer init -upgrade -var-file ubuntu-azure-vars.json ubuntu-hardened-azure.pkr.hcl
packer validate -var-file ubuntu-azure-vars.json ubuntu-hardened-azure.pkr.hcl
packer build -timestamp-ui -var-file ubuntu-azure-vars.json ubuntu-hardened-azure.pkr.hclRequires Packer, QEMU and OVMF.
To build the image, run bash build_box.sh. The script generates a throwaway
SSH keypair, then builds ubuntu-hardened-qemu.pkr.hcl,
a self-contained template with no external dependencies: Packer boots the
official Ubuntu 26.04 live-server ISO in QEMU and installs it unattended
using the autoinstall
configuration in http/user-data.pkrtpl.
Once the build completes, an SBOM of the image is generated with Syft using scripts/sbom.sh.
The generated .qcow2 disk image, along with its SPDX (.spdx.json) and
CycloneDX (.cdx.json) SBOM files and a CHECKSUMS file, are stored in a
timestamped subdirectory under output.
By default the image ships with a single ubuntu account (password
ubuntu, see var.password/var.password_hash) and no persisted SSH keys.
Pass your own public keys to keep them installed in the built image:
bash build_box.sh -var 'ssh_authorized_keys=["ssh-ed25519 AAAA... you@example.com"]'Extra arguments given to build_box.sh are passed through to packer build.
The ephemeral keypair Packer itself uses to provision the image is always
stripped by scripts/cleanup.sh before the build
finishes.
There's a SLSA artifact present under the slsa action workflow.
Boot the image built by build_box.sh with QEMU, using the same OVMF UEFI firmware as the build, and forward a local port to the guest's SSH server:
cp /usr/share/OVMF/OVMF_VARS_4M.fd /tmp/OVMF_VARS.fd
qemu-system-x86_64 \
-machine q35,accel=kvm \
-cpu host \
-m 2048 \
-drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
-drive if=pflash,format=raw,file=/tmp/OVMF_VARS.fd \
-drive if=virtio,format=qcow2,file=./output/<build>/<build>.qcow2 \
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
-display none -serial mon:stdio \
-device virtio-net-pci,netdev=net0Replace ./output/<build>/<build>.qcow2 with the actual .qcow2 path
produced under the output directory, and drop -cpu host,accel=kvm if
hardware virtualization isn't available on the host.
Once booted, connect over SSH as the ubuntu user, either with a key you
passed via ssh_authorized_keys or with the password ubuntu
(see Local qcow2 image):
ssh -p 2222 -o StrictHostKeyChecking=no ubuntu@localhost.
├── azure_vars_export
├── build_box.sh
├── config
│ ├── ansible.cfg
│ └── local.yml
├── http
│ ├── meta-data
│ └── user-data.pkrtpl
├── LICENSE
├── README.md
├── scripts
│ ├── azure.sh
│ ├── cleanup.sh
│ ├── hardening.sh
│ └── sbom.sh
├── SECURITY.md
├── ubuntu-azure-vars.json
├── ubuntu-hardened-azure.pkr.hcl
└── ubuntu-hardened-qemu.pkr.hcl
3 directories, 16 filesDo you want to contribute? Great! Contributions are always welcome, no matter how large or small. If you found something odd, feel free to submit a issue, improve the code by creating a pull request, or by sponsoring this project.
Apache License Version 2.0