fix(bootstrap): complete Phase 4 ArgoCD bootstrap with all permanent fixes
- Fix ArgoCD Application schema: move syncOptions under syncPolicy (00-secrets.yaml) - Remove helm install --wait flag (talos-cp-2 slow node timeout issue) - Add comprehensive progress logging with timestamps to bootstrap.sh - Fix SOPS key path (/Users/rockliang/.sops/key.txt, not homelab-age.key) - Add local SOPS decryption for bootstrap secrets - Add CNPG NetworkPolicy allowing app→database connectivity - Disable Forgejo bundled dependencies (saves 66Gi storage) - Inject database credentials via deployment.env (GITEA__DATABASE__*) - Remove invalid ext4 mount options from StorageClass - Add namespace manifests with PodSecurity labels - Add encrypted forgejo-admin secret (SOPS) - Reduce forgejo-db size 50Gi→25Gi per instance - Prepare ArgoCD SOPS CMP plugin (for post-bootstrap)
This commit is contained in:
+155
-33
@@ -19,7 +19,7 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BOOT="$SCRIPT_DIR/k8s/bootstrap"
|
||||
SOPS_KEY="${SOPS_KEY:-$HOME/.sops/homelab-age.key}"
|
||||
SOPS_KEY="${SOPS_KEY:-$HOME/.sops/key.txt}"
|
||||
DEPLOY_KEY="${DEPLOY_KEY:-$HOME/.ssh/argocd_seed}"
|
||||
GITHUB_SSH="[email protected]:Riotpiaole/riotpiao.homelab.com.git"
|
||||
|
||||
@@ -27,6 +27,13 @@ log() { echo "[$(date +%H:%M:%S)] $*"; }
|
||||
die() { echo "ERROR: $*" >&2; exit 1; }
|
||||
phase(){ echo; echo "━━━ $* ━━━"; echo; }
|
||||
|
||||
# Idempotent helm repo setup
|
||||
ensure_helm_repo() {
|
||||
local name=$1 url=$2
|
||||
helm repo list 2>/dev/null | grep -q "^$name" || helm repo add "$name" "$url" >/dev/null
|
||||
helm repo update "$name" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
preflight() {
|
||||
log "preflight…"
|
||||
kubectl cluster-info >/dev/null || die "kubectl not configured / cluster unreachable"
|
||||
@@ -39,7 +46,7 @@ preflight() {
|
||||
p1_cilium() {
|
||||
phase "PHASE 1a: CNI (Cilium)"
|
||||
if kubectl -n kube-system get ds cilium >/dev/null 2>&1; then log "cilium present, skip"; return; fi
|
||||
helm repo add cilium https://helm.cilium.io >/dev/null; helm repo update >/dev/null
|
||||
ensure_helm_repo cilium https://helm.cilium.io
|
||||
helm install cilium cilium/cilium -n kube-system \
|
||||
--set ipam.mode=kubernetes --set kubeProxyReplacement=true --wait --timeout 10m
|
||||
log "✅ cilium installed"
|
||||
@@ -47,60 +54,175 @@ p1_cilium() {
|
||||
|
||||
p1_longhorn() {
|
||||
phase "PHASE 1b: STORAGE (Longhorn)"
|
||||
if helm -n longhorn-system list 2>/dev/null | grep -q longhorn; then log "longhorn present, skip"; return; fi
|
||||
helm repo add longhorn https://charts.longhorn.io >/dev/null; helm repo update >/dev/null
|
||||
helm install longhorn longhorn/longhorn -n longhorn-system --create-namespace \
|
||||
--values "$BOOT/phase1-storage/longhorn-values.yaml" --wait --timeout 10m
|
||||
kubectl -n longhorn-system wait --for=condition=available --timeout=300s deploy/longhorn-manager
|
||||
|
||||
# Always ensure namespace + StorageClasses (idempotent, resumable)
|
||||
kubectl apply -f "$BOOT/phase1-storage/namespace.yaml"
|
||||
|
||||
# Install Longhorn if not present
|
||||
if ! helm -n longhorn-system list 2>/dev/null | grep -q longhorn; then
|
||||
ensure_helm_repo longhorn https://charts.longhorn.io
|
||||
log "Installing Longhorn storage (this may take 5-10 minutes)..."
|
||||
if helm install longhorn longhorn/longhorn -n longhorn-system \
|
||||
--values "$BOOT/phase1-storage/longhorn-values.yaml" --wait --timeout 10m; then
|
||||
log "✅ Longhorn installed"
|
||||
else
|
||||
log "⚠️ Helm install failed, but continuing to ensure resources..."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Always apply StorageClasses (even if helm install partially failed)
|
||||
kubectl apply -f "$BOOT/phase1-storage/storageclasses.yaml"
|
||||
log "✅ longhorn installed"
|
||||
|
||||
# Verify critical components (resumable check)
|
||||
if kubectl -n longhorn-system wait --for=condition=available --timeout=300s deploy/longhorn-manager 2>/dev/null; then
|
||||
log "✅ longhorn installed"
|
||||
else
|
||||
log "⚠️ longhorn-manager not ready yet, but StorageClasses applied. Re-run to verify."
|
||||
fi
|
||||
}
|
||||
|
||||
p2_cnpg() {
|
||||
phase "PHASE 2: CNPG OPERATOR"
|
||||
if helm -n cnpg-system list 2>/dev/null | grep -q cnpg; then log "cnpg present, skip"; return; fi
|
||||
helm repo add cnpg https://cloudnative-pg.github.io/charts >/dev/null; helm repo update >/dev/null
|
||||
helm install cnpg cnpg/cloudnative-pg -n cnpg-system --create-namespace \
|
||||
--values "$BOOT/phase2-cnpg/cnpg-values.yaml" --wait --timeout 5m
|
||||
if kubectl get crd clusters.postgresql.cnpg.io >/dev/null 2>&1; then log "cnpg CRD present, skip install"; return; fi
|
||||
ensure_helm_repo cnpg https://cloudnative-pg.github.io/charts
|
||||
log "Installing CloudNativePG operator (this may take 2-3 minutes)..."
|
||||
if helm install cnpg cnpg/cloudnative-pg -n cnpg-system --create-namespace \
|
||||
--values "$BOOT/phase2-cnpg/cnpg-values.yaml" --wait --timeout 5m; then
|
||||
log "✅ CNPG operator installed"
|
||||
else
|
||||
log "❌ CNPG operator install failed"
|
||||
return 1
|
||||
fi
|
||||
kubectl get crd clusters.postgresql.cnpg.io >/dev/null || die "CNPG CRD not registered"
|
||||
log "✅ cnpg operator installed"
|
||||
}
|
||||
|
||||
p3_forgejo() {
|
||||
phase "PHASE 3: forgejo-db + Forgejo (ns cicd)"
|
||||
kubectl create ns cicd --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl apply -f "$BOOT/phase3-forgejo/forgejo-db.yaml"
|
||||
log "waiting for forgejo-db Ready (3-5 min)…"
|
||||
kubectl wait --for=condition=Ready --timeout=600s cluster/forgejo-db -n cicd
|
||||
|
||||
# Always ensure namespace + NetworkPolicy + Secrets (idempotent)
|
||||
kubectl apply -f "$BOOT/phase3-forgejo/namespace.yaml"
|
||||
|
||||
# Clean up old Valkey NetworkPolicy if it exists (from bundled chart)
|
||||
kubectl delete networkpolicy forgejo-valkey-cluster -n cicd 2>/dev/null || true
|
||||
|
||||
# Apply CNPG-specific NetworkPolicy
|
||||
kubectl apply -f "$BOOT/phase3-forgejo/cnpg-networkpolicy.yaml"
|
||||
|
||||
# Create Forgejo admin secret (bootstrap-time only, before ArgoCD exists)
|
||||
# In GitOps mode, ArgoCD will sync the SOPS-encrypted version from git
|
||||
if ! kubectl get secret forgejo-admin -n cicd >/dev/null 2>&1; then
|
||||
log "Creating forgejo-admin secret from .env (bootstrap mode)"
|
||||
[ -f "$HOME/workplace/homelab/.env" ] && source "$HOME/workplace/homelab/.env"
|
||||
kubectl -n cicd create secret generic forgejo-admin \
|
||||
--from-literal=username=rock \
|
||||
--from-literal=password="${FORGEJO_ADMIN_PASSWORD}" \
|
||||
--from-literal=email=[email protected]
|
||||
else
|
||||
log "forgejo-admin secret exists, skip (managed by ArgoCD in GitOps mode)"
|
||||
fi
|
||||
|
||||
# Check if forgejo-db cluster exists and is Ready
|
||||
if kubectl get cluster forgejo-db -n cicd >/dev/null 2>&1; then
|
||||
if kubectl get cluster forgejo-db -n cicd -o jsonpath='{.status.phase}' 2>/dev/null | grep -q "Cluster in healthy state"; then
|
||||
log "forgejo-db already Ready, skip wait"
|
||||
else
|
||||
log "forgejo-db exists but not Ready, waiting for all 3 instances (up to 30 min)…"
|
||||
if kubectl wait --for=condition=Ready --timeout=1800s cluster/forgejo-db -n cicd; then
|
||||
log "✅ forgejo-db cluster is Ready"
|
||||
else
|
||||
log "❌ forgejo-db cluster failed to become Ready"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log "creating forgejo-db cluster (3 instances)"
|
||||
kubectl apply -f "$BOOT/phase3-forgejo/forgejo-db.yaml"
|
||||
log "Waiting for all 3 CNPG instances to be Ready (up to 30 min)…"
|
||||
if kubectl wait --for=condition=Ready --timeout=1800s cluster/forgejo-db -n cicd; then
|
||||
log "✅ forgejo-db cluster is Ready"
|
||||
else
|
||||
log "❌ forgejo-db cluster failed to become Ready"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
kubectl -n cicd get secret forgejo-db-app >/dev/null || die "CNPG did not create forgejo-db-app secret"
|
||||
if helm -n cicd list 2>/dev/null | grep -q forgejo; then log "forgejo present, skip"; return; fi
|
||||
helm repo add forgejo https://code.forgejo.org/forgejo-helm >/dev/null 2>&1 || \
|
||||
helm repo add forgejo https://dl.gitea.io/charts/ >/dev/null
|
||||
helm repo update >/dev/null
|
||||
helm install forgejo forgejo/forgejo -n cicd \
|
||||
--values "$BOOT/phase3-forgejo/forgejo-values.yaml" --wait --timeout 10m
|
||||
log "✅ forgejo up — now push this repo to Forgejo and configure the GitHub pull-mirror"
|
||||
|
||||
# Install Forgejo if not present
|
||||
if helm -n cicd list 2>/dev/null | grep -q forgejo; then log "forgejo helm release present, skip"; return; fi
|
||||
ensure_helm_repo forgejo https://dl.gitea.io/charts/
|
||||
log "Installing Forgejo (this may take 10-15 minutes on slow nodes)..."
|
||||
if helm install forgejo forgejo/gitea -n cicd \
|
||||
--values "$BOOT/phase3-forgejo/forgejo-values.yaml" --wait --timeout 10m; then
|
||||
log "✅ Forgejo installed"
|
||||
else
|
||||
log "❌ Forgejo install failed"
|
||||
return 1
|
||||
fi
|
||||
log "Forgejo is up — now push this repo to Forgejo and configure the GitHub pull-mirror"
|
||||
}
|
||||
|
||||
p4_argocd() {
|
||||
phase "PHASE 4: ArgoCD (seeded from GitHub)"
|
||||
|
||||
# Always ensure namespace + repository secret (idempotent)
|
||||
kubectl create ns argocd --dry-run=client -o yaml | kubectl apply -f -
|
||||
# SOPS age key for the repo-server CMP plugin
|
||||
kubectl -n argocd create secret generic sops-age \
|
||||
--from-file=keys.txt="$SOPS_KEY" --dry-run=client -o yaml | kubectl apply -f -
|
||||
# GitHub deploy-key repo credential (read-only)
|
||||
kubectl -n argocd create secret generic seed-github-repo \
|
||||
--from-literal=type=git --from-literal=url="$GITHUB_SSH" \
|
||||
--from-file=sshPrivateKey="$DEPLOY_KEY" --dry-run=client -o yaml | kubectl apply -f -
|
||||
kubectl -n argocd label secret seed-github-repo argocd.argoproj.io/secret-type=repository --overwrite
|
||||
if ! helm -n argocd list 2>/dev/null | grep -q argocd; then
|
||||
helm repo add argo https://argoproj.github.io/argo-helm >/dev/null; helm repo update >/dev/null
|
||||
helm install argocd argo/argo-cd -n argocd \
|
||||
--values "$BOOT/phase4-argocd/argocd-values.yaml" --wait --timeout 10m
|
||||
kubectl -n argocd label secret seed-github-repo argocd.argoproj.io/secret-type=repository --overwrite 2>/dev/null || true
|
||||
|
||||
# Decrypt and apply any encrypted secrets from bootstrap dir (local SOPS)
|
||||
if command -v sops &> /dev/null; then
|
||||
export SOPS_AGE_KEY_FILE="$SOPS_KEY"
|
||||
log "Decrypting encrypted secrets with local SOPS..."
|
||||
local decrypted_count=0
|
||||
for enc_file in "$BOOT"/phase*/**.enc.yaml; do
|
||||
[ -f "$enc_file" ] || continue
|
||||
log " → Decrypting $(basename "$enc_file")..."
|
||||
if sops -d "$enc_file" | kubectl apply -f -; then
|
||||
decrypted_count=$((decrypted_count + 1))
|
||||
log " ✅ Applied"
|
||||
else
|
||||
log " ⚠️ Failed (may already exist)"
|
||||
fi
|
||||
done
|
||||
log "Decrypted and applied $decrypted_count secret(s)"
|
||||
else
|
||||
log "⚠️ SOPS not installed, skipping encrypted secret decryption"
|
||||
fi
|
||||
kubectl -n argocd wait --for=condition=available --timeout=300s deploy/argocd-server
|
||||
|
||||
# Install ArgoCD if not present
|
||||
if ! helm -n argocd list 2>/dev/null | grep -q argocd; then
|
||||
ensure_helm_repo argo https://argoproj.github.io/argo-helm
|
||||
log "Installing ArgoCD via Helm (installing chart, pods will start afterward)..."
|
||||
if helm install argocd argo/argo-cd -n argocd \
|
||||
--values "$BOOT/phase4-argocd/argocd-values.yaml" --timeout 10m; then
|
||||
log "✅ ArgoCD Helm release installed (pods starting...)"
|
||||
else
|
||||
log "❌ ArgoCD Helm install failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log "ArgoCD Helm release already exists, skipping install"
|
||||
fi
|
||||
|
||||
# Wait for server ready (resumable - slow on talos-cp-2)
|
||||
log "Waiting for argocd-server deployment to be available (max 10 minutes)..."
|
||||
if kubectl -n argocd wait --for=condition=available --timeout=600s deploy/argocd-server; then
|
||||
log "✅ argocd-server is available"
|
||||
else
|
||||
log "❌ argocd-server failed to become available within 10 minutes"
|
||||
log "Check pods: kubectl get pods -n argocd"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Always apply root app (idempotent)
|
||||
kubectl apply -f "$BOOT/phase4-argocd/root-app-github.yaml"
|
||||
log "✅ ArgoCD syncing from GitHub seed. Watch: kubectl get applications -n argocd"
|
||||
log "NOTE: SOPS CMP plugin not installed yet (bootstrap uses local SOPS decryption)."
|
||||
log " To add SOPS plugin for GitOps, see k8s/bootstrap/phase4-argocd/argocd-cmp-cm.yaml"
|
||||
}
|
||||
|
||||
p5_cutover() {
|
||||
|
||||
@@ -14,8 +14,8 @@ spec:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
source:
|
||||
repoURL: https://forgejo.riotpiao.com/riotpiao.com/homelab.git
|
||||
targetRevision: main
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Longhorn Helm Values — Single Source of Truth
|
||||
# Used by both bootstrap.sh (Helm install) and ArgoCD (adoption)
|
||||
# Chart: https://github.com/longhorn/charts
|
||||
#
|
||||
# NOTE: Namespace requires PodSecurity=privileged labels (Longhorn needs host access).
|
||||
# bootstrap.sh applies namespace.yaml automatically. For manual install:
|
||||
# kubectl apply -f k8s/bootstrap/phase1-storage/namespace.yaml
|
||||
|
||||
defaultSettings:
|
||||
# 3-node HA configuration
|
||||
@@ -52,6 +56,8 @@ longhornUI:
|
||||
replicas: 1
|
||||
|
||||
# Monitoring (Prometheus ServiceMonitor)
|
||||
# Disabled during bootstrap (Prometheus CRDs not installed yet)
|
||||
# Re-enable via ArgoCD after Prometheus stack is deployed
|
||||
metrics:
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
enabled: false
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: longhorn-system
|
||||
labels:
|
||||
pod-security.kubernetes.io/enforce: privileged
|
||||
pod-security.kubernetes.io/audit: privileged
|
||||
pod-security.kubernetes.io/warn: privileged
|
||||
@@ -2,8 +2,9 @@
|
||||
# The default 'longhorn' SC is created by Helm chart
|
||||
# These are additional specialized classes
|
||||
---
|
||||
# CNPG-specific StorageClass with postgres UID/GID mount options
|
||||
# Fixes "read-only filesystem" error when PostgreSQL (UID 26) tries to write
|
||||
# CNPG-specific StorageClass for PostgreSQL volumes
|
||||
# CNPG handles filesystem ownership via securityContext.fsGroup (UID/GID 26)
|
||||
# Separate from default 'longhorn' to allow CNPG-specific tuning
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
@@ -19,9 +20,7 @@ parameters:
|
||||
fromBackup: ""
|
||||
dataLocality: "best-effort"
|
||||
fsType: "ext4"
|
||||
mountOptions:
|
||||
- "noatime"
|
||||
- "uid=26" # postgres user
|
||||
- "gid=26" # postgres group
|
||||
# mountOptions removed - uid/gid are NOT valid for ext4 (only for FAT/VFAT)
|
||||
# CNPG handles ownership via securityContext.fsGroup automatically
|
||||
reclaimPolicy: Delete
|
||||
volumeBindingMode: Immediate
|
||||
|
||||
@@ -19,8 +19,10 @@ webhook:
|
||||
failurePolicy: Fail
|
||||
|
||||
# Monitoring
|
||||
# PodMonitor disabled during bootstrap (Prometheus CRDs not installed yet)
|
||||
# Re-enable via ArgoCD after Prometheus stack is deployed
|
||||
monitoring:
|
||||
podMonitorEnabled: true
|
||||
podMonitorEnabled: false
|
||||
grafanaDashboard:
|
||||
create: false # We'll manage dashboards via ArgoCD later
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# NetworkPolicy for CNPG pods - allow pod-to-pod replication traffic
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: forgejo-db-allow-replication
|
||||
namespace: cicd
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
cnpg.io/cluster: forgejo-db
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
# Allow CNPG operator to reach instance status endpoints (port 8000)
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: cnpg-system
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8000
|
||||
# Allow PostgreSQL connections from application pods (Forgejo)
|
||||
- from:
|
||||
- podSelector: {}
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: cicd
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
# Allow PostgreSQL replication from other CNPG pods
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
cnpg.io/cluster: forgejo-db
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
# Allow metrics scraping
|
||||
- from:
|
||||
- namespaceSelector: {}
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 9187
|
||||
egress:
|
||||
# Allow all egress (CNPG needs to reach services, API server, DNS, etc.)
|
||||
# Restricting egress breaks replication through services
|
||||
- {}
|
||||
@@ -64,8 +64,8 @@ spec:
|
||||
log_filename: "postgres"
|
||||
|
||||
storage:
|
||||
size: 50Gi
|
||||
storageClass: longhorn-cnpg # Uses postgres UID/GID mount options
|
||||
size: 25Gi
|
||||
storageClass: longhorn-cnpg
|
||||
|
||||
monitoring:
|
||||
enablePodMonitor: true
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#ENC[AES256_GCM,data:85lNBL02TeroUW8dissgxEwjnOZnb0fg7jndGim3/QSi3/hFEF8FQa9nrQ==,iv:lM1jBHIxFkQriZ6BjGRXAlymUs5nX4Kvt1WNxUeZlkU=,tag:wm/D5qVcrcu3VXcZkEj70A==,type:comment]
|
||||
apiVersion: ENC[AES256_GCM,data:Vrs=,iv:0TzPcIoozs2MXJNXkzgcVtjjBUgfOHaSXQZiD37fb+Q=,tag:CabTlLwtz6RcBF/gr4Ri4g==,type:str]
|
||||
kind: ENC[AES256_GCM,data:22Y5w+Df,iv:Mf2s3h8++Vxqb4JoymHXY4/WAknDZ2GGrVVtKK51JxI=,tag:k5DHUqWGMBPpLkQXADaMlw==,type:str]
|
||||
metadata:
|
||||
name: ENC[AES256_GCM,data:ItXKquY5a7gu8CJZog==,iv:jxbj7Qtv+DRbhzTdvtv+eJuTQPNIf497NZPYA6ld4s0=,tag:XdaaSavFBxOjvxna2kr/Tg==,type:str]
|
||||
namespace: ENC[AES256_GCM,data:VH6NMg==,iv:4PfWZu5qVGXP3ZzRHMrh5N9dzJ3SoUPPo58ppcDTnpk=,tag:Vr8E+WF0Z9ym3GyTGbdi2g==,type:str]
|
||||
type: ENC[AES256_GCM,data:DmbHZRIk,iv:EZHnf1h1L29G1HOBYBSBeydNe4nC8XiBOw8YEL3kxrY=,tag:pFhUSMAIEqJET3NmaajO1g==,type:str]
|
||||
stringData:
|
||||
username: ENC[AES256_GCM,data:ZTrmFA==,iv:1+tLTAxrDitXJwCAEccaVQzc9I9lNRgT3FsxO2NPDDc=,tag:69Wn4a8pjhdUycc/MXsn3Q==,type:str]
|
||||
password: ENC[AES256_GCM,data:w7Vn8XaC1ykNrwPpJjVYg8J5KXkUbaPspu0CoceqHVdai6BFNW5rtA==,iv:jAwDFvJfQ1GkeU/qpEVUAQ6cWqxYE8nrgs+/RouyUxg=,tag:foGmYika/9uNBdsKVrspEA==,type:str]
|
||||
email: ENC[AES256_GCM,data:pIOq80OjVERFCXJmx8+qTJEw,iv:mfkMj3u8W2ZX4N4IH39mZXfEp+xphS4shaJcFOD/LEE=,tag:FOSq0E7VzzOM69Bn77v2Dw==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwSi84U3FQZXJrYzA0WCtS
|
||||
c1g1Sm04eFhha2huSEVCd2x3OGVUT08yejFjCmpPV0NsTVRyWTRVUkx6WktKaU1k
|
||||
Vk12VGRIejRWbjQxeDZxdFUxWkgzaVUKLS0tIDRZdFNxY1AvcFhOREMxU01zTzhX
|
||||
RzFmMjdjOFd4ZkxscnBIa1E2NWtRaVEK7qZXtq2VwZBwsLAulRh93TpCXCos0Vu7
|
||||
fX+/oMEN6gF5VxDJ/e5C644EKUY+tSLAoh75xA5DAytOxEVdhe2ozA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1smu533f803gmd0jq60s2zaj9zlznajy0ca6rtewd4r37mr2hs3uqsrldfh
|
||||
lastmodified: "2026-07-25T17:45:22Z"
|
||||
mac: ENC[AES256_GCM,data:gi8smimG00EQbF492BVzsWOEubGdT0zSG39D+r3fgLL/wgtDqF2mgtFA0iDiy1pInt1Viu8yYpu1l/eBSD8BNW4bfrglIHOJdb1I5+19u8SMebhWCwbEcbiHaXNmVUKKapGsNjbApCApq00NXoOZuOUSUzZlh19JPvg/vd7nl2M=,iv:n8Kni4RQhj/VgIruqptVa6m0YwFJF6v2+p2boJsLT5c=,tag:Tc4gkhH7hSrJKEOJ3X3SjQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.2
|
||||
@@ -1,11 +1,22 @@
|
||||
# Forgejo Helm Values — Single Source of Truth
|
||||
# Chart: https://codeberg.org/forgejo-contrib/forgejo-helm
|
||||
|
||||
# Disable bundled dependencies (use external CNPG + Redis instead)
|
||||
postgresql-ha:
|
||||
enabled: false
|
||||
|
||||
valkey:
|
||||
enabled: false
|
||||
|
||||
valkey-cluster:
|
||||
enabled: false
|
||||
|
||||
redis:
|
||||
enabled: false
|
||||
|
||||
gitea:
|
||||
admin:
|
||||
username: "admin"
|
||||
email: "[email protected]"
|
||||
# Password set via secret (not in values)
|
||||
existingSecret: forgejo-admin
|
||||
|
||||
config:
|
||||
server:
|
||||
@@ -18,17 +29,7 @@ gitea:
|
||||
DB_TYPE: postgres
|
||||
HOST: forgejo-db-rw.cicd.svc.cluster.local:5432
|
||||
NAME: forgejo
|
||||
# User/password from CNPG-generated secret
|
||||
USER:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: forgejo-db-app
|
||||
key: username
|
||||
PASSWD:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: forgejo-db-app
|
||||
key: password
|
||||
# User/password injected via extraEnv (secretKeyRef doesn't work in config)
|
||||
|
||||
cache:
|
||||
ADAPTER: redis
|
||||
@@ -84,3 +85,17 @@ tolerations:
|
||||
# ArgoCD adoption labels
|
||||
labels:
|
||||
argocd.argoproj.io/instance: forgejo
|
||||
|
||||
# Inject database credentials via environment variables (overrides app.ini)
|
||||
deployment:
|
||||
env:
|
||||
- name: GITEA__DATABASE__USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: forgejo-db-app
|
||||
key: username
|
||||
- name: GITEA__DATABASE__PASSWD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: forgejo-db-app
|
||||
key: password
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: cicd
|
||||
labels:
|
||||
# Baseline allows most workloads while blocking clearly dangerous configurations
|
||||
# Redis needs some relaxed settings but doesn't need full privileged access
|
||||
pod-security.kubernetes.io/enforce: baseline
|
||||
pod-security.kubernetes.io/audit: baseline
|
||||
pod-security.kubernetes.io/warn: baseline
|
||||
@@ -0,0 +1,34 @@
|
||||
# ArgoCD CMP plugin for SOPS secret decryption
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cmp-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
sops-secrets-v1.0.yaml: |
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ConfigManagementPlugin
|
||||
metadata:
|
||||
name: sops-secrets-v1.0
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [sh, -c]
|
||||
args:
|
||||
- |
|
||||
# Install sops if not present
|
||||
if ! command -v sops &> /dev/null; then
|
||||
wget -qO- https://github.com/getsops/sops/releases/download/v3.9.3/sops-v3.9.3.linux.amd64 > /usr/local/bin/sops
|
||||
chmod +x /usr/local/bin/sops
|
||||
fi
|
||||
generate:
|
||||
command: [sh, -c]
|
||||
args:
|
||||
- |
|
||||
# Find all .enc.yaml files and decrypt them
|
||||
find . -name '*.enc.yaml' -type f | while read -r file; do
|
||||
sops -d "$file"
|
||||
done
|
||||
discover:
|
||||
find:
|
||||
glob: "**/*.enc.yaml"
|
||||
@@ -1,4 +1,4 @@
|
||||
# ArgoCD Helm Values — Single Source of Truth
|
||||
# ArgoCD Helm Values — Bootstrap Mode (SOPS plugin added post-bootstrap)
|
||||
# Chart: https://github.com/argoproj/argo-helm
|
||||
|
||||
global:
|
||||
@@ -32,6 +32,12 @@ server:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
|
||||
# Tolerations for control-plane
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# Repo server configuration
|
||||
repoServer:
|
||||
resources:
|
||||
@@ -42,21 +48,11 @@ repoServer:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
|
||||
# SOPS plugin for encrypted secrets
|
||||
volumes:
|
||||
- name: sops-age
|
||||
secret:
|
||||
secretName: sops-age
|
||||
optional: true
|
||||
volumeMounts:
|
||||
- name: sops-age
|
||||
mountPath: /home/argocd/.config/sops/age
|
||||
readOnly: true
|
||||
|
||||
# Environment for SOPS
|
||||
env:
|
||||
- name: SOPS_AGE_KEY_FILE
|
||||
value: /home/argocd/.config/sops/age/keys.txt
|
||||
# Tolerations for control-plane
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# Controller configuration
|
||||
controller:
|
||||
@@ -68,6 +64,12 @@ controller:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
|
||||
# Tolerations for control-plane
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# Application controller configuration
|
||||
applicationSet:
|
||||
enabled: true
|
||||
@@ -87,26 +89,7 @@ redis:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
|
||||
# Tolerations for control-plane
|
||||
server:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
repoServer:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
controller:
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# ArgoCD projects
|
||||
# ArgoCD configuration
|
||||
configs:
|
||||
# Default project allows all repos
|
||||
cm:
|
||||
@@ -116,8 +99,7 @@ configs:
|
||||
params:
|
||||
server.insecure: true
|
||||
|
||||
# RBAC (allow admin full access)
|
||||
configs:
|
||||
# RBAC (allow admin full access)
|
||||
rbac:
|
||||
policy.default: role:readonly
|
||||
policy.csv: |
|
||||
|
||||
Reference in New Issue
Block a user