Files
bootapi/docs/deployment.md
T
unkinben 274c480b09
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
Initial bootapi: NetBox-driven PXE/kickstart boot service
bootapi replaces Cobbler's PXE/kickstart side. It resolves a PXE-booting host
from NetBox (by MAC or hostname), renders an iPXE boot script and a kickstart
from Go text/templates, and serves them over HTTP. The ENC half already moved to
encapi; this covers the provisioning/boot half.

What's here:
- cmd/bootapi + internal/{config,model,netbox,render,server}; embedded default
  templates under templates/ (AlmaLinux 9 + Fedora kickstarts, iPXE boot +
  unknown-MAC fallbacks) ported from Cobbler's boot/bootstrap contract.
- NetBox client (v4.x API) behind a Resolver interface with a short-TTL cache;
  tested against httptest fixtures using real NetBox JSON shapes.
- chi HTTP server: /ipxe/{mac}, /boot/ipxe?mac=, /ks/{ident}, healthz/readyz,
  Prometheus /metrics. Unknown MAC -> safe fallback iPXE (200), unknown KS -> 404.
- Secrets (root pw hash, ssh keys) injected at render time from env/Vault, never
  NetBox. Config is env-based per estate convention.
- Makefile (build/test/lint/docker + patch/minor/major), Dockerfile (distroless),
  .woodpecker (pre-commit, golangci-lint v2 + go test -race, docker build on PR;
  image push + Gitea binary release on v* tag), docs/ and example config.

go build/vet clean, go test -race green, golangci-lint v2 clean, pre-commit clean.

Claude-Session: https://claude.ai/code/session_015ur3i7D2azsMAWTSVABApv
2026-07-28 20:57:11 +10:00

80 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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, read-only** NetBox API token for bootapi
(create a `bootapi` NetBox user/token via terraform-netbox rather than
reusing the seeded superuser token at
`kv/kubernetes/namespace/netbox/default/netbox-superuser`).
- `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. **ConfigMap** `bootapi-templates` (optional) — override `*.ks.tmpl` /
`*.ipxe.tmpl`, mounted at `BOOTAPI_TEMPLATE_DIR=/etc/bootapi/templates`. Omit
to use the embedded defaults. Annotate the Deployment with
`reloader.stakater.com/auto: "true"` so template edits roll the pods.
5. **Deployment** — image above, env from `config.example.env`, secret keys wired
as `BOOTAPI_NETBOX_TOKEN_FILE`/`BOOTAPI_ROOT_PASSWORD_HASH_FILE` (mount the
Secret) or `...FROM secretKeyRef`. Least-privilege securityContext
(`runAsNonRoot`, `drop: [all]`). Baseline resources: requests `512Mi`/`1`,
limits `2Gi`/`2` cpu.
6. **Service** `bootapi` (ClusterIP, port 80 → 8000) plus a **LoadBalancer** (or
Gateway HTTPRoute) reachable by PXE clients at a stable address/hostname —
this is what DHCP points at. Reuse the Vault-issued TLS the Cobbler vhost used
if you terminate TLS at a gateway; note that iPXE fetches are plain HTTP, so a
plain HTTP listener on the PXE VLAN is required either way.
7. Register in `argocd/applicationsets/platform.yaml` (`apps/overlays/*/bootapi`)
and the platform AppProject destinations.
### Cross-repo dependencies (per estate conventions)
- **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 read-only `bootapi` NetBox token and seed it
(plus `root_password_hash`) into the Vault kv path above.
## 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.