From e9ce0dcf9b26bac9f428944db850178f1afac953 Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 9 Nov 2026 10:58:14 +1000 Subject: [PATCH 1/2] ci: use the remote buildx driver, and build amd64 only The build job failed at "Set up Docker Buildx": Error: ERROR: error for bootstrap "builder-...": Get "https://192.168.178.160:6443/apis/apps/v1/namespaces/gitea-runner/deployments/builder-...": tls: failed to verify certificate: x509: certificate signed by unknown authority Cause ------ The `kubernetes` buildx driver talks to the cluster API to create its builder pods. That needs the k3s CA certificate, which the job container does not have - the `catthehacker/ubuntu:act-latest` image ships the public CA set only. So the bootstrap could not verify the API server. Fix --- Switch to the `remote` driver, which talks straight to the buildkit daemon already running in the cluster: driver: remote endpoint: tcp://buildkit.gitea-runner.svc:1234 No cluster API access is needed at all, so no kubeconfig is needed for the build. This is the same pattern the wedding-photos workflow uses. `endpoint` must be a top-level key, not a driver-opt: the action appends it as the POSITIONAL argument to `buildx create --driver remote `, and passing it in driver-opts yields "ERROR: no remote endpoint provided". Also ---- The build was multi-arch (linux/amd64,linux/arm64), which runs qemu emulation and crashes the buildkit pod on this NUC (exit 255, SandboxChanged), after which the build hangs for many minutes. The cluster is amd64, so the image is built for linux/amd64 only. Verified: `docker buildx create --driver remote tcp://buildkit.gitea-runner.svc:1234` succeeds from the runner's dind container and reports buildkit v0.29.0 with linux/amd64 among its supported platforms. --- .gitea/workflows/build_push.yml | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/build_push.yml b/.gitea/workflows/build_push.yml index 159937d..deaec02 100644 --- a/.gitea/workflows/build_push.yml +++ b/.gitea/workflows/build_push.yml @@ -41,16 +41,28 @@ jobs: - name: Create Kubeconfig run: | - mkdir $HOME/.kube + mkdir -p $HOME/.kube echo "${{ secrets.KUBEC_CONFIG_BUILDX }}" > $HOME/.kube/config - - name: Set up Docker Buildx + - name: Set up Docker Buildx (remote -> persistent buildkit daemon) uses: docker/setup-buildx-action@v3 with: - driver: kubernetes - driver-opts: | - namespace=gitea-runner - qemu.install=true + # The `kubernetes` driver talks to the cluster API, which needs the + # k3s CA certificate. The job container does not have it, so the + # bootstrap failed with "x509: certificate signed by unknown + # authority". + # + # The `remote` driver talks straight to the buildkit daemon that is + # already running in the cluster, so no cluster API access (and no + # kubeconfig) is needed at all. This is the same pattern the + # wedding-photos workflow uses successfully. + # + # NOTE: `endpoint` must be passed as a top-level key. The action + # appends it as the POSITIONAL argument to + # `buildx create --driver remote `; putting it in + # driver-opts instead yields "ERROR: no remote endpoint provided". + driver: remote + endpoint: tcp://buildkit.gitea-runner.svc:1234 - name: Login to Docker Registry uses: docker/login-action@v3 @@ -65,7 +77,10 @@ jobs: context: . push: true file: base.Dockerfile - platforms: linux/amd64,linux/arm64 + # amd64 only. A multi-arch build runs qemu emulation, which crashes + # the buildkit pod on this NUC (exit 255, SandboxChanged) and the + # build then hangs for many minutes. The cluster is amd64. + platforms: linux/amd64 tags: | git.aridgwayweb.com/armistace/beer_base_image:latest @@ -75,6 +90,6 @@ jobs: context: . push: true file: flask.Dockerfile - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 tags: | git.aridgwayweb.com/armistace/beer-data:latest From a48fb1c566e099f7e0f48ab1e48f4798662d8a8c Mon Sep 17 00:00:00 2001 From: hermes Date: Mon, 9 Nov 2026 11:01:59 +1000 Subject: [PATCH 2/2] ci: drop the kubeconfig from the build job The independent review flagged that the build job wrote the `KUBEC_CONFIG_BUILDX` secret to disk even though the build does not need cluster access at all. That secret is cluster-admin-equivalent: a build step that never talks to the cluster should not be able to read it, and a compromised dependency in the build would otherwise have a direct path to the cluster. The "Create Kubeconfig" step is removed from the build job. The deploy step, added in a later PR, gets its own job with the kubeconfig - so the privilege is only present where it is actually used. Also checked the buildkit daemon's resources in response to the review: it already has limits (2 CPU, 4Gi memory, 2Gi ephemeral storage) and requests (500m, 1Gi). The crash that motivated the amd64-only change was qemu exhausting memory during multi-arch emulation, not an unconstrained daemon, so no change is needed there. --- .gitea/workflows/build_push.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/build_push.yml b/.gitea/workflows/build_push.yml index deaec02..d7f41b6 100644 --- a/.gitea/workflows/build_push.yml +++ b/.gitea/workflows/build_push.yml @@ -28,6 +28,11 @@ jobs: . .venv/bin/activate python -m pytest tests/ -q + # NOTE: the build job deliberately does NOT have a kubeconfig. The + # `KUBEC_CONFIG_BUILDX` secret is cluster-admin-equivalent, and a build step + # that has no need of the cluster should not be able to read it. The deploy + # step, added in a later PR, gets its own job with the kubeconfig - that way + # a compromised dependency in the build cannot reach the cluster. build: name: Build and push image runs-on: ubuntu-latest @@ -39,11 +44,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Create Kubeconfig - run: | - mkdir -p $HOME/.kube - echo "${{ secrets.KUBEC_CONFIG_BUILDX }}" > $HOME/.kube/config - - name: Set up Docker Buildx (remote -> persistent buildkit daemon) uses: docker/setup-buildx-action@v3 with: