Seed bootapi templates + distro catalog
Initial content for bootapi's git-synced template set: the kickstart and iPXE templates and the distro catalog (almalinux9, fedora) ported from bootapi's embedded defaults, plus CI that validates every template/catalog renders for every distro (bootapi validate) and shellchecks the %post blocks. Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
This commit is contained in:
@@ -0,0 +1,15 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v5.0.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-added-large-files
|
||||||
|
- id: check-merge-conflict
|
||||||
|
|
||||||
|
- repo: https://github.com/adrienverge/yamllint
|
||||||
|
rev: v1.35.1
|
||||||
|
hooks:
|
||||||
|
- id: yamllint
|
||||||
|
args: [-c, .yamllint.yaml]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
when:
|
||||||
|
- event: [pull_request, push]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: pre-commit
|
||||||
|
image: git.unkin.net/unkin/almalinux9-gobuilder:20260606
|
||||||
|
commands:
|
||||||
|
- uvx pre-commit run --all-files
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
serviceAccountName: default
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 1
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
cpu: 2
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
when:
|
||||||
|
- event: [pull_request, push]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
# Render every kickstart + iPXE script for every catalog distro against a
|
||||||
|
# fixture host, failing on any parse/resolve error or unresolved value. Reuses
|
||||||
|
# the real bootapi engine via its `validate` subcommand (fetched @main).
|
||||||
|
- name: validate
|
||||||
|
image: golang:1.25
|
||||||
|
environment:
|
||||||
|
GOFLAGS: -buildvcs=false
|
||||||
|
GOPRIVATE: git.unkin.net
|
||||||
|
GONOSUMCHECK: "1"
|
||||||
|
GOSUMDB: "off"
|
||||||
|
GOPROXY: direct
|
||||||
|
commands:
|
||||||
|
- go run git.unkin.net/unkin/bootapi/cmd/bootapi@main validate .
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
serviceAccountName: default
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 1
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
cpu: 2
|
||||||
|
|
||||||
|
# Best-effort shellcheck of the %post blocks (template actions stripped first).
|
||||||
|
- name: shellcheck
|
||||||
|
image: koalaman/shellcheck-alpine:stable
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache bash
|
||||||
|
- bash ci/shellcheck-post.sh
|
||||||
|
backend_options:
|
||||||
|
kubernetes:
|
||||||
|
serviceAccountName: default
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 128Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 500m
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
extends: relaxed
|
||||||
|
|
||||||
|
rules:
|
||||||
|
line-length: disable
|
||||||
|
document-start: disable
|
||||||
|
comments:
|
||||||
|
min-spaces-from-content: 1
|
||||||
@@ -1,3 +1,43 @@
|
|||||||
# bootapi-templates
|
# bootapi-templates
|
||||||
|
|
||||||
Live kickstart/iPXE templates + distro catalog for bootapi. Pulled by bootapi via git-sync; CI validates templates render for every distro.
|
Live kickstart / iPXE templates and the **distro catalog** for
|
||||||
|
[bootapi](https://git.unkin.net/unkin/bootapi). bootapi git-syncs this repo
|
||||||
|
(every ~3 minutes, like argocd) and hot-swaps its template set on change, so OS
|
||||||
|
and template updates ship by merging here — no bootapi rebuild or redeploy. If
|
||||||
|
this repo is unreachable, bootapi falls back to its embedded copy of these files.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
kickstart/<name>.ks.tmpl # kickstart templates (Go text/template)
|
||||||
|
ipxe/<name>.ipxe.tmpl # iPXE scripts: boot, fallback-local, fallback-shell
|
||||||
|
catalog/<name>.yaml # distro catalog: host -> boot images + kickstart
|
||||||
|
catalog/README.md # catalog field reference + debian/talos path
|
||||||
|
ci/shellcheck-post.sh # %post shellcheck helper (used by CI)
|
||||||
|
```
|
||||||
|
|
||||||
|
The data model available to templates and the catalog field reference live in
|
||||||
|
the bootapi docs: [data-model](https://git.unkin.net/unkin/bootapi/src/branch/main/docs/data-model.md),
|
||||||
|
[template-authoring](https://git.unkin.net/unkin/bootapi/src/branch/main/docs/template-authoring.md),
|
||||||
|
and [catalog/README.md](catalog/README.md) here.
|
||||||
|
|
||||||
|
## CI (required before merge)
|
||||||
|
|
||||||
|
- **validate** — `bootapi validate .` renders every kickstart + iPXE script for
|
||||||
|
every catalog distro against a fixture host and fails on any parse/resolve
|
||||||
|
error or unresolved `<no value>`. Plus `shellcheck` (error severity) of the
|
||||||
|
`%post` blocks.
|
||||||
|
- **pre-commit** — yamllint + whitespace/EOF checks.
|
||||||
|
|
||||||
|
Validate locally before pushing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go run git.unkin.net/unkin/bootapi/cmd/bootapi@main validate .
|
||||||
|
bash ci/shellcheck-post.sh # needs shellcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a distro
|
||||||
|
|
||||||
|
Add `catalog/<name>.yaml` + `kickstart/<name>.ks.tmpl` (and, if the OS needs a
|
||||||
|
mirror bootapi can't reach, a terraform-artifactapi remote). See
|
||||||
|
[catalog/README.md](catalog/README.md) — including the debian/talos path.
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Distro catalog
|
||||||
|
|
||||||
|
One YAML file per bootable OS. bootapi selects an entry for a NetBox host
|
||||||
|
(platform slug / OS family / `provision_template` override) and uses it to build
|
||||||
|
the iPXE kernel/initrd URLs and pick the kickstart template. Adding an OS is a
|
||||||
|
YAML + template change here — **no bootapi code change**.
|
||||||
|
|
||||||
|
## Fields
|
||||||
|
|
||||||
|
| Field | Required | Meaning |
|
||||||
|
|-------|----------|---------|
|
||||||
|
| `name` | yes | catalog key; also what a `provision_template` override matches |
|
||||||
|
| `match.platforms` | one of platforms/family | exact NetBox platform slugs |
|
||||||
|
| `match.family` | one of platforms/family | NetBox platform family (matches any version) |
|
||||||
|
| `kickstart` | yes | kickstart template name (`kickstart/<name>.ks.tmpl`) |
|
||||||
|
| `kernel_url` | yes | Go-template for the installer kernel URL |
|
||||||
|
| `initrd_url` | yes | Go-template for the installer initrd URL |
|
||||||
|
| `version_default` | no | version used when the platform slug carries none |
|
||||||
|
| `kernel_args` | no | extra iPXE kernel args |
|
||||||
|
| `vars` | no | named Go-template strings exposed to templates as `.DistroVars.<key>` |
|
||||||
|
|
||||||
|
`kernel_url`, `initrd_url` and `vars` values are rendered with:
|
||||||
|
`{{.ArtifactBase}}` (bootapi's `BOOTAPI_ARTIFACT_BASE_URL`), `{{.Version}}`,
|
||||||
|
`{{.Arch}}`, `{{.Hostname}}`, `{{.Platform}}`, `{{.OSFamily}}`.
|
||||||
|
|
||||||
|
## Shipped entries
|
||||||
|
|
||||||
|
- **almalinux9** — artifactapi `almalinux` remote, tree
|
||||||
|
`almalinux/<ver>/BaseOS/<arch>/os/`.
|
||||||
|
- **fedora** — artifactapi `fedora` remote, tree
|
||||||
|
`fedora/releases/<ver>/Everything/<arch>/os/`.
|
||||||
|
|
||||||
|
## Adding another distro (the intended path)
|
||||||
|
|
||||||
|
Add `catalog/<name>.yaml` + `kickstart/<name>.ks.tmpl`. If the OS lives on a
|
||||||
|
mirror bootapi can't already reach, add an artifactapi remote first
|
||||||
|
(terraform-artifactapi `config/remote_rpm/<name>.yaml`). CI (`bootapi validate`)
|
||||||
|
will render the new distro against a fixture host and fail the PR if anything is
|
||||||
|
unresolved.
|
||||||
|
|
||||||
|
### Debian / Talos — not implemented, different artifact shapes
|
||||||
|
|
||||||
|
These need their own catalog fields/template because their PXE artifacts differ
|
||||||
|
from the RHEL-family `images/pxeboot/{vmlinuz,initrd.img}` layout:
|
||||||
|
|
||||||
|
- **Debian**: netboot installer under
|
||||||
|
`dists/<release>/main/installer-<arch>/current/images/netboot/` with
|
||||||
|
`linux` + `initrd.gz`, and preseed (not kickstart). A `debian` entry would set
|
||||||
|
`kernel_url`/`initrd_url` to those paths and reference a `debian.preseed.tmpl`
|
||||||
|
template (and bootapi would serve it from `/ks` unchanged — it's just text).
|
||||||
|
- **Talos**: immutable OS booted from factory images (`vmlinuz` + `initramfs.xz`
|
||||||
|
from the Talos image factory / a mirrored remote), configured by a machine
|
||||||
|
config, not a kickstart. A `talos` entry would point `kernel_url`/`initrd_url`
|
||||||
|
at the factory artifacts and carry the config URL via `kernel_args`/`vars`.
|
||||||
|
|
||||||
|
Both are deferred; this note records the shape so the follow-up is mechanical.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Distro catalog entry: AlmaLinux 9.
|
||||||
|
# Boot images are proxied through the artifactapi "almalinux" remote. Host ->
|
||||||
|
# distro selection is NetBox-driven (platform slug almalinux9, or the almalinux
|
||||||
|
# family, or a provision_template override naming "almalinux9").
|
||||||
|
name: almalinux9
|
||||||
|
match:
|
||||||
|
platforms: [almalinux9]
|
||||||
|
family: almalinux
|
||||||
|
kickstart: almalinux9
|
||||||
|
version_default: "9"
|
||||||
|
kernel_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/vmlinuz"
|
||||||
|
initrd_url: "{{.ArtifactBase}}/almalinux/{{.Version}}/BaseOS/{{.Arch}}/os/images/pxeboot/initrd.img"
|
||||||
|
kernel_args:
|
||||||
|
- inst.text
|
||||||
|
- net.ifnames=0
|
||||||
|
vars:
|
||||||
|
# Version-level mirror base; the kickstart appends BaseOS/AppStream under it.
|
||||||
|
mirror: "{{.ArtifactBase}}/almalinux/{{.Version}}"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Distro catalog entry: Fedora (family-level, matches any fedoraNN platform).
|
||||||
|
# Boot images are proxied through the artifactapi "fedora" remote, whose tree
|
||||||
|
# lives under releases/<ver>/Everything/<arch>/os/.
|
||||||
|
name: fedora
|
||||||
|
match:
|
||||||
|
family: fedora
|
||||||
|
kickstart: fedora
|
||||||
|
version_default: "41"
|
||||||
|
kernel_url: "{{.ArtifactBase}}/fedora/releases/{{.Version}}/Everything/{{.Arch}}/os/images/pxeboot/vmlinuz"
|
||||||
|
initrd_url: "{{.ArtifactBase}}/fedora/releases/{{.Version}}/Everything/{{.Arch}}/os/images/pxeboot/initrd.img"
|
||||||
|
kernel_args:
|
||||||
|
- inst.text
|
||||||
|
- net.ifnames=0
|
||||||
|
vars:
|
||||||
|
# Install-tree root; the kickstart appends <arch>/os/ under it.
|
||||||
|
mirror: "{{.ArtifactBase}}/fedora/releases/{{.Version}}/Everything"
|
||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Best-effort shellcheck of the %post ... %end blocks in the kickstart templates.
|
||||||
|
# The bodies are Go text/templates, so we first replace every {{ ... }} action
|
||||||
|
# with a shell-safe placeholder, then extract each %post block into a script and
|
||||||
|
# run shellcheck at "error" severity (template placeholders make lower-severity
|
||||||
|
# style warnings meaningless). Fails the build on any shellcheck error.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "${tmp}"' EXIT
|
||||||
|
rc=0
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
|
for ks in kickstart/*.ks.tmpl; do
|
||||||
|
# Split into %post-scriptlets; strip {{...}} -> "PH"; give each a shebang.
|
||||||
|
awk '
|
||||||
|
/^%post/ { inpost=1; n++; next }
|
||||||
|
/^%end/ { inpost=0; next }
|
||||||
|
inpost { print > ("'"${tmp}"'/post-" n ".sh") }
|
||||||
|
' "${ks}"
|
||||||
|
|
||||||
|
for f in "${tmp}"/post-*.sh; do
|
||||||
|
[ -e "${f}" ] || continue
|
||||||
|
# Neutralize Go template actions and prepend a shebang.
|
||||||
|
sed -i 's/{{[^}]*}}/PH/g' "${f}"
|
||||||
|
printf '#!/usr/bin/env bash\n%s' "$(cat "${f}")" > "${f}.final"
|
||||||
|
echo "shellcheck (error severity): ${ks} -> $(basename "${f}")"
|
||||||
|
if ! shellcheck -S error "${f}.final"; then
|
||||||
|
rc=1
|
||||||
|
fi
|
||||||
|
rm -f "${f}" "${f}.final"
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${rc}" -eq 0 ]; then
|
||||||
|
echo "shellcheck: no errors in %post blocks"
|
||||||
|
fi
|
||||||
|
exit "${rc}"
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{{- /*
|
||||||
|
iPXE boot script for a known, PXE-enabled host. Chains the OS installer
|
||||||
|
kernel+initrd (from the distro catalog) and points inst.ks= back at bootapi's
|
||||||
|
/ks/<host> over plain HTTP, so an installer with no internal-CA trust can fetch
|
||||||
|
it. Mirrors how Cobbler generated a per-MAC gPXE script carrying inst.ks=.
|
||||||
|
|
||||||
|
KernelURL/InitrdURL/RepoURL come from the selected catalog entry (artifactapi
|
||||||
|
remote); KernelArgs are the catalog's extra args. KickstartURL uses
|
||||||
|
BOOTAPI_BASE_URL (http://).
|
||||||
|
*/ -}}
|
||||||
|
#!ipxe
|
||||||
|
echo bootapi: provisioning {{ .FQDN }} ({{ .Platform }})
|
||||||
|
{{ if and .KernelURL .InitrdURL -}}
|
||||||
|
kernel {{ .KernelURL }} initrd=initrd.img{{ if .RepoURL }} inst.repo={{ .RepoURL }}{{ end }} inst.ks={{ .KickstartURL }} ip=dhcp{{ range .KernelArgs }} {{ . }}{{ end }}
|
||||||
|
initrd {{ .InitrdURL }}
|
||||||
|
boot
|
||||||
|
{{- else -}}
|
||||||
|
echo bootapi: no boot images resolved for {{ .Platform }} (no catalog entry / BOOTAPI_BOOT_BASE_URL); booting local disk
|
||||||
|
sanboot --no-describe --drive 0x80 || exit
|
||||||
|
{{- end }}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{{- /*
|
||||||
|
Safe default for an UNKNOWN MAC (NetBox has no matching device). We deliberately
|
||||||
|
do NOT start an installer for a machine we can't identify — that could wipe a
|
||||||
|
production box that PXE-booted by accident. Instead we boot from local disk, so
|
||||||
|
an already-installed host just continues, and a brand-new host loops back to PXE
|
||||||
|
on its next attempt (by which point NetBox should know it).
|
||||||
|
*/ -}}
|
||||||
|
#!ipxe
|
||||||
|
echo bootapi: unknown MAC ${net0/mac}; not provisioning. Booting local disk.
|
||||||
|
sanboot --no-describe --drive 0x80 || exit
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{{- /*
|
||||||
|
Debug fallback for an unknown MAC (opt in via BOOTAPI_UNKNOWN_MAC_FALLBACK=shell).
|
||||||
|
Drops to an interactive iPXE shell instead of booting anything, so an operator
|
||||||
|
racking a new box can inspect ${net0/mac} and register it in NetBox. Not the
|
||||||
|
default because it halts the boot and is unsafe for an accidental PXE of a prod
|
||||||
|
host.
|
||||||
|
*/ -}}
|
||||||
|
#!ipxe
|
||||||
|
echo bootapi: unknown MAC ${net0/mac}; dropping to iPXE shell for debugging.
|
||||||
|
shell
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
{{- /*
|
||||||
|
AlmaLinux 9 kickstart, ported from the Cobbler default.ks contract.
|
||||||
|
|
||||||
|
Rendered by bootapi from NetBox data + render-time secrets + the distro catalog.
|
||||||
|
Install source comes from the artifactapi almalinux remote (via the catalog
|
||||||
|
mirror var). The %post installs the Puppet agent and points it at the k8s
|
||||||
|
puppetserver (puppet.k8s.syd1.au.unkin.net / puppetca.k8s...), writes the
|
||||||
|
puppet-initial PUPPETCA_URL env file, then posts back to bootapi so pxe_enabled
|
||||||
|
flips off (Cobbler's netboot_enabled flow).
|
||||||
|
|
||||||
|
Data model: see docs/data-model.md. `.RootPasswordHash` and `.ProvisionToken`
|
||||||
|
come from Vault/env at render time, never from NetBox.
|
||||||
|
*/ -}}
|
||||||
|
{{- $mirror := .DistroVars.mirror -}}
|
||||||
|
#version=RHEL9
|
||||||
|
# Rendered by bootapi for {{ .FQDN }} (platform {{ .Platform }}, role {{ default "none" .Role }})
|
||||||
|
text
|
||||||
|
eula --agreed
|
||||||
|
firstboot --disable
|
||||||
|
reboot
|
||||||
|
|
||||||
|
# --- install source (artifactapi almalinux remote, from the distro catalog) ---
|
||||||
|
url --url={{ $mirror }}/BaseOS/{{ .Arch }}/os/
|
||||||
|
repo --name=AppStream --baseurl={{ $mirror }}/AppStream/{{ .Arch }}/os/
|
||||||
|
|
||||||
|
# --- localization ---
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
lang en_AU.UTF-8
|
||||||
|
timezone Australia/Sydney --utc
|
||||||
|
|
||||||
|
# --- security ---
|
||||||
|
{{ if .RootPasswordHash -}}
|
||||||
|
rootpw --iscrypted {{ .RootPasswordHash }}
|
||||||
|
{{- else -}}
|
||||||
|
rootpw --lock
|
||||||
|
{{- end }}
|
||||||
|
selinux --enforcing
|
||||||
|
firewall --enabled --service=ssh
|
||||||
|
authselect select sssd with-mkhomedir --force
|
||||||
|
|
||||||
|
# --- networking (static, from NetBox) ---
|
||||||
|
{{- $primary := .PrimaryInterface }}
|
||||||
|
{{- range .Interfaces }}
|
||||||
|
{{- if .IP }}
|
||||||
|
network --bootproto=static --device={{ .MAC }} --ip={{ .IP }} --netmask={{ .Netmask }}{{ if .Gateway }} --gateway={{ .Gateway }}{{ end }}{{ range $.Nameservers }} --nameserver={{ . }}{{ end }}{{ if and $primary (eq .MAC $primary.MAC) }} --hostname={{ $.FQDN }}{{ end }} --activate --onboot=on --noipv6
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# --- storage ---
|
||||||
|
ignoredisk --only-use=sda
|
||||||
|
clearpart --all --initlabel --drives=sda
|
||||||
|
bootloader --location=mbr --boot-drive=sda --append="crashkernel=auto"
|
||||||
|
autopart --type=lvm --nohome
|
||||||
|
|
||||||
|
# --- packages ---
|
||||||
|
%packages --ignoremissing --excludedocs
|
||||||
|
@^minimal-environment
|
||||||
|
openssh-server
|
||||||
|
chrony
|
||||||
|
vim-minimal
|
||||||
|
tmux
|
||||||
|
git
|
||||||
|
-iwl*-firmware
|
||||||
|
%end
|
||||||
|
|
||||||
|
# --- bootstrap: puppet (k8s) + end-of-install callback ---
|
||||||
|
%post --log=/root/bootapi-post.log
|
||||||
|
set -x
|
||||||
|
|
||||||
|
# chrony: keep time sane before any cert work.
|
||||||
|
systemctl enable chronyd
|
||||||
|
|
||||||
|
{{ if .SSHAuthorizedKeys -}}
|
||||||
|
# root authorized_keys (from render-time config, not NetBox).
|
||||||
|
install -d -m0700 /root/.ssh
|
||||||
|
cat > /root/.ssh/authorized_keys <<'EOF'
|
||||||
|
{{ range .SSHAuthorizedKeys }}{{ . }}
|
||||||
|
{{ end }}EOF
|
||||||
|
chmod 0600 /root/.ssh/authorized_keys
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# Install the Puppet 8 agent from the puppet platform repo.
|
||||||
|
rpm -q puppet-agent >/dev/null 2>&1 || \
|
||||||
|
dnf install -y https://yum.puppet.com/puppet8-release-el-9.noarch.rpm
|
||||||
|
dnf install -y puppet-agent
|
||||||
|
|
||||||
|
# Point the agent at the k8s puppetserver / CA.
|
||||||
|
PUPPET_BIN=/opt/puppetlabs/bin/puppet
|
||||||
|
"$PUPPET_BIN" config set --section main certname "{{ .FQDN }}"
|
||||||
|
"$PUPPET_BIN" config set --section main server "{{ .PuppetServer }}"
|
||||||
|
"$PUPPET_BIN" config set --section main ca_server "{{ .PuppetCAServer }}"
|
||||||
|
"$PUPPET_BIN" config set --section main report_server "{{ .PuppetServer }}"
|
||||||
|
"$PUPPET_BIN" config set --section main environment production
|
||||||
|
|
||||||
|
# puppet-initial bootstrap unit reads PUPPETCA_URL from this EnvironmentFile.
|
||||||
|
install -d -m0755 /etc/sysconfig
|
||||||
|
cat > /etc/sysconfig/puppet-initial <<'EOF'
|
||||||
|
PUPPETCA_URL={{ .PuppetCAURL }}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Enable the agent; first boot triggers firstrun (autosign handles the CSR).
|
||||||
|
systemctl enable puppet
|
||||||
|
|
||||||
|
{{ if and .ProvisionToken .CallbackURL -}}
|
||||||
|
# Tell bootapi the install is done so it clears pxe_enabled in NetBox and the
|
||||||
|
# next PXE boots local disk. Runs over plain HTTP (no internal CA trust yet);
|
||||||
|
# the token authenticates the call. Non-fatal if it fails (the local-disk
|
||||||
|
# fallback still protects a re-provisioned host on the following boot).
|
||||||
|
curl -fsS -m 15 -X POST \
|
||||||
|
-H "Authorization: Bearer {{ .ProvisionToken }}" \
|
||||||
|
"{{ .CallbackURL }}" || echo "bootapi: provisioned callback failed (non-fatal)"
|
||||||
|
{{- end }}
|
||||||
|
%end
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
{{- /*
|
||||||
|
Fedora kickstart (family-level template: matches any "fedoraNN" platform slug
|
||||||
|
via the catalog family match). Kept close to the AlmaLinux template so the two
|
||||||
|
stay comparable; the differences are the install-tree layout (releases/.../
|
||||||
|
Everything) and the puppet release RPM. Install source + boot images come from
|
||||||
|
the artifactapi fedora remote via the distro catalog.
|
||||||
|
*/ -}}
|
||||||
|
{{- $mirror := .DistroVars.mirror -}}
|
||||||
|
#version=F{{ default "" .OSVersion }}
|
||||||
|
# Rendered by bootapi for {{ .FQDN }} (platform {{ .Platform }})
|
||||||
|
text
|
||||||
|
firstboot --disable
|
||||||
|
reboot
|
||||||
|
|
||||||
|
# --- install source (artifactapi fedora remote, from the distro catalog) ---
|
||||||
|
url --url={{ $mirror }}/{{ .Arch }}/os/
|
||||||
|
|
||||||
|
keyboard --xlayouts='us'
|
||||||
|
lang en_AU.UTF-8
|
||||||
|
timezone Australia/Sydney --utc
|
||||||
|
|
||||||
|
{{ if .RootPasswordHash -}}
|
||||||
|
rootpw --iscrypted {{ .RootPasswordHash }}
|
||||||
|
{{- else -}}
|
||||||
|
rootpw --lock
|
||||||
|
{{- end }}
|
||||||
|
selinux --enforcing
|
||||||
|
firewall --enabled --service=ssh
|
||||||
|
|
||||||
|
# --- networking (static, from NetBox) ---
|
||||||
|
{{- $primary := .PrimaryInterface }}
|
||||||
|
{{- range .Interfaces }}
|
||||||
|
{{- if .IP }}
|
||||||
|
network --bootproto=static --device={{ .MAC }} --ip={{ .IP }} --netmask={{ .Netmask }}{{ if .Gateway }} --gateway={{ .Gateway }}{{ end }}{{ range $.Nameservers }} --nameserver={{ . }}{{ end }}{{ if and $primary (eq .MAC $primary.MAC) }} --hostname={{ $.FQDN }}{{ end }} --activate --onboot=on --noipv6
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
|
|
||||||
|
# --- storage ---
|
||||||
|
ignoredisk --only-use=sda
|
||||||
|
clearpart --all --initlabel --drives=sda
|
||||||
|
bootloader --location=mbr --boot-drive=sda
|
||||||
|
autopart --type=lvm --nohome
|
||||||
|
|
||||||
|
%packages --ignoremissing
|
||||||
|
@^minimal-environment
|
||||||
|
openssh-server
|
||||||
|
chrony
|
||||||
|
git
|
||||||
|
%end
|
||||||
|
|
||||||
|
%post --log=/root/bootapi-post.log
|
||||||
|
set -x
|
||||||
|
systemctl enable chronyd sshd
|
||||||
|
{{ if .SSHAuthorizedKeys -}}
|
||||||
|
install -d -m0700 /root/.ssh
|
||||||
|
cat > /root/.ssh/authorized_keys <<'EOF'
|
||||||
|
{{ range .SSHAuthorizedKeys }}{{ . }}
|
||||||
|
{{ end }}EOF
|
||||||
|
chmod 0600 /root/.ssh/authorized_keys
|
||||||
|
{{- end }}
|
||||||
|
dnf install -y "https://yum.puppet.com/puppet8-release-fedora-{{ default "40" .OSVersion }}.noarch.rpm" || true
|
||||||
|
dnf install -y puppet-agent
|
||||||
|
PUPPET_BIN=/opt/puppetlabs/bin/puppet
|
||||||
|
"$PUPPET_BIN" config set --section main certname "{{ .FQDN }}"
|
||||||
|
"$PUPPET_BIN" config set --section main server "{{ .PuppetServer }}"
|
||||||
|
"$PUPPET_BIN" config set --section main ca_server "{{ .PuppetCAServer }}"
|
||||||
|
|
||||||
|
install -d -m0755 /etc/sysconfig
|
||||||
|
cat > /etc/sysconfig/puppet-initial <<'EOF'
|
||||||
|
PUPPETCA_URL={{ .PuppetCAURL }}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl enable puppet
|
||||||
|
|
||||||
|
{{ if and .ProvisionToken .CallbackURL -}}
|
||||||
|
curl -fsS -m 15 -X POST \
|
||||||
|
-H "Authorization: Bearer {{ .ProvisionToken }}" \
|
||||||
|
"{{ .CallbackURL }}" || echo "bootapi: provisioned callback failed (non-fatal)"
|
||||||
|
{{- end }}
|
||||||
|
%end
|
||||||
Reference in New Issue
Block a user