- Shell 100%
| resources | ||
| .gitlab-ci.yml | ||
| .sops.yaml | ||
| kustomization.yaml | ||
| kustomize-build.sh | ||
| README.md | ||
| renovate.json | ||
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.yamlrunner.labels - Keine Node.js-fähigen Runner-Images (Standard-Actions wie
actions/checkoutwerden 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: truenur auf dem DinD-Container (unvermeidbar für nested containers)- Eigene PVC für DinD-Layer (
/var/lib/docker) — getrennt von Runner-Daten →resources/deployment.yamlContainerdind+ Volumedind-storage container.docker_hostverbindet den Runner selbst mit DinD auflocalhost:2376, waehrend Job-Container DinD ueber einen explizitendocker-Alias mit passendem TLS-Zertifikatsnamen erreichencontainer.optionsmountet die TLS-Client-Zertifikate read-only nach/certs/client, damit Docker-Clients in Job-Containern denselben DinD-Endpunkt sicher nutzen koennen →resources/configmap-config.yamlcontainer.docker_host+runner.envsautomountServiceAccountToken: falseverhindert, 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.iofür das tatsächlich genutzte Imageghcr.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