Files
bootapi/docs/deployment.md
T
unkin-agent a414918350
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
Fetch templates over HTTP instead of shelling out to git
The runtime image is distroless and has no git binary, so every sync
failed and bootapi silently served the stale embedded templates.

- fetch the branch tarball (<repo>/archive/<branch>.tar.gz) and extract
  it into an in-memory FS; no checkout, no writable volume
- digest the extracted tree, not the archive bytes, so a recompressed
  identical archive is not a change
- skip entries that would escape the tree
- log the source commit from Gitea's immutable Link header
2026-09-26 18:59:07 +10:00

5.8 KiB
Raw Blame History

Deploying bootapi

The actual argocd-apps deployment is a follow-up task and is intentionally not part of this repo. This document is the spec for that follow-up plus the DHCP change the estate needs.

bootapi is a stateless HTTP service. It mirrors encapi's deployment shape: a Go binary in a distroless image, config from env, secrets from Vault via the Vault Secrets Operator (VSO).

Container image

git.unkin.net/unkin/bootapi:<tag> (built + pushed by .woodpecker/docker.yaml on a v* tag). Also mirror to the artifactapi local docker registry if desired.

Kubernetes wiring (argocd-apps follow-up)

Create apps/base/bootapi/ following the argocd-apps AGENTS.md pattern:

  1. namespace bootapi.
  2. VaultAuth (default) — kubernetes method, mount k8s/au/syd1, role default, SA default (copy netbox's vaultauth.yaml).
  3. VaultStaticSecret → k8s Secret bootapi-secrets, from Vault kv path kubernetes/namespace/bootapi/default/bootapi-secrets with keys:
    • netbox_token — a dedicated NetBox API token for bootapi. It needs read on devices/interfaces/ip-addresses AND write on the device pxe_enabled custom field (the provisioned callback PATCHes it — see security.md). Create a bootapi NetBox user/token via terraform-netbox rather than reusing the seeded superuser token at kv/kubernetes/namespace/netbox/default/netbox-superuser.
    • provision_token — the shared bearer token guarding POST /provisioned (also embedded in rendered kickstarts). Generate a random value.
    • root_password_hash — crypt(3) hash for the installed root account (the successor to Cobbler's eyaml default_password_crypted).
    • ssh_authorized_keys — optional, newline-separated.
  4. Templates: prefer git-sync — set BOOTAPI_TEMPLATE_GIT_URL to https://git.unkin.net/unkin/bootapi-templates.git (public; no token needed) and bootapi pulls it every BOOTAPI_TEMPLATE_GIT_INTERVAL (default 3m). No ConfigMap or pod restart is needed to change templates — merge to the repo's main and bootapi reloads within the interval (last-good kept on a bad push). The embedded defaults remain the fallback if the repo is unreachable. (A BOOTAPI_TEMPLATE_DIR ConfigMap is still supported for air-gapped installs.)
  5. Deployment — image above, env from config.example.env, secret keys wired as BOOTAPI_NETBOX_TOKEN_FILE / BOOTAPI_PROVISION_TOKEN_FILE / BOOTAPI_ROOT_PASSWORD_HASH_FILE (mount the Secret). Least-privilege securityContext (runAsNonRoot, drop: [all]). Baseline resources: requests 512Mi/1, limits 2Gi/2 cpu. No git binary and no writable volume are needed: template sync is an HTTP fetch of the repo's branch tarball, held in memory.
  6. Service + exposure: see the Gateway section below.
  7. Register in argocd/applicationsets/platform.yaml (apps/overlays/*/bootapi) and the platform AppProject destinations.

Gateway: HTTP and HTTPS

PXE installers do not trust the internal CA, so the boot path must be served over plain HTTP. Unlike the estate default, the bootapi HTTPRoute must not blanket-301 HTTP→HTTPS:

  • A plain-HTTP listener/HTTPRoute (or a LoadBalancer Service on port 80→8000) reachable by PXE clients at a stable address/hostname on the PXE VLAN — this is the BOOTAPI_BASE_URL DHCP/iPXE points at. No redirect.
  • Optionally an HTTPS HTTPRoute for humans/tooling that do trust the CA (bootapi can serve TLS directly via BOOTAPI_TLS_*, or terminate at the gateway). This is additive; it must not replace or redirect the HTTP boot path.

The end-of-kickstart callback (POST /provisioned) runs over the same plain-HTTP base by default (the token authenticates it; the install has no CA trust yet). If you install the internal CA early in %post, you may set BOOTAPI_CALLBACK_BASE_URL to the HTTPS URL instead.

Cross-repo dependencies (per estate conventions)

  • terraform-git: unkin/bootapi-templates repo (this PR's sibling) holds the live template set + distro catalog + validation CI.
  • argocd-apps: add a serviceaccount_* under apps/base/woodpecker/ if the bootapi pipelines need a dedicated SA (they use default today).
  • terraform-vault: add the k8s auth role + kv policy granting the bootapi namespace read on kv/kubernetes/namespace/bootapi/default/*.
  • terraform-netbox: create the bootapi NetBox token (read + write on the pxe_enabled device custom field) and seed it, provision_token and root_password_hash into the Vault kv path above. Also define the pxe_enabled boolean custom field on the Device model.

DHCP change (the cutover)

Cobbler advertised itself at anycast 198.18.19.19 as the DHCP next-server, with filename "/ipxe.efi" (UEFI arch 7/9) or /undionly.kpxe (BIOS arch 0). Today those are set in puppet-prod hieradata hieradata/roles/infra/dhcp/server.yaml (pools.*.pxeserver and the UEFI-64/Legacy dhcp classes).

To cut a subnet over to bootapi, repoint DHCP for that pool:

  • next-server → bootapi's LB IP (or keep the 198.18.19.19 anycast and move the anycast advertisement to bootapi's node/LB).
  • filename → the iPXE binary as before (/ipxe.efi / /undionly.kpxe); bootapi does not serve the NBP itself. The chained iPXE must then be told to fetch bootapi's script — either bake chain http://<bootapi>/ipxe/${net0/mac} into the site iPXE binary/embedded script, or set DHCP option 67 to that URL for iPXE user-class requests. This replaces Cobbler's chain http://${next-server}/cblr/svc/op/gpxe/mac/${net0/mac}.

Roll one pool at a time (the PXE subnets are 198.18.13.0/24–198.18.17.0/24); Puppet autosign already trusts those subnets and *.main.unkin.net, so a host installed via bootapi checks in exactly as before.