Ludus Source — OsmSec #
Packer templates for Ludus, published by OsmSec. Six images across Ubuntu, Kali and Debian, ready to deploy in any range.
Quick start #
ludus source add https://tangled.org/osmsec.xyz/ludus-source-osmsec --all
ludus templates build -n osmsec-ubuntu-26.04-x64-server-template
Adding a source registers the templates; building turns them into VM images. Build one image and everyone on the host can clone it.
Requires Ludus 2.2.0 or later.
Templates #
| Name | Base | CPU | RAM | Disk | Login |
|---|---|---|---|---|---|
osmsec-ubuntu-26.04-x64-server-template |
Ubuntu 26.04.1 LTS | 1 | 2 GB | 20 GB | localuser:password |
osmsec-ubuntu-26.04-x64-desktop-template |
Ubuntu 26.04.1 LTS | 2 | 4 GB | 25 GB | localuser:password |
osmsec-ubuntu-24.04-x64-server-template |
Ubuntu 24.04.5 LTS | 1 | 2 GB | 20 GB | localuser:password |
osmsec-ubuntu-24.04-x64-desktop-template |
Ubuntu 24.04.5 LTS | 2 | 4 GB | 25 GB | localuser:password |
osmsec-kali-2026.2-x64-desktop-template |
Kali Linux 2026.2 (XFCE) | 2 | 4 GB | 25 GB | kali:kali |
osmsec-debian-13-x64-server-template |
Debian 13.7 (Trixie) | 1 | 1 GB | 10 GB | debian:debian |
The name is the vm_name in each template — use it as the template: value in a
range config. The osmsec- prefix keeps these images identifiable and prevents
name collisions with built-in templates.
Logging in #
Each image provisions the account Ludus expects for its distribution:
| Distro | Account | Defined by |
|---|---|---|
| Ubuntu | localuser:password |
group_vars/ubuntu.yml |
| Kali | kali:kali |
group_vars/kali.yml |
| Debian | debian:debian |
group_vars/debian.yml |
These names aren't a preference. Ludus uses them for Ansible over SSH when
provisioning a range, and no range-config field overrides them. An image with a
different account will build and boot fine, then fail deployment with
UNREACHABLE! ... Invalid/incorrect password.
All accounts have passwordless sudo.
Building #
# One template
ludus templates build -n osmsec-debian-13-x64-server-template
# Everything
ludus templates build -n all
Each build downloads its ISO and installs unattended, taking roughly 10–15 minutes for a server image and 20–30 for a desktop image depending on network and storage. Re-sync after changes without rebuilding:
ludus source sync
Using a template #
ludus:
- vm_name: "{{ range_id }}-kali"
hostname: kali
template: osmsec-kali-2026.2-x64-desktop-template
vlan: 10
ip_last_octet: 10
ram_gb: 8
cpus: 4
linux: true
ludus range config set -f range-config.yml
ludus range deploy
CPU and RAM in a range config override the template defaults. Disk does not — a range config has no disk field, so each range VM inherits the template's disk size, and changing it later means rebuilding the template. Size disk generously; memory and CPU are cheap to adjust per range.
How it works #
The Ubuntu images install via cloud-init's subiquity autoinstall
(http/user-data). The Kali and Debian images are Debian-based and use a
debian-installer preseed (http/*-preseed.cfg) fetched over HTTP.
Both mechanisms do the same three things:
- create the Ludus account, with passwordless sudo and SSH enabled;
- install the minimum packages needed in a range;
- clear SSH host keys and
/etc/machine-id, so every clone generates its own identity on first boot instead of sharing the image's.
No provisioners run during a build. Everything in a .pkr.hcl beyond sizing and
the ISO is Proxmox and Ludus wiring.
Why resolvconf is installed #
Ludus configures a VM's DNS by writing dns-nameservers <router> into
/etc/network/interfaces. That directive is only acted on by the ifupdown
resolvconf hook, so without the resolvconf package it is silently ignored.
This matters most for the range router: it boots with an empty
/etc/resolv.conf, and every apt-get during deployment fails to resolve
deb.debian.org. All templates therefore install resolvconf, and
validate_source.py fails any template that drops it.
Repository layout #
source.yml source metadata
templates/
ubuntu-26.04-x64-server/ one directory per template
*.pkr.hcl Packer config: sizing, ISO, Proxmox wiring
icon.png catalog icon
http/ installer payload (user-data or preseed)
tools/ validation scripts
Working on templates #
python3 tools/validate_source.py # template structure, accounts, sizing
./tools/check_isos.sh # ISO checksums against each vendor
validate_source.py applies the same rules Ludus does, plus checks for the
mistakes that have actually broken builds here: missing Ludus variables,
accounts that don't match group_vars, preseed installs that forget to enable
SSH, and wrong boot devices. It runs on a stock Python with no third-party
packages. Run both scripts before pushing.
tools/sha512crypt.py generates password hashes for the Ubuntu user-data files
(Python 3.13 removed the stdlib crypt module):
python3 tools/sha512crypt.py yourpassword
ISO storage #
Each template downloads its ISO with iso_download_pve = true, which hands the
download to the Proxmox node. The node names the file from the URL, so it lands
in storage under its real name:
local:iso/debian-13.7.0-amd64-netinst.iso
Without that flag Packer downloads the ISO itself and uploads it from its local
cache, where the file is keyed by checksum — leaving storage full of files like
835a50e8f0f00897249604c3b931dbb5d953f672.iso. The node still verifies
iso_checksum during the download; a mismatch fails the task and the build.
These are lab images with public default credentials. Don't put real data on them, and don't reuse them outside a range.
To pin a build, register the source at a specific commit:
ludus source add https://tangled.org/osmsec.xyz/ludus-source-osmsec --ref <commit-sha>
License #
MIT — see LICENSE.