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
14 changes: 14 additions & 0 deletions docs/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ without `git clean`. The hostname line uses `__PACKER_RECIPE_NAME__`, replaced b
the same script, so the preseed files stay identical across Debian releases
(enforced by `tests/recipe-consistency.test.ts`).

An answer that only one release needs goes on that recipe's `boot_command`
instead, where the installer reads it as a preseed value, but only for a key
`preseed.cfg` does not set. The installer loads the file after applying boot
parameters, so the file wins any key both name. `debian-11` passes
`apt-setup/services-select=updates` there: the bullseye-security index still
lists packages whose binaries security.debian.org no longer serves, so
selecting the security suite fails the package step.

The package mirror is a key the preseed does set, so it is a placeholder
instead: `__PACKER_DEBIAN_MIRROR__`, filled by `scripts/inject-placeholders.sh`
from the recipe's `# mirror:` header and defaulting to `deb.debian.org`. A
release Debian has archived sets `# mirror: archive.debian.org`, which keeps
serving it after `deb.debian.org` drops it.

## AlmaLinux and Rocky Linux kickstart

Copy the nearest `ks.cfg` and update repository and release details. These builds
Expand Down
23 changes: 23 additions & 0 deletions recipes/debian-11.pkr.hcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# display: Debian 11 (Bullseye)
# group: debian
# mirror: archive.debian.org
# build_vmid: 4000
# min_cores: 1
# min_memory: 1024
Expand Down Expand Up @@ -173,6 +174,17 @@ source "proxmox-iso" "debian-11" {
"console-setup/ask_detect=false <wait>",
"console-keymaps-at/keymap=us <wait>",
"grub-installer/bootdev=/dev/sda <wait>",
# Install from main and bullseye-updates only. Debian has removed every
# bullseye binary from security.debian.org while the bullseye-security
# index still lists them, so selecting that suite makes pkgsel fetch 404s
# and the install stops at "Select and install software". Passed here
# rather than in preseed.cfg, which the Debian recipes share byte for
# byte; this works only because preseed.cfg never sets the key, since the
# installer loads that file after the boot parameters and it would win.
# The mirror is archive.debian.org (`# mirror:` above), where bullseye
# stays after deb.debian.org drops it. Bullseye's cloud.cfg sets
# apt_preserve_sources_list, so clones keep this sources.list.
"apt-setup/services-select=updates <wait>",
"<enter><wait>",
]

Expand Down Expand Up @@ -208,6 +220,17 @@ build {
]
}

# A mirror that silently fell back to deb.debian.org would build fine today
# and break the day Debian drops bullseye from it, so check what landed.
provisioner "shell" {
inline = [
"echo '==> Verifying apt sources'",
"cat /etc/apt/sources.list",
"grep -Eq '^deb http://archive.debian.org/debian/? bullseye main' /etc/apt/sources.list || { echo 'sources.list does not use archive.debian.org' >&2; exit 1; }",
"! grep -Eq '^deb .*bullseye-security' /etc/apt/sources.list || { echo 'sources.list still enables bullseye-security' >&2; exit 1; }",
]
}

provisioner "file" {
source = "${path.root}/_shared/cloud-init-cleanup.sh"
destination = "/tmp/cloud-init-cleanup.sh"
Expand Down
2 changes: 1 addition & 1 deletion recipes/debian-11/http/preseed.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ d-i netcfg/get_hostname string packer-__PACKER_RECIPE_NAME__
d-i netcfg/wireless_wep string
d-i hw-detect/load_firmware boolean false
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/hostname string __PACKER_DEBIAN_MIRROR__
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i passwd/root-login boolean false
Expand Down
2 changes: 1 addition & 1 deletion recipes/debian-12/http/preseed.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ d-i netcfg/get_hostname string packer-__PACKER_RECIPE_NAME__
d-i netcfg/wireless_wep string
d-i hw-detect/load_firmware boolean false
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/hostname string __PACKER_DEBIAN_MIRROR__
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i passwd/root-login boolean false
Expand Down
2 changes: 1 addition & 1 deletion recipes/debian-13/http/preseed.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ d-i netcfg/get_hostname string packer-__PACKER_RECIPE_NAME__
d-i netcfg/wireless_wep string
d-i hw-detect/load_firmware boolean false
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/hostname string __PACKER_DEBIAN_MIRROR__
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i passwd/root-login boolean false
Expand Down
8 changes: 8 additions & 0 deletions scripts/inject-placeholders.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ rm -f "$VARS_FILE"

RECIPE_DIR="recipes/${RECIPE}"

# Debian package mirror, from the recipe's `# mirror:` header. It has to reach
# the preseed file rather than the boot command: the installer loads the file
# with debconf-set-selections after applying boot parameters, so a key the
# shared preseed sets would silently override a boot-command value.
DEBIAN_MIRROR="$(sed -nE 's/^#[[:space:]]*mirror:[[:space:]]*([^[:space:]]+).*/\1/p' "recipes/${RECIPE}.pkr.hcl" 2>/dev/null | head -n1 || true)"
DEBIAN_MIRROR="${DEBIAN_MIRROR:-deb.debian.org}"

# ── Detect installer files and generate ephemeral SSH keypair ────────────────
PRESEED="${RECIPE_DIR}/http/preseed.cfg"
USER_DATA="${RECIPE_DIR}/http/user-data"
Expand All @@ -47,6 +54,7 @@ if [ "$NEEDS_KEY" = "1" ]; then
"s|__PACKER_SSH_PUBLIC_KEY__|${PUB_KEY}|g; \
s|ssh-ed25519 AAAA[^ ]+ packer-${RECIPE}-[^ '\"]*|${PUB_KEY}|g; \
s|__PACKER_RECIPE_NAME__|${RECIPE}|g; \
s|__PACKER_DEBIAN_MIRROR__|${DEBIAN_MIRROR}|g; \
s|__PACKER_BUILD_IP__|${CF_BUILD_IP:-}|g; \
s|__PACKER_BUILD_GW__|${CF_BUILD_GW:-}|g; \
s|__PACKER_BUILD_DNS__|${CF_BUILD_DNS:-1.1.1.1}|g" \
Expand Down
34 changes: 34 additions & 0 deletions tests/inject-placeholders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ describe('inject-placeholders.sh', () => {
preseed,
[
'd-i netcfg/get_hostname string packer-__PACKER_RECIPE_NAME__',
'd-i mirror/http/hostname string __PACKER_DEBIAN_MIRROR__',
'd-i netcfg/get_ipaddress string __PACKER_BUILD_IP__',
'd-i netcfg/get_gateway string __PACKER_BUILD_GW__',
'd-i netcfg/get_nameservers string __PACKER_BUILD_DNS__',
Expand All @@ -57,6 +58,10 @@ describe('inject-placeholders.sh', () => {
expect(injected).toContain(
'd-i netcfg/get_hostname string packer-debian-test'
)
// No recipe file, so no `# mirror:` header: the default mirror.
expect(injected).toContain(
'd-i mirror/http/hostname string deb.debian.org'
)
expect(injected).toContain('d-i netcfg/get_ipaddress string 10.99.0.5')
expect(injected).toContain('d-i netcfg/get_gateway string 10.99.0.1')
expect(injected).toContain('d-i netcfg/get_nameservers string 9.9.9.9')
Expand All @@ -69,6 +74,35 @@ describe('inject-placeholders.sh', () => {
)
})

// A release Debian has archived installs from archive.debian.org. This has
// to reach the preseed file: a boot-command value loses to it.
test("takes the Debian mirror from the recipe's # mirror: header", () => {
const root = mkdtempSync(join(tmpdir(), 'cf-inject-'))
tempDirs.push(root)
const httpDir = join(root, 'recipes', 'debian-old', 'http')
const runnerTemp = join(root, 'runner-temp')
mkdirSync(httpDir, { recursive: true })
mkdirSync(runnerTemp)
writeFileSync(
join(root, 'recipes', 'debian-old.pkr.hcl'),
'# display: Debian Old\n# mirror: archive.debian.org\n'
)
const preseed = join(httpDir, 'preseed.cfg')
writeFileSync(
preseed,
'd-i mirror/http/hostname string __PACKER_DEBIAN_MIRROR__\n'
)

const result = Bun.spawnSync(['bash', script, 'debian-old'], {
cwd: root,
env: { ...process.env, RUNNER_TEMP: runnerTemp },
})
expect(result.exitCode).toBe(0)
expect(readFileSync(preseed, 'utf8')).toBe(
'd-i mirror/http/hostname string archive.debian.org\n'
)
})

test('protects generated Windows credentials with mode 0600', () => {
const root = mkdtempSync(join(tmpdir(), 'cf-inject-'))
tempDirs.push(root)
Expand Down
Loading