Forgejo Runner deployment (raw Kustomize + DinD sidecar)
Find a file
2026-05-20 10:00:38 +02:00
resources fix(dind): set bridge MTU=1420 to match Flannel WireGuard pod MTU 2026-05-18 22:08:15 +02:00
.gitlab-ci.yml refactor: raw Kustomize with runner v7.0.0 + server.connections config 2026-05-06 12:28:05 +02:00
.sops.yaml feat: initial Forgejo Runner deployment repo (Phase 1.3) 2026-05-06 10:11:27 +02:00
kustomization.yaml refactor: Dagger-only runner with DinD isolation and NetworkPolicy 2026-05-06 13:41:33 +02:00
kustomize-build.sh chore: add renovate annotations for Docker image tracking in kustomize-build.sh 2026-05-20 10:00:38 +02:00
README.md fix(runner): use DinD certificate hostname for Docker TLS 2026-05-08 13:22:38 +02:00
renovate.json fix(renovate): extend kubernetes-deployment preset 2026-05-18 14:45:36 +02:00

forgejo-runner

Forgejo Actions runner for git.xarif.de.

Anforderungen und Entscheidungen

1. Ausschließlich Dagger-Pipelines

Pipelines werden nur mit Dagger geschrieben — keine GitHub-Actions-Kompatibilität nötig. Daraus folgt:

  • Single Label mit minimalem Dagger-Image statt mehrerer ubuntu-Varianten → resources/configmap-config.yaml runner.labels
  • Keine Node.js-fähigen Runner-Images (Standard-Actions wie actions/checkout werden nicht verwendet)
  • Runner-Name: k8s-dagger-runner

2. Bestmögliche Isolation vom Host und Cluster

CI-Jobs und Docker-Builds dürfen keine Angriffsmöglichkeit auf das Hostsystem oder andere Cluster-Services bieten.

Docker-in-Docker (DinD) statt Host-Socket-Mount:

  • Eigener Docker-Daemon im Sidecar — kein Zugriff auf den Host-Daemon
  • privileged: true nur auf dem DinD-Container (unvermeidbar für nested containers)
  • Eigene PVC für DinD-Layer (/var/lib/docker) — getrennt von Runner-Daten → resources/deployment.yaml Container dind + Volume dind-storage
  • container.docker_host verbindet den Runner selbst mit DinD auf localhost:2376, waehrend Job-Container DinD ueber einen expliziten docker-Alias mit passendem TLS-Zertifikatsnamen erreichen
  • container.options mountet die TLS-Client-Zertifikate read-only nach /certs/client, damit Docker-Clients in Job-Containern denselben DinD-Endpunkt sicher nutzen koennen → resources/configmap-config.yaml container.docker_host + runner.envs
  • automountServiceAccountToken: false verhindert, dass CI-Jobs unnoetig ein Kubernetes ServiceAccount Token im Runner-Pod vorfinden → resources/deployment.yaml

NetworkPolicy blockiert Egress zu allen Cluster-internen CIDRs:

  • Blocked: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16
  • Erlaubt: kube-dns, Forgejo-Pod (Port 3000), Internet
  • Effekt: Cluster-interne Pod- und Service-CIDRs sind blockiert; oeffentliche Node-Endpunkte bleiben damit aber noch erreichbar und muessen fuer eine haertere Isolation separat bewertet werden → resources/networkpolicy.yaml

Interne Kommunikation: Runner verbindet sich über den Cluster-internen Service (forgejo-http.forgejo.svc.cluster.local:3000) statt über die externe URL — vermeidet Hairpin-NAT-Probleme und ist unabhängig von Ingress/TLS. → resources/deployment.yaml --instance im Register-Command

3. Renovate-Integration

Image-Tags in den Runner-Labels werden von Renovate automatisch aktualisiert. Dafür:

  • Labels stehen in einer Klartext-ConfigMap (nicht im SOPS-Secret) — Renovate kann sie lesen → resources/configmap-config.yaml
  • Custom Regex-Manager liest Tags direkt aus ghcr.io für das tatsächlich genutzte Image ghcr.io/dagger/engine. Die globale Renovate-Konfiguration liefert dafür die nötige GHCR-Authentifizierung via GitHub PAT. → renovate.json
  • Token bleibt separat SOPS-verschlüsselt → resources/secret-token.yaml

Architektur

┌─────────────────────────────────────────────────┐
│ Pod: forgejo-runner                             │
│                                                 │
│  ┌─────────────┐       ┌─────────────────────┐ │
│  │   runner    │──TLS──▶│   dind (privileged) │ │
│  │ forgejo/    │       │   docker:28-dind     │ │
│  │ runner:8.0  │       │                     │ │
│  └──────┬──────┘       └──────────┬──────────┘ │
│         │                         │             │
│    /data (.runner, cache)    /var/lib/docker    │
│    PVC: runner-data (1Gi)    PVC: dind (10Gi)  │
└─────────────────────────────────────────────────┘
         │
         ▼ (NetworkPolicy: nur DNS + Forgejo + Internet)
   forgejo-http.forgejo.svc:3000

Token-Rotation

Nötig nach Forgejo-DB-Reset oder Runner-Deregistrierung:

ADMIN_PASS=$(kubectl get secret forgejo-admin-user -n forgejo -o jsonpath='{.data.password}' | base64 -d)
TOKEN=$(curl -s -u "xarif-admin:${ADMIN_PASS}" "https://git.xarif.de/api/v1/admin/runners/registration-token" | jq -r '.token')
cd ~/git/xarif.de/kubernetes/forgejo-runner
sops -d resources/secret-token.yaml > /tmp/t.yaml
sed -i '' "s|token:.*|token: ${TOKEN}|" /tmp/t.yaml
sops -e /tmp/t.yaml > resources/secret-token.yaml && rm /tmp/t.yaml
# Commit, push, delete runner-data PVC (stale .runner file), ArgoCD syncs