# Reproducible dev toolbox for the home-network-k3s platform (Phase 1+). # Bakes the NON-Python prereqs (OpenTofu, age, sops, flux) plus ansible + kube tooling # AND the pinned conformance-harness toolchain (kyverno CLI, kyverno-json, chainsaw, # yamllint, check-jsonschema, yq, promtool) into a single pinned baseline image, so `make -C ci gate` # runs identically here and in CI. This is the Proxmox (bpg/proxmox) toolbox + conformance # harness: OpenTofu drives the Proxmox-hosted k3s cluster via the bpg provider (dev.sh opens # an SSH tunnel to the kube-API), NOT a local libvirt/KVM host. The libvirt-client/qemu-img # packages are retained as harmless disk-image utilities. Fedora base is pinned for # reproducibility. ARG FEDORA_VERSION=44 FROM registry.fedoraproject.org/fedora:${FEDORA_VERSION} ARG OPENTOFU_VERSION ARG AGE_VERSION ARG SOPS_VERSION ARG FLUX_VERSION ARG KUBECTL_VERSION ARG HELM_VERSION # Conformance test toolchain (pinned to match ci/versions.env). ARG KYVERNO_VERSION ARG KYVERNO_JSON_VERSION ARG CHAINSAW_VERSION ARG YAMLLINT_VERSION ARG YQ_VERSION ARG CHECK_JSONSCHEMA_VERSION # promtool: the engine that evaluates the alert-rule UNIT TESTS (platform/**/*.promtool-test.yaml). # Pinned to the Prometheus version prod actually runs — see versions.env for why that lockstep is # the point, and for the live-cluster command that establishes it. ARG PROMTOOL_VERSION ARG TARGETARCH=amd64 # Distro packages: libvirt client libs (harmless disk-image utilities), ansible, ssh, # disk-image + cloud-init seed tooling, python3-pip (for the pip-installed test tools), # diffutils (provides `diff`, used by L1 golden render checks), and common utilities. # # wireguard-tools + qrencode serve the REMOTE-ACCESS VPN (platform/vpn-ingress): # * wireguard-tools gives `wg genkey` / `wg pubkey`, needed to mint the server key and to derive # every client profile's `[Peer] PublicKey` (infra/vpn-ingress/new-peer.sh). # * qrencode renders a client profile as a QR code, which is how a PHONE gets enrolled. # They are here rather than on the operator's host because this image is THE toolchain # (CLAUDE.md §5; README "Don't install the toolchain on the host"). That rule was briefly broken # by a runbook that told the operator to run `wg` on the host and `sops` in here, and then split # every step across the boundary to work around it — a procedure nobody should have to follow, and # one that cannot be reproduced in CI. Both tools in one image means `sops -d … | wg pubkey` is # just a pipeline again. # Unpinned on purpose, like every other dnf package here: the Fedora base is what is pinned # (FEDORA_VERSION), and these are not gate tools whose version changes what green means. RUN dnf -y install --setopt=install_weak_deps=False \ libvirt-client qemu-img cloud-utils genisoimage openssh-clients \ ansible-core python3-libvirt python3-pip diffutils \ wireguard-tools qrencode \ git make jq unzip tar gzip xz which ca-certificates curl \ && dnf clean all # Pinned Go binaries from official releases. RUN set -eux; \ curl -fsSL "https://github.com/opentofu/opentofu/releases/download/v${OPENTOFU_VERSION}/tofu_${OPENTOFU_VERSION}_linux_${TARGETARCH}.zip" -o /tmp/tofu.zip; \ unzip -d /usr/local/bin /tmp/tofu.zip tofu; chmod +x /usr/local/bin/tofu; \ curl -fsSL "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-${TARGETARCH}.tar.gz" | tar -xz -C /tmp; \ install -m0755 /tmp/age/age /tmp/age/age-keygen /usr/local/bin/; \ curl -fsSL "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.${TARGETARCH}" -o /usr/local/bin/sops; chmod +x /usr/local/bin/sops; \ curl -fsSL "https://github.com/fluxcd/flux2/releases/download/v${FLUX_VERSION}/flux_${FLUX_VERSION}_linux_${TARGETARCH}.tar.gz" | tar -xz -C /usr/local/bin flux; \ curl -fsSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" -o /usr/local/bin/kubectl; chmod +x /usr/local/bin/kubectl; \ curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-${TARGETARCH}.tar.gz" | tar -xz -C /tmp; \ install -m0755 "/tmp/linux-${TARGETARCH}/helm" /usr/local/bin/helm; \ rm -rf /tmp/* # Pinned conformance test toolchain (L0/L1 static gate + the L2/L3 Chainsaw runner). # Arch strings differ per tool: yq/chainsaw/kyverno-json use ${TARGETARCH} (amd64), the # kyverno CLI tarball uses x86_64 — map amd64 -> x86_64 for it only. RUN set -eux; \ python3 -m pip install --no-cache-dir --break-system-packages \ "yamllint==${YAMLLINT_VERSION}" "check-jsonschema==${CHECK_JSONSCHEMA_VERSION}"; \ curl -fsSL "https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_linux_${TARGETARCH}" -o /usr/local/bin/yq; \ chmod +x /usr/local/bin/yq; \ curl -fsSL "https://github.com/kyverno/chainsaw/releases/download/v${CHAINSAW_VERSION}/chainsaw_linux_${TARGETARCH}.tar.gz" | tar -xz -C /usr/local/bin chainsaw; \ KYVERNO_CLI_ARCH="$([ "${TARGETARCH}" = amd64 ] && echo x86_64 || echo "${TARGETARCH}")"; \ curl -fsSL "https://github.com/kyverno/kyverno/releases/download/v${KYVERNO_VERSION}/kyverno-cli_v${KYVERNO_VERSION}_linux_${KYVERNO_CLI_ARCH}.tar.gz" | tar -xz -C /usr/local/bin kyverno; \ curl -fsSL "https://github.com/kyverno/kyverno-json/releases/download/v${KYVERNO_JSON_VERSION}/kyverno-json_linux_${TARGETARCH}.tar.gz" | tar -xz -C /usr/local/bin kyverno-json; \ PROM_TARBALL="prometheus-${PROMTOOL_VERSION}.linux-${TARGETARCH}"; \ curl -fsSL "https://github.com/prometheus/prometheus/releases/download/v${PROMTOOL_VERSION}/${PROM_TARBALL}.tar.gz" \ | tar -xz -C /usr/local/bin --strip-components=1 "${PROM_TARBALL}/promtool"; \ chmod +x /usr/local/bin/yq /usr/local/bin/chainsaw /usr/local/bin/kyverno /usr/local/bin/kyverno-json \ /usr/local/bin/promtool; \ rm -rf /tmp/* # Record the baked toolchain for provenance. RUN { echo "opentofu=${OPENTOFU_VERSION}"; echo "age=${AGE_VERSION}"; echo "sops=${SOPS_VERSION}"; \ echo "flux=${FLUX_VERSION}"; echo "kubectl=${KUBECTL_VERSION}"; echo "helm=${HELM_VERSION}"; \ echo "kyverno=${KYVERNO_VERSION}"; echo "kyverno-json=${KYVERNO_JSON_VERSION}"; \ echo "chainsaw=${CHAINSAW_VERSION}"; echo "yamllint=${YAMLLINT_VERSION}"; \ echo "yq=${YQ_VERSION}"; echo "check-jsonschema=${CHECK_JSONSCHEMA_VERSION}"; \ echo "promtool=${PROMTOOL_VERSION}"; \ } > /etc/dev-toolchain-versions WORKDIR /work CMD ["/bin/bash"]