diff --git a/k8s/talos-ci-cd/argocd-values.yaml b/k8s/talos-ci-cd/argocd-values.yaml new file mode 100644 index 0000000..b0782e4 --- /dev/null +++ b/k8s/talos-ci-cd/argocd-values.yaml @@ -0,0 +1,139 @@ +# k8s/talos-ci-cd/argocd-values.yaml +# Argo CD — single-replica homelab install (fits the 4-CPU / 32 GB RAM budget). +# Deployed via helmfile (name=argocd) so it sits alongside Authentik, MinIO, etc. +# +# UI is exposed via the cluster Ingress (k8s/ingress/ingress.yaml, +# argocd.riotpiao.homelab.com) over the WireGuard/LAN-only nginx ingress — +# never as a LoadBalancer, since argocd-server holds cluster-admin +# credentials and that Ingress isn't reachable from the WAN. +# +# OIDC + RBAC declared in configs.cm / configs.rbac below — applied by Helm directly. +# Requires: oidc-secret K8s secret (created by helmfile argocd presync hook from env vars). +# CA trust for Forgejo repo clones injected into argocd-tls-certs-cm by postsync hook. + +global: + domain: argocd.riotpiao.homelab.com + +configs: + params: + server.insecure: false # keep TLS on argocd-server even behind the ingress + + cm: + # Must match the Ingress host above and the redirect URI registered in + # Authentik (provision_oidc.py argocd_url) exactly — ArgoCD builds its + # OIDC redirect_uri as "{url}/auth/callback", so any mismatch here is + # what Authentik's "Invalid redirect URL" error means. + url: "https://argocd.riotpiao.homelab.com" + oidc.config: | + name: Authentik + issuer: https://authentik.riotpiao.homelab.com/application/o/argocd/ + clientID: $oidc-secret:client-id + clientSecret: $oidc-secret:client-secret + requestedScopes: [openid, profile, email, groups] + rootCA: | + -----BEGIN CERTIFICATE----- + MIIBbTCCARSgAwIBAgIUNa409I6cGHye4YqeiphmWDaCEXUwCgYIKoZIzj0EAwIw + FTETMBEGA1UEAxMKaG9tZWxhYi1jYTAeFw0yNjA2MTcxNjUzMjVaFw0zNjA2MTQx + NjUzMjVaMBUxEzARBgNVBAMTCmhvbWVsYWItY2EwWTATBgcqhkjOPQIBBggqhkjO + PQMBBwNCAARwkubJPPdhgKcqr+3AEO2tr5I7MhC3zzeAqpmv8glngsweiGznaDhi + Dbf8JFfilbrLEJBSuwHZQPuoNx+3fbvYo0IwQDAOBgNVHQ8BAf8EBAMCAqQwDwYD + VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUod8iYq0+QyetnxfKDprIf3XbWkEwCgYI + KoZIzj0EAwIDRwAwRAIgQ4HOLs5DOqcfAMv8NSImxoYN7TyebnlQAQXSARnIqBMC + IB9RycFvG/rpJuz/LIKi4rf6RARjLcHM/zqhXQJvBw53 + -----END CERTIFICATE----- + + rbac: + policy.csv: | + g, homelab-admins, role:admin + policy.default: role:readonly + +server: + replicas: 1 + deploymentStrategy: + type: Recreate + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + # No Service of type LoadBalancer — port-forward only + service: + type: ClusterIP + podAnnotations: + secret.reloader.stakater.com/reload: "oidc-secret" + configmap.reloader.stakater.com/reload: "argocd-tls-certs-cm,argocd-cm" + metrics: + enabled: true + serviceMonitor: + enabled: true + +repoServer: + replicas: 1 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + metrics: + enabled: true + serviceMonitor: + enabled: true + +applicationSet: + replicas: 1 + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + +controller: + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + metrics: + enabled: true + serviceMonitor: + enabled: true + +redis: + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + +notifications: + enabled: false # add back later if alert routing is needed diff --git a/k8s/talos-ci-cd/charts/forgejo-runner/Chart.yaml b/k8s/talos-ci-cd/charts/forgejo-runner/Chart.yaml new file mode 100644 index 0000000..36e524e --- /dev/null +++ b/k8s/talos-ci-cd/charts/forgejo-runner/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: forgejo-runner +description: Forgejo Actions runner with Docker-in-Docker sidecar for homelab CI/CD +type: application +version: 0.1.0 +appVersion: "6" diff --git a/k8s/talos-ci-cd/charts/forgejo-runner/templates/deployment.yaml b/k8s/talos-ci-cd/charts/forgejo-runner/templates/deployment.yaml new file mode 100644 index 0000000..a87a725 --- /dev/null +++ b/k8s/talos-ci-cd/charts/forgejo-runner/templates/deployment.yaml @@ -0,0 +1,100 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Release.Name }} +spec: + replicas: 1 + strategy: + type: Recreate # RWO PVCs — old pod must terminate before new one mounts them + selector: + matchLabels: + app: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ .Release.Name }} + spec: + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + + initContainers: + - name: register + image: {{ .Values.runner.image.repository }}:{{ .Values.runner.image.tag }} + command: ["sh", "-c"] + args: + - | + test -f /data/.runner || forgejo-runner register --no-interactive \ + --instance {{ .Values.runner.forgejoUrl }} \ + --token $(RUNNER_TOKEN) \ + --name {{ .Values.runner.name }} \ + --labels "{{ .Values.runner.labels }}" + env: + - name: RUNNER_TOKEN + valueFrom: + secretKeyRef: + name: {{ .Values.runner.tokenSecret }} + key: token + volumeMounts: + - name: runner-data + mountPath: /data + - name: homelab-ca + mountPath: /etc/ssl/certs/homelab-ca.pem + subPath: ca.crt + workingDir: /data + + containers: + - name: runner + image: {{ .Values.runner.image.repository }}:{{ .Values.runner.image.tag }} + command: ["sh", "-c", "forgejo-runner daemon"] + workingDir: /data + env: + - name: DOCKER_HOST + value: tcp://localhost:2376 + - name: DOCKER_TLS_VERIFY + value: "1" + - name: DOCKER_CERT_PATH + value: /docker-certs/client + volumeMounts: + - name: runner-data + mountPath: /data + - name: docker-certs + mountPath: /docker-certs + - name: homelab-ca + mountPath: /etc/ssl/certs/homelab-ca.pem + subPath: ca.crt + resources: + {{- toYaml .Values.runner.resources | nindent 12 }} + + - name: dind + image: {{ .Values.dind.image.repository }}:{{ .Values.dind.image.tag }} + securityContext: + privileged: true # required for DinD; cicd namespace is labelled privileged + env: + - name: DOCKER_TLS_CERTDIR + value: /docker-certs + volumeMounts: + - name: docker-certs + mountPath: /docker-certs + - name: dind-storage + mountPath: /var/lib/docker + - name: homelab-ca + mountPath: /etc/ssl/certs/homelab-ca.pem + subPath: ca.crt + resources: + {{- toYaml .Values.dind.resources | nindent 12 }} + + volumes: + - name: runner-data + persistentVolumeClaim: + claimName: runner-reg + - name: dind-storage + persistentVolumeClaim: + claimName: runner-dind + - name: docker-certs + emptyDir: {} # DinD regenerates mTLS certs on each start + - name: homelab-ca + secret: + secretName: homelab-ca diff --git a/k8s/talos-ci-cd/charts/forgejo-runner/templates/networkpolicy.yaml b/k8s/talos-ci-cd/charts/forgejo-runner/templates/networkpolicy.yaml new file mode 100644 index 0000000..96a0414 --- /dev/null +++ b/k8s/talos-ci-cd/charts/forgejo-runner/templates/networkpolicy.yaml @@ -0,0 +1,31 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ .Release.Name }}-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: {{ .Release.Name }} + policyTypes: [Egress] + egress: + # Forgejo — same cicd namespace (git push, registry push/pull) + - to: + - podSelector: {} + # CoreDNS + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + # Internet (action deps, base images) — never LAN or pod network + - to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + - 192.168.1.0/24 + - 10.244.0.0/16 diff --git a/k8s/talos-ci-cd/charts/forgejo-runner/templates/pvc.yaml b/k8s/talos-ci-cd/charts/forgejo-runner/templates/pvc.yaml new file mode 100644 index 0000000..c1ae8d1 --- /dev/null +++ b/k8s/talos-ci-cd/charts/forgejo-runner/templates/pvc.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: runner-reg + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Release.Name }} +spec: + accessModes: [ReadWriteOnce] + storageClassName: {{ .Values.persistence.reg.storageClass }} + resources: + requests: + storage: {{ .Values.persistence.reg.size }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: runner-dind + namespace: {{ .Release.Namespace }} + labels: + app: {{ .Release.Name }} +spec: + accessModes: [ReadWriteOnce] + storageClassName: {{ .Values.persistence.dind.storageClass }} + resources: + requests: + storage: {{ .Values.persistence.dind.size }} diff --git a/k8s/talos-ci-cd/charts/forgejo-runner/values.yaml b/k8s/talos-ci-cd/charts/forgejo-runner/values.yaml new file mode 100644 index 0000000..964a074 --- /dev/null +++ b/k8s/talos-ci-cd/charts/forgejo-runner/values.yaml @@ -0,0 +1,42 @@ +runner: + image: + repository: code.forgejo.org/forgejo/runner + tag: "6" # pin exact release before apply + name: talos-runner + labels: "docker:docker://node:22-bookworm" + forgejoUrl: https://forgejo.riotpiao.homelab.com + # tokenSecret: name of the K8s Secret that holds the runner registration token + # created automatically by the helmfile presync hook (see helmfile.yaml.gotmpl) + tokenSecret: runner-token + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "2" + memory: 4Gi + +dind: + image: + repository: docker + tag: "27-dind" # pin exact release before apply + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "2" + memory: 4Gi + +persistence: + reg: + storageClass: longhorn + size: 1Gi # .runner registration file + config — survives pod restarts + dind: + storageClass: longhorn + size: 30Gi # docker layer cache — keeps rebuilds fast across restarts + +tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule diff --git a/k8s/talos-ci-cd/deploy-scaffold/api/deployment.yaml b/k8s/talos-ci-cd/deploy-scaffold/api/deployment.yaml new file mode 100644 index 0000000..a9ac753 --- /dev/null +++ b/k8s/talos-ci-cd/deploy-scaffold/api/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api + namespace: api +spec: + replicas: 1 + selector: + matchLabels: + app: api + template: + metadata: + labels: + app: api + spec: + containers: + - name: api + # CI bumps this tag on every push to main (ci.yml step "bump deploy repo") + image: forgejo.riotpiao.homelab.com/rock/api:latest + ports: + - containerPort: 8080 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + readinessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 15 + periodSeconds: 30 diff --git a/k8s/talos-ci-cd/deploy-scaffold/api/service.yaml b/k8s/talos-ci-cd/deploy-scaffold/api/service.yaml new file mode 100644 index 0000000..4e52894 --- /dev/null +++ b/k8s/talos-ci-cd/deploy-scaffold/api/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: api + namespace: api +spec: + selector: + app: api + ports: + - name: http + port: 80 + targetPort: 8080 diff --git a/k8s/talos-ci-cd/deploy-scaffold/apps/api.yaml b/k8s/talos-ci-cd/deploy-scaffold/apps/api.yaml new file mode 100644 index 0000000..a9066cc --- /dev/null +++ b/k8s/talos-ci-cd/deploy-scaffold/apps/api.yaml @@ -0,0 +1,22 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: api + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "10" +spec: + project: default + source: + repoURL: https://forgejo.forge.riotpiao.homelab.com/rock/deploy.git + targetRevision: main + path: api + destination: + server: https://kubernetes.default.svc + namespace: api + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/k8s/talos-ci-cd/deploy-scaffold/apps/argocd.yaml b/k8s/talos-ci-cd/deploy-scaffold/apps/argocd.yaml new file mode 100644 index 0000000..f53013d --- /dev/null +++ b/k8s/talos-ci-cd/deploy-scaffold/apps/argocd.yaml @@ -0,0 +1,19 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: argocd + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + project: default + source: + repoURL: https://forgejo.forge.riotpiao.homelab.com/rock/deploy.git + targetRevision: main + path: argocd + destination: + server: https://kubernetes.default.svc + namespace: argocd + # NO syncPolicy.automated — manual sync required. + # Argo CD managing itself auto-synced is a footgun: a misconfigured commit could + # take down the CD system before anyone can intervene. Approve manually. diff --git a/k8s/talos-ci-cd/deploy-scaffold/apps/forge.yaml b/k8s/talos-ci-cd/deploy-scaffold/apps/forge.yaml new file mode 100644 index 0000000..e6641c7 --- /dev/null +++ b/k8s/talos-ci-cd/deploy-scaffold/apps/forge.yaml @@ -0,0 +1,19 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: forge + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + project: default + source: + repoURL: https://forgejo.forge.riotpiao.homelab.com/rock/deploy.git + targetRevision: main + path: forge + destination: + server: https://kubernetes.default.svc + namespace: forge + # NO syncPolicy.automated — manual sync required. + # Forgejo is what CI uses to push commits; auto-sync would let a bad CI commit + # break the very system CI depends on. Approve syncs manually in the Argo CD UI. diff --git a/k8s/talos-ci-cd/example-workflows/ci.yml b/k8s/talos-ci-cd/example-workflows/ci.yml new file mode 100644 index 0000000..60de495 --- /dev/null +++ b/k8s/talos-ci-cd/example-workflows/ci.yml @@ -0,0 +1,81 @@ +# .forgejo/workflows/ci.yml +# +# Copy to YOUR APPLICATION REPO at .forgejo/workflows/ci.yml +# (not this infra repo — this is a template). +# +# What this does (on every push to main): +# 1. Run tests — fail here and nothing ships. +# 2. Build a Docker image and push it to the Forgejo built-in OCI registry. +# 3. Clone rock/deploy, bump the image tag in api/deployment.yaml, push the commit. +# 4. Argo CD sees the commit within 3 minutes and rolls out the new version. +# +# Required repo secrets (Forgejo UI → repo → Settings → Actions → Secrets): +# REGISTRY_TOKEN — ci-bot's package:write Forgejo API token +# DEPLOY_TOKEN — ci-bot's repo:write Forgejo API token (scoped to rock/deploy only) +# +# Prerequisites: +# - ci-bot user created in Forgejo (see IAM section §11.5 of talos_version_control.html) +# - ci-bot added as collaborator on rock/deploy with Write access +# - Runner (cicd ns) is online and registered (Block 3 of build runbook) +# - Talos nodes trust the homelab CA (Block 2 of build runbook) + +on: + push: + branches: [main] + +jobs: + build-push-deploy: + runs-on: docker + + steps: + - uses: actions/checkout@v4 + + # ── 1. Tests ──────────────────────────────────────────────────────────── + - name: test + run: make test # replace with your test command; failure stops the pipeline + + # ── 2. Build + push OCI image ─────────────────────────────────────────── + - name: build and push image + env: + REGISTRY: forgejo.forge.riotpiao.homelab.com + OWNER: rock + run: | + REPO_NAME=${{ github.event.repository.name }} + TAG=$(git rev-parse --short HEAD) + IMAGE="${REGISTRY}/${OWNER}/${REPO_NAME}:${TAG}" + + echo "${{ secrets.REGISTRY_TOKEN }}" \ + | docker login "${REGISTRY}" -u "${OWNER}" --password-stdin + + docker build -t "${IMAGE}" . + docker push "${IMAGE}" + + # Pass values to subsequent steps + echo "TAG=${TAG}" >> "$GITHUB_ENV" + echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV" + echo "REPO_NAME=${REPO_NAME}" >> "$GITHUB_ENV" + + # ── 3. Bump image tag in the deploy repo ──────────────────────────────── + # This is the ONLY write operation CI has on the cluster side. + # Argo CD notices the commit and rolls out the new image. + - name: bump deploy repo + env: + REGISTRY: forgejo.forge.riotpiao.homelab.com + OWNER: rock + run: | + git clone \ + "https://ci-bot:${{ secrets.DEPLOY_TOKEN }}@${REGISTRY}/${OWNER}/deploy.git" \ + /tmp/deploy + + TARGET_FILE="/tmp/deploy/${REPO_NAME}/deployment.yaml" + + sed -i \ + "s|${REGISTRY}/${OWNER}/${REPO_NAME}:.*|${IMAGE}|" \ + "${TARGET_FILE}" + + git -C /tmp/deploy \ + -c user.name="ci-bot" \ + -c user.email="ci-bot@forgejo.forge.riotpiao.homelab.com" \ + commit -am "${REPO_NAME}: deploy ${TAG}" + + git -C /tmp/deploy push diff --git a/k8s/talos-ci-cd/forgejo-values.yaml b/k8s/talos-ci-cd/forgejo-values.yaml new file mode 100644 index 0000000..b3ca058 --- /dev/null +++ b/k8s/talos-ci-cd/forgejo-values.yaml @@ -0,0 +1,151 @@ +# k8s/talos-ci-cd/forgejo-values.yaml +# Forgejo deployed via the gitea-charts/gitea Helm chart with image override. +# Admin password injected via helmfile --set (FORGEJO_ADMIN_PASSWORD in .env). +# Runner is managed by a separate helmfile release (charts/forgejo-runner/). +# +# Chart docs: https://gitea.com/gitea/helm-chart + +# ── Image (Forgejo replaces Gitea — drop-in compatible) ────────────────────── +image: + repository: codeberg.org/forgejo/forgejo + tag: "13" # pin exact release — check codeberg.org/forgejo/forgejo/releases + pullPolicy: IfNotPresent + +# ── Bootstrap admin (provisioned by a post-install Job inside the chart) ────── +gitea: + admin: + username: rock + email: locartrock@gmail.com + # password: injected via helmfile --set (FORGEJO_ADMIN_PASSWORD from .env) + + config: + server: + PROTOCOL: http # nginx ingress handles TLS; pod serves plain HTTP + DOMAIN: forgejo.riotpiao.homelab.com + ROOT_URL: https://forgejo.riotpiao.homelab.com/ + HTTP_PORT: 3000 + START_SSH_SERVER: true + SSH_DOMAIN: forgejo.riotpiao.homelab.com + SSH_PORT: 2222 + SSH_LISTEN_PORT: 2222 + database: + DB_TYPE: sqlite3 + PATH: /data/forgejo.db + repository: + ROOT: /data/git + actions: + ENABLED: true + packages: + ENABLED: true # built-in OCI registry + metrics: + ENABLED: true # Prometheus at /metrics + service: + DISABLE_REGISTRATION: true # no self-signup; Authentik OAuth2 auto-creates accounts + oauth2: + ENABLED: true + PROVIDER: openidconnect + OPENID_CONNECT_DISCOVERY_URL: https://authentik.riotpiao.homelab.com/application/o/forgejo/.well-known/openid-configuration + CLIENT_ID: forgejo + AUTO_DISCOVER_URL: https://authentik.riotpiao.homelab.com/application/o/forgejo/.well-known/openid-configuration + cache: + ADAPTER: memory # no Redis — single-replica SQLite setup + session: + PROVIDER: memory + queue: + TYPE: channel # in-memory queue; no file lock, no LevelDB contention on rollout + + metrics: + enabled: true + serviceMonitor: + enabled: true # kube-prometheus-stack discovers ServiceMonitors cluster-wide + +# ── Persistence (Longhorn RWO — SQLite lives here) ──────────────────────────── +persistence: + enabled: true + storageClass: longhorn + size: 20Gi + accessModes: + - ReadWriteOnce + +# ── Deployment strategy ──────────────────────────────────────────────────────── +# RWO PVC + SQLite: old pod must terminate before new one mounts the volume. +deployment: + strategy: + type: Recreate + env: + - name: SSL_CERT_DIR + value: /homelab-ca + +# ── Cert / CA auto-reload ───────────────────────────────────────────────────── +# nginx serves the wildcard-tls cert — Forgejo itself never reads a TLS secret. +# The only reload trigger is homelab-ca: if the root CA rotates, the mounted +# ConfigMap changes and Forgejo must restart to pick up the new CA bundle for OIDC. +podAnnotations: + configmap.reloader.stakater.com/reload: "homelab-ca" + +# ── Services (Cilium LB-IPAM pins both to 192.168.1.165) ───────────────────── +service: + http: + type: LoadBalancer + port: 3000 + targetPort: 3000 + annotations: + io.cilium/lb-ipam-ips: "192.168.1.165" + io.cilium/lb-ipam-sharing-key: "forgejo" + ssh: + type: LoadBalancer + port: 2222 + targetPort: 2222 + annotations: + io.cilium/lb-ipam-ips: "192.168.1.165" + io.cilium/lb-ipam-sharing-key: "forgejo" + +# ── Resources ───────────────────────────────────────────────────────────────── +resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "1" + memory: 1Gi + +# ── Node resilience ─────────────────────────────────────────────────────────── +tolerations: + - key: node-role.kubernetes.io/control-plane + operator: Exists + effect: NoSchedule + +# ── CA trust ───────────────────────────────────────────────────────────────── +# Go reads SSL_CERT_DIR as an additional cert directory ON TOP OF the default +# cert files (ca-certificates.crt stays intact — no init container needed). +# Setting SSL_CERT_DIR=/homelab-ca makes Go also read homelab-ca.crt from there, +# trusting both the standard Mozilla bundle and our homelab CA. +# Required for OIDC: Forgejo fetches Authentik's discovery endpoint which +# presents a cert signed by homelab-ca. +extraVolumes: + - name: homelab-ca + configMap: + name: homelab-ca + +extraVolumeMounts: + - name: homelab-ca + mountPath: /homelab-ca + readOnly: true + +# ── Ingress: disabled — rule lives in k8s/ingress/ingress.yaml ─────────────── +ingress: + enabled: false + +# ── Bundled databases + cache: all disabled — SQLite + memory is the chosen backend ── +postgresql: + enabled: false +postgresql-ha: + enabled: false +mysql: + enabled: false +redis-cluster: + enabled: false # 6-node cluster is overkill for single-replica SQLite Forgejo + +# ── Act runner subchart: disabled — managed by the forgejo-runner helmfile release +act_runner: + enabled: false diff --git a/k8s/talos-ci-cd/talos-cli.sh b/k8s/talos-ci-cd/talos-cli.sh new file mode 100755 index 0000000..0bff8d7 --- /dev/null +++ b/k8s/talos-ci-cd/talos-cli.sh @@ -0,0 +1,49 @@ +# 0. Source your env (Authentik bootstrap token + Vault addr) +set -a && source ~/.authentik/.env && set +a +VAULT_ADDR="http://vault.riotpiao.homelab.com" +AUTHENTIK_URL="http://authentik.riotpiao.homelab.com" + +# 1. Fetch the real client_id + client_secret from Authentik +PROVIDER_PK=$(curl -s \ + -H "Authorization: Bearer ${AUTHENTIK_BOOTSTRAP_TOKEN}" \ + "${AUTHENTIK_URL}/api/v3/providers/oauth2/?name=talos-cli-shell" \ + | python3 -c "import json,sys; r=json.load(sys.stdin)['results']; print(r[0]['pk'] if r else 'NOT_FOUND')") + +echo "Provider PK: ${PROVIDER_PK}" + +CLIENT_ID=$(curl -s \ + -H "Authorization: Bearer ${AUTHENTIK_BOOTSTRAP_TOKEN}" \ + "${AUTHENTIK_URL}/api/v3/providers/oauth2/${PROVIDER_PK}/" \ + | python3 -c "import json,sys; p=json.load(sys.stdin); print(p['client_id'])") + +CLIENT_SECRET=$(curl -s \ + -H "Authorization: Bearer ${AUTHENTIK_BOOTSTRAP_TOKEN}" \ + "${AUTHENTIK_URL}/api/v3/providers/oauth2/${PROVIDER_PK}/" \ + | python3 -c "import json,sys; p=json.load(sys.stdin); print(p['client_secret'])") + +echo "client_id: ${CLIENT_ID}" + +# 2. Get a JWT from Authentik (client credentials flow) +# JWT=$(curl -s -X POST \ +# "${AUTHENTIK_URL}/application/o/talos-cli-shell/token/" \ +# -d "grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&scope=openid" \ +# | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('access_token', d))") +# echo "JWT: ${JWT}..." + +curl -v -s -X POST \ + "${AUTHENTIK_URL}/application/o/homelab-mac-cli/token/" \ + -d "grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&scope=openid" + +# # 3. Exchange JWT for a Vault token +# VAULT_TOKEN=$(curl -s -X POST \ +# "${VAULT_ADDR}/v1/auth/jwt/login" \ +# -H "Content-Type: application/json" \ +# -d "{\"jwt\": \"${JWT}\", \"role\": \"shell\"}" \ +# | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('auth',{}).get('client_token', d))") + +# echo "Vault token: ${VAULT_TOKEN:0:20}..." + +# # 4. Verify the token works +# curl -s -H "X-Vault-Token: ${VAULT_TOKEN}" \ +# "${VAULT_ADDR}/v1/auth/token/lookup-self" \ +# | python3 -c "import json,sys; d=json.load(sys.stdin); print('policies:', d['data']['policies'])"