feat(images): build on rootless BuildKit over mTLS; verification moves into Containerfile verify stages
Operator decision on PR #81: no CI job may reach a privileged daemon. The act runner is untouched. - platform/buildkit (+ clusters/base/buildkit.yaml): moby/buildkit v0.34.1-rootless pinned by digest, uid 1000, not privileged; caps drop ALL + SETUID/SETGID, seccomp/AppArmor Unconfined, read-only rootfs (each measured under podman). cert-manager CA + server/client certs, ClusterIP :1234, default-deny with ingress only from the act-runner pod (DinD masquerades jobs to it). No Kyverno exception: every applied policy matches app-namespace=true only. - Each Containerfile names its shipped stage and ends in a `verify` stage holding every former `docker run` assertion (uid 65532, RUN --network=none, writable-path inventory standing in for the read-only rootfs). The old `&& echo` checks that could never fail are real assertions now. - images.sh drives buildkitd with a pinned buildx (sha256-checked) via bake; PRs build verify targets with agent-runner FROM the in-run toolbox (target: context); main verifies, then pushes. Fails loudly when the builder secrets are absent or buildkitd is unreachable. - infra/gitea/buildkit-client-secret.sh copies the client cert into Actions secrets. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
65a7769f55
commit
310deb0f7d
28 files changed
+1490
-608
No files matched your search
@@ -0,0 +1,88 @@
|
||||
---
|
||||
# Default-deny for the namespace, then two narrow allows.
|
||||
#
|
||||
# WHO MAY CONNECT — AND WHY THE ACT-RUNNER POD IS THE RIGHT SOURCE (measured 2026-10-29).
|
||||
# CI jobs do not run as pods. act_runner starts each job as a container on its DinD sidecar, on a
|
||||
# docker bridge network INSIDE the act-runner pod's network namespace, and dockerd masquerades it:
|
||||
# kubectl -n gitea-actions exec deploy/act-runner -c dind -- iptables -t nat -S POSTROUTING
|
||||
# -A POSTROUTING -s 172.18.0.0/16 ! -o br-c0f13d041ad6 -j MASQUERADE
|
||||
# -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
|
||||
# So a job's connection leaves with the act-runner POD's IP and therefore Cilium's identity for
|
||||
# that pod (namespace gitea-actions, app.kubernetes.io/name=act-runner). That is exactly what the
|
||||
# ingress rule names. It cannot distinguish one job from another, or a job from act_runner
|
||||
# itself — per-job authentication is the mTLS client certificate, not this policy. What the
|
||||
# policy adds is that NOTHING ELSE in the cluster can even open the port.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: buildkit
|
||||
labels:
|
||||
homelab.keithjackson.dev/baseline: default-deny
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress: []
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: buildkitd-allow-ci-ingress
|
||||
namespace: buildkit
|
||||
labels:
|
||||
app.kubernetes.io/name: buildkitd
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: buildkitd
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
io.kubernetes.pod.namespace: gitea-actions
|
||||
app.kubernetes.io/name: act-runner
|
||||
toPorts:
|
||||
- ports:
|
||||
- { port: "1234", protocol: TCP }
|
||||
---
|
||||
# WHAT A BUILD NEEDS TO REACH. Build steps download (dnf mirrors, GitHub releases, dl.k8s.io,
|
||||
# npm, PyPI, Fedora/Docker registries) and the result is pushed to the Gitea registry.
|
||||
# * world 443/80: the downloads. 80 because Fedora's mirror list includes plain-http mirrors
|
||||
# (dnf verifies package GPG signatures, not transport).
|
||||
# * ingress (the Gateway's Envoy, reserved:ingress): the push. gitea.home.keithjackson.dev
|
||||
# resolves in-cluster to the Gateway VIP (split-horizon CoreDNS), and Cilium hands that
|
||||
# traffic to the Gateway, whose identity is `ingress` — the same identity gitea's own
|
||||
# ingress allow admits.
|
||||
# Nothing else in the cluster: not the API server, not other pods. A build step is code from a
|
||||
# pull request.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: buildkitd-allow-egress
|
||||
namespace: buildkit
|
||||
labels:
|
||||
app.kubernetes.io/name: buildkitd
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: buildkitd
|
||||
egress:
|
||||
- toEntities:
|
||||
- world
|
||||
toPorts:
|
||||
- ports:
|
||||
- { port: "443", protocol: TCP }
|
||||
- { port: "80", protocol: TCP }
|
||||
- toEntities:
|
||||
- ingress
|
||||
Reference in new issue
Block a user