From 7ab1222310c23c5f1305c48c199ce432c2fd0848 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 19:40:26 +0200 Subject: Test: verify SSH push works --- TEST_PUSH.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 TEST_PUSH.md diff --git a/TEST_PUSH.md b/TEST_PUSH.md new file mode 100644 index 0000000..d7c9377 --- /dev/null +++ b/TEST_PUSH.md @@ -0,0 +1 @@ +# Test push from paul -- cgit v1.2.3 From ec8bd651d57deab371021c27b88f6698376f8e78 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:06:02 +0200 Subject: Add self-hosted git server with SSH and cgit web UI Deploy a self-hosted git repository solution to replace external Codeberg dependency. Components: - SSH git server: Alpine-based container with OpenSSH and git - cgit web UI: Browse repositories at cgit.f3s.buetow.org - Single pod design: git-server + cgit containers sharing storage Infrastructure: - Docker image in git-server/docker-image/ with Justfile build automation - Helm chart in git-server/helm-chart/ for Kubernetes deployment - 5Gi ReadWriteMany PVC for NFS-backed repository storage - ClusterIP service for ArgoCD internal access - NodePort 30022 for external SSH push access - Traefik ingress for cgit web UI ArgoCD Application manifest deployed to cicd namespace. Note: SSH keys must be created as Kubernetes secrets manually, not in git. Co-Authored-By: Claude Sonnet 4.5 --- f3s/argocd-apps/cicd/git-server.yaml | 28 +++++++ f3s/git-server/docker-image/Dockerfile | 23 +++++ f3s/git-server/docker-image/Justfile | 7 ++ f3s/git-server/docker-image/sshd_config | 35 ++++++++ f3s/git-server/helm-chart/Chart.yaml | 5 ++ .../helm-chart/templates/configmap-cgit.yaml | 23 +++++ .../helm-chart/templates/deployment.yaml | 97 ++++++++++++++++++++++ f3s/git-server/helm-chart/templates/ingress.yaml | 24 ++++++ .../helm-chart/templates/persistent-volume.yaml | 27 ++++++ f3s/git-server/helm-chart/templates/service.yaml | 38 +++++++++ 10 files changed, 307 insertions(+) create mode 100644 f3s/argocd-apps/cicd/git-server.yaml create mode 100644 f3s/git-server/docker-image/Dockerfile create mode 100644 f3s/git-server/docker-image/Justfile create mode 100644 f3s/git-server/docker-image/sshd_config create mode 100644 f3s/git-server/helm-chart/Chart.yaml create mode 100644 f3s/git-server/helm-chart/templates/configmap-cgit.yaml create mode 100644 f3s/git-server/helm-chart/templates/deployment.yaml create mode 100644 f3s/git-server/helm-chart/templates/ingress.yaml create mode 100644 f3s/git-server/helm-chart/templates/persistent-volume.yaml create mode 100644 f3s/git-server/helm-chart/templates/service.yaml diff --git a/f3s/argocd-apps/cicd/git-server.yaml b/f3s/argocd-apps/cicd/git-server.yaml new file mode 100644 index 0000000..be96b7f --- /dev/null +++ b/f3s/argocd-apps/cicd/git-server.yaml @@ -0,0 +1,28 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: git-server + namespace: cicd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: https://codeberg.org/snonux/conf.git + targetRevision: master + path: f3s/git-server/helm-chart + destination: + server: https://kubernetes.default.svc + namespace: cicd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=false + retry: + limit: 3 + backoff: + duration: 5s + factor: 2 + maxDuration: 1m diff --git a/f3s/git-server/docker-image/Dockerfile b/f3s/git-server/docker-image/Dockerfile new file mode 100644 index 0000000..382ad0d --- /dev/null +++ b/f3s/git-server/docker-image/Dockerfile @@ -0,0 +1,23 @@ +FROM alpine:3.19 + +# Install OpenSSH server and git +RUN apk add --no-cache openssh git + +# Create git user with UID 1000 and set git-shell as login shell +# This restricts the user to git operations only +RUN adduser -D -u 1000 -s /usr/bin/git-shell git && \ + mkdir -p /home/git/.ssh /repos && \ + chown -R git:git /home/git /repos + +# Generate SSH host keys +# These will be regenerated if not persisted via volume mount +RUN ssh-keygen -A + +# Copy sshd configuration +COPY sshd_config /etc/ssh/sshd_config + +# Expose SSH port +EXPOSE 22 + +# Run SSH daemon in foreground with error logging to stderr +CMD ["/usr/sbin/sshd", "-D", "-e"] diff --git a/f3s/git-server/docker-image/Justfile b/f3s/git-server/docker-image/Justfile new file mode 100644 index 0000000..1b54e4a --- /dev/null +++ b/f3s/git-server/docker-image/Justfile @@ -0,0 +1,7 @@ +all: + docker build -t git-server:1.0 . + +f3s: + docker build -t git-server:1.0 . + docker tag git-server:1.0 r0.lan.buetow.org:30001/git-server:1.0 + docker push r0.lan.buetow.org:30001/git-server:1.0 diff --git a/f3s/git-server/docker-image/sshd_config b/f3s/git-server/docker-image/sshd_config new file mode 100644 index 0000000..e49c5bb --- /dev/null +++ b/f3s/git-server/docker-image/sshd_config @@ -0,0 +1,35 @@ +# SSH Server Configuration for Git Server +# Security-hardened configuration for git-only access + +# Network +Port 22 +AddressFamily any +ListenAddress 0.0.0.0 + +# Host Keys +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key + +# Security +PermitRootLogin no +PubkeyAuthentication yes +PasswordAuthentication no +PermitEmptyPasswords no +ChallengeResponseAuthentication no +UsePAM no + +# Restrict to git user only +AllowUsers git + +# Disable tunneling and forwarding +X11Forwarding no +AllowTcpForwarding no +AllowAgentForwarding no +PermitTunnel no + +# Logging +SyslogFacility AUTH +LogLevel INFO + +# Performance +UseDNS no diff --git a/f3s/git-server/helm-chart/Chart.yaml b/f3s/git-server/helm-chart/Chart.yaml new file mode 100644 index 0000000..eeceffb --- /dev/null +++ b/f3s/git-server/helm-chart/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: git-server +description: A Helm chart for deploying a self-hosted SSH git server with cgit web UI. +version: 0.1.0 +appVersion: "1.0" diff --git a/f3s/git-server/helm-chart/templates/configmap-cgit.yaml b/f3s/git-server/helm-chart/templates/configmap-cgit.yaml new file mode 100644 index 0000000..840fbd4 --- /dev/null +++ b/f3s/git-server/helm-chart/templates/configmap-cgit.yaml @@ -0,0 +1,23 @@ +# CGit Configuration +# Configures cgit to scan /repos for git repositories + +apiVersion: v1 +kind: ConfigMap +metadata: + name: cgit-config + namespace: cicd +data: + cgitrc: | + # Global settings + root-title=f3s Git Repository Browser + root-desc=Browse git repositories in f3s cluster + + # Enable git-config for per-repo settings + enable-git-config=1 + + # Remove .git suffix from repository URLs + remove-suffix=1 + + # Scan for repositories in /repos + # This must be the last setting in the file + scan-path=/repos diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml new file mode 100644 index 0000000..7e262f8 --- /dev/null +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -0,0 +1,97 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: git-server + namespace: cicd + labels: + app: git-server +spec: + replicas: 1 + selector: + matchLabels: + app: git-server + template: + metadata: + labels: + app: git-server + spec: + # Allow both git (1000) and www-data (33) to access shared files + securityContext: + fsGroup: 1000 + + containers: + # Container 1: SSH Git Server + - name: git-server + image: r0.lan.buetow.org:30001/git-server:1.0 + ports: + - containerPort: 22 + name: ssh + protocol: TCP + volumeMounts: + - name: repos + mountPath: /repos + - name: git-ssh-keys + mountPath: /home/git/.ssh/authorized_keys + subPath: authorized_keys + readOnly: true + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + # Container 2: CGit Web UI + - name: cgit + image: joseluisq/alpine-cgit:latest + ports: + - containerPort: 8080 + name: http + protocol: TCP + env: + - name: CGIT_TITLE + value: "f3s Git Repository Browser" + - name: CGIT_DESC + value: "Browse git repositories" + volumeMounts: + - name: repos + mountPath: /repos + readOnly: true + - name: cgit-config + mountPath: /etc/cgitrc + subPath: cgitrc + readOnly: true + securityContext: + runAsUser: 33 + runAsGroup: 33 + runAsNonRoot: true + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + add: ["NET_BIND_SERVICE"] + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi + + volumes: + - name: repos + persistentVolumeClaim: + claimName: git-server-pvc + - name: git-ssh-keys + secret: + secretName: git-server-authorized-keys + defaultMode: 0400 + - name: cgit-config + configMap: + name: cgit-config diff --git a/f3s/git-server/helm-chart/templates/ingress.yaml b/f3s/git-server/helm-chart/templates/ingress.yaml new file mode 100644 index 0000000..e47ff7f --- /dev/null +++ b/f3s/git-server/helm-chart/templates/ingress.yaml @@ -0,0 +1,24 @@ +# CGit Web UI Ingress +# Exposes cgit web interface at cgit.f3s.buetow.org +# Following f3s cluster ingress pattern (Traefik) + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cgit-ingress + namespace: cicd + annotations: + spec.ingressClassName: traefik + traefik.ingress.kubernetes.io/router.entrypoints: web +spec: + rules: + - host: cgit.f3s.buetow.org + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: git-server + port: + number: 80 diff --git a/f3s/git-server/helm-chart/templates/persistent-volume.yaml b/f3s/git-server/helm-chart/templates/persistent-volume.yaml new file mode 100644 index 0000000..174e66e --- /dev/null +++ b/f3s/git-server/helm-chart/templates/persistent-volume.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: git-server-pv +spec: + capacity: + storage: 5Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + hostPath: + path: /data/nfs/k3svolumes/git-server + type: Directory +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: git-server-pvc + namespace: cicd +spec: + storageClassName: "" + accessModes: + - ReadWriteMany + resources: + requests: + storage: 5Gi diff --git a/f3s/git-server/helm-chart/templates/service.yaml b/f3s/git-server/helm-chart/templates/service.yaml new file mode 100644 index 0000000..9675e86 --- /dev/null +++ b/f3s/git-server/helm-chart/templates/service.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Service +metadata: + name: git-server + namespace: cicd + labels: + app: git-server +spec: + selector: + app: git-server + ports: + - name: ssh + protocol: TCP + port: 22 + targetPort: 22 + - name: http + protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + name: git-server-ssh + namespace: cicd + labels: + app: git-server +spec: + selector: + app: git-server + ports: + - name: ssh + protocol: TCP + port: 22 + targetPort: 22 + nodePort: 30022 + type: NodePort -- cgit v1.2.3 From c6ec63635fd9c75c452dbb1a040912e5301f731e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:09:43 +0200 Subject: Fix git-server deployment - Use registry.lan.buetow.org for deployment (internal DNS) - Add emptyDir volume for cgit cache directory - Add README.md with deployment and secret management instructions This fixes image pull issues and cgit permission errors. --- f3s/git-server/README.md | 273 +++++++++++++++++++++ .../helm-chart/templates/deployment.yaml | 6 +- 2 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 f3s/git-server/README.md diff --git a/f3s/git-server/README.md b/f3s/git-server/README.md new file mode 100644 index 0000000..fe23bee --- /dev/null +++ b/f3s/git-server/README.md @@ -0,0 +1,273 @@ +# Self-Hosted Git Server with SSH and CGit Web UI + +A self-hosted git repository solution for the f3s k3s cluster, replacing external Codeberg dependency. + +## Components + +- **SSH Git Server**: Alpine-based container with OpenSSH and git for repository access +- **CGit Web UI**: Browse repositories at `http://cgit.f3s.buetow.org` +- **Single Pod Design**: Both containers share storage via ReadWriteMany PVC + +## Architecture + +``` +┌────────────────────────────────────────┐ +│ Pod: git-server (cicd namespace) │ +├────────────────────────────────────────┤ +│ Container 1: SSH Git Server │ +│ - Port 22 (SSH) │ +│ - User: git (UID 1000) │ +│ │ +│ Container 2: cgit + nginx │ +│ - Port 8080 (HTTP) │ +│ - User: www-data (UID 33) │ +│ │ +│ Shared Volume: /repos (5Gi, NFS) │ +└────────────────────────────────────────┘ +``` + +## Network Access + +- **Internal (ArgoCD)**: `git-server.cicd.svc.cluster.local:22` +- **External SSH**: NodePort 30022 on any cluster node +- **Web UI**: `http://cgit.f3s.buetow.org` + +## Initial Setup + +### 1. Build and Push Docker Image + +```bash +cd docker-image +just f3s +``` + +### 2. Setup Storage on Cluster Nodes + +```bash +ssh root@r0 +mkdir -p /data/nfs/k3svolumes/git-server/repos +chown -R 1000:33 /data/nfs/k3svolumes/git-server +chmod -R 0755 /data/nfs/k3svolumes/git-server +``` + +### 3. Initialize Repository + +Clone the existing Codeberg repository as a bare repo: + +```bash +ssh root@r0 +cd /data/nfs/k3svolumes/git-server/repos +git clone --bare https://codeberg.org/snonux/conf.git conf.git +chown -R 1000:33 conf.git +chmod -R 0755 conf.git +``` + +### 4. Create SSH Key Secrets + +**IMPORTANT**: Secrets must be created manually in Kubernetes, NOT stored in git. + +#### For ArgoCD Access + +Generate SSH key pair: + +```bash +ssh-keygen -t ed25519 -C "argocd@f3s.cluster" -f /tmp/argocd-git-key -N "" +``` + +Create authorized_keys secret for git-server: + +```bash +# Save public key to file +cat /tmp/argocd-git-key.pub > /tmp/authorized_keys + +# Create secret in Kubernetes +kubectl create secret generic git-server-authorized-keys \ + --from-file=authorized_keys=/tmp/authorized_keys \ + -n cicd +``` + +Create private key secret for ArgoCD (needed later): + +```bash +kubectl create secret generic argocd-git-ssh-key \ + --from-file=sshPrivateKey=/tmp/argocd-git-key \ + -n cicd +``` + +#### For User Push Access + +To add additional SSH keys for users to push: + +```bash +# Get current authorized_keys +kubectl get secret git-server-authorized-keys -n cicd -o jsonpath='{.data.authorized_keys}' | base64 -d > /tmp/authorized_keys + +# Add your SSH public key +echo "ssh-ed25519 AAAAC3Nza... user@host" >> /tmp/authorized_keys + +# Update secret +kubectl create secret generic git-server-authorized-keys \ + --from-file=authorized_keys=/tmp/authorized_keys \ + -n cicd \ + --dry-run=client -o yaml | kubectl apply -f - + +# Restart git-server to pick up new keys +kubectl rollout restart deployment/git-server -n cicd +``` + +### 5. Deploy via ArgoCD + +```bash +kubectl apply -f /home/paul/git/conf/f3s/argocd-apps/cicd/git-server.yaml +``` + +Or commit and push the ArgoCD Application manifest to let ArgoCD sync automatically. + +### 6. Verify Deployment + +```bash +# Check pod status +kubectl get pods -n cicd -l app=git-server + +# Check logs +kubectl logs -n cicd -l app=git-server -c git-server --tail=50 +kubectl logs -n cicd -l app=git-server -c cgit --tail=50 + +# Test cgit web UI +curl -I http://cgit.f3s.buetow.org +``` + +## Repository URLs + +### For ArgoCD (Internal) + +``` +ssh://git@git-server.cicd.svc.cluster.local/repos/conf.git +``` + +### For Users (External) + +```bash +# Via NodePort (direct) +git clone ssh://git@r0:30022/repos/conf.git + +# Via SSH config alias +# Add to ~/.ssh/config: +Host f3s-git + HostName r0.f3s.buetow.org + Port 30022 + User git + IdentityFile ~/.ssh/id_f3s_git + +# Then clone with: +git clone f3s-git:/repos/conf.git +``` + +## Managing Repositories + +### Add New Repository + +```bash +ssh root@r0 +cd /data/nfs/k3svolumes/git-server/repos +git init --bare newrepo.git +chown -R 1000:33 newrepo.git +chmod -R 0755 newrepo.git +``` + +The new repository will automatically appear in cgit (scan-path feature). + +### Remove Repository + +```bash +ssh root@r0 +rm -rf /data/nfs/k3svolumes/git-server/repos/oldrepo.git +``` + +## Troubleshooting + +### Git Push Fails with Permission Denied + +1. Check if your SSH key is in authorized_keys: + ```bash + kubectl get secret git-server-authorized-keys -n cicd -o jsonpath='{.data.authorized_keys}' | base64 -d + ``` + +2. Verify git-server pod is running: + ```bash + kubectl get pods -n cicd -l app=git-server + ``` + +3. Check SSH logs: + ```bash + kubectl logs -n cicd -l app=git-server -c git-server -f + ``` + +### CGit Shows No Repositories + +1. Check if repos exist in storage: + ```bash + ssh root@r0 ls -la /data/nfs/k3svolumes/git-server/repos/ + ``` + +2. Check cgit container logs: + ```bash + kubectl logs -n cicd -l app=git-server -c cgit + ``` + +3. Verify cgit configuration: + ```bash + kubectl get configmap cgit-config -n cicd -o yaml + ``` + +### ArgoCD Can't Clone Repository + +1. Verify ArgoCD SSH key secret exists: + ```bash + kubectl get secret argocd-git-ssh-key -n cicd + ``` + +2. Check if ArgoCD public key is in authorized_keys: + ```bash + kubectl get secret git-server-authorized-keys -n cicd -o jsonpath='{.data.authorized_keys}' | base64 -d + ``` + +3. Test SSH connection from ArgoCD repo-server: + ```bash + kubectl exec -n cicd deploy/argocd-repo-server -- \ + ssh -T git@git-server.cicd.svc.cluster.local + ``` + +## Backup and Recovery + +Backups are handled by ZFS snapshots at the storage layer (`/data/nfs/k3svolumes/git-server`). + +To recover: +1. Restore ZFS snapshot +2. Redeploy git-server application via ArgoCD + +## Security Notes + +- SSH keys are restricted to git-shell only (no shell access) +- git-server container runs as non-root user (UID 1000) +- cgit container has read-only access to repositories +- All container capabilities dropped except NET_BIND_SERVICE for cgit +- Secrets managed via Kubernetes Secrets, never committed to git + +## Monitoring + +View logs: + +```bash +# SSH server logs +kubectl logs -n cicd -l app=git-server -c git-server -f + +# CGit web server logs +kubectl logs -n cicd -l app=git-server -c cgit -f +``` + +Check resource usage: + +```bash +kubectl top pod -n cicd -l app=git-server +``` diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 7e262f8..a3f4568 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -22,7 +22,7 @@ spec: containers: # Container 1: SSH Git Server - name: git-server - image: r0.lan.buetow.org:30001/git-server:1.0 + image: registry.lan.buetow.org:30001/git-server:1.0 ports: - containerPort: 22 name: ssh @@ -68,6 +68,8 @@ spec: mountPath: /etc/cgitrc subPath: cgitrc readOnly: true + - name: cgit-cache + mountPath: /var/cache/cgit securityContext: runAsUser: 33 runAsGroup: 33 @@ -95,3 +97,5 @@ spec: - name: cgit-config configMap: name: cgit-config + - name: cgit-cache + emptyDir: {} -- cgit v1.2.3 From 676786639604fe244ba5372471afba4c7824e365 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:11:58 +0200 Subject: Fix sshd_config and cgit permissions - Remove unsupported UsePAM option from sshd_config - Run cgit as root to allow cache directory initialization - Add CHOWN and DAC_OVERRIDE capabilities for cgit --- f3s/git-server/docker-image/sshd_config | 1 - f3s/git-server/helm-chart/templates/deployment.yaml | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/f3s/git-server/docker-image/sshd_config b/f3s/git-server/docker-image/sshd_config index e49c5bb..7e899e0 100644 --- a/f3s/git-server/docker-image/sshd_config +++ b/f3s/git-server/docker-image/sshd_config @@ -16,7 +16,6 @@ PubkeyAuthentication yes PasswordAuthentication no PermitEmptyPasswords no ChallengeResponseAuthentication no -UsePAM no # Restrict to git user only AllowUsers git diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index a3f4568..0446725 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -71,13 +71,12 @@ spec: - name: cgit-cache mountPath: /var/cache/cgit securityContext: - runAsUser: 33 - runAsGroup: 33 - runAsNonRoot: true + runAsUser: 0 + runAsGroup: 0 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] - add: ["NET_BIND_SERVICE"] + add: ["NET_BIND_SERVICE", "CHOWN", "DAC_OVERRIDE"] resources: requests: cpu: 50m -- cgit v1.2.3 From 774c4d4c32e3e32e51ef545c1af5cbc0c1bd6191 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:13:28 +0200 Subject: Fix SSH host keys and container security - Generate SSH host keys at runtime via entrypoint script - Remove fsGroup security context to fix emptyDir permissions - Allow cgit to initialize cache directory as root --- f3s/git-server/docker-image/Dockerfile | 16 ++++++++++------ f3s/git-server/helm-chart/templates/deployment.yaml | 4 ---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/f3s/git-server/docker-image/Dockerfile b/f3s/git-server/docker-image/Dockerfile index 382ad0d..574a94b 100644 --- a/f3s/git-server/docker-image/Dockerfile +++ b/f3s/git-server/docker-image/Dockerfile @@ -9,15 +9,19 @@ RUN adduser -D -u 1000 -s /usr/bin/git-shell git && \ mkdir -p /home/git/.ssh /repos && \ chown -R git:git /home/git /repos -# Generate SSH host keys -# These will be regenerated if not persisted via volume mount -RUN ssh-keygen -A - # Copy sshd configuration COPY sshd_config /etc/ssh/sshd_config +# Create entrypoint script to generate host keys at runtime +RUN echo '#!/bin/sh' > /entrypoint.sh && \ + echo 'if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then' >> /entrypoint.sh && \ + echo ' ssh-keygen -A' >> /entrypoint.sh && \ + echo 'fi' >> /entrypoint.sh && \ + echo 'exec /usr/sbin/sshd -D -e' >> /entrypoint.sh && \ + chmod +x /entrypoint.sh + # Expose SSH port EXPOSE 22 -# Run SSH daemon in foreground with error logging to stderr -CMD ["/usr/sbin/sshd", "-D", "-e"] +# Run entrypoint script +CMD ["/entrypoint.sh"] diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 0446725..b74a5fc 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -15,10 +15,6 @@ spec: labels: app: git-server spec: - # Allow both git (1000) and www-data (33) to access shared files - securityContext: - fsGroup: 1000 - containers: # Container 1: SSH Git Server - name: git-server -- cgit v1.2.3 From fb7d61bd2b49ecf9eb6aa5194a2b108e8933c88d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:16:18 +0200 Subject: Run containers as root and use emptyDir for writeable dirs - Mount emptyDir for /etc/ssh to allow SSH host key generation - Mount emptyDir for /var/cache/cgit to allow cache initialization - Run both containers as root with proper capabilities - Copy sshd_config at runtime from /tmp to /etc/ssh - Add imagePullPolicy: Always to force image refresh --- f3s/git-server/docker-image/Dockerfile | 11 ++++++++--- f3s/git-server/helm-chart/templates/deployment.yaml | 10 ++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/f3s/git-server/docker-image/Dockerfile b/f3s/git-server/docker-image/Dockerfile index 574a94b..f721fab 100644 --- a/f3s/git-server/docker-image/Dockerfile +++ b/f3s/git-server/docker-image/Dockerfile @@ -9,11 +9,16 @@ RUN adduser -D -u 1000 -s /usr/bin/git-shell git && \ mkdir -p /home/git/.ssh /repos && \ chown -R git:git /home/git /repos -# Copy sshd configuration -COPY sshd_config /etc/ssh/sshd_config +# Copy sshd configuration to /tmp (will be copied to /etc/ssh by entrypoint) +COPY sshd_config /tmp/sshd_config -# Create entrypoint script to generate host keys at runtime +# Create entrypoint script to setup SSH and run sshd RUN echo '#!/bin/sh' > /entrypoint.sh && \ + echo '# Copy sshd_config if not exists' >> /entrypoint.sh && \ + echo 'if [ ! -f /etc/ssh/sshd_config ]; then' >> /entrypoint.sh && \ + echo ' cp /tmp/sshd_config /etc/ssh/sshd_config' >> /entrypoint.sh && \ + echo 'fi' >> /entrypoint.sh && \ + echo '# Generate host keys if not exists' >> /entrypoint.sh && \ echo 'if [ ! -f /etc/ssh/ssh_host_ed25519_key ]; then' >> /entrypoint.sh && \ echo ' ssh-keygen -A' >> /entrypoint.sh && \ echo 'fi' >> /entrypoint.sh && \ diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index b74a5fc..6f47c36 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -19,6 +19,7 @@ spec: # Container 1: SSH Git Server - name: git-server image: registry.lan.buetow.org:30001/git-server:1.0 + imagePullPolicy: Always ports: - containerPort: 22 name: ssh @@ -30,12 +31,15 @@ spec: mountPath: /home/git/.ssh/authorized_keys subPath: authorized_keys readOnly: true + - name: ssh-host-keys + mountPath: /etc/ssh securityContext: - runAsUser: 1000 - runAsGroup: 1000 + runAsUser: 0 + runAsGroup: 0 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] + add: ["CHOWN", "DAC_OVERRIDE"] resources: requests: cpu: 50m @@ -94,3 +98,5 @@ spec: name: cgit-config - name: cgit-cache emptyDir: {} + - name: ssh-host-keys + emptyDir: {} -- cgit v1.2.3 From fdfefbb9e888dac2e43ad6d1a779db5faa41b7bd Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:22:20 +0200 Subject: Fix permissions using fsGroup and initContainer pattern Follow webdav/filebrowser pattern for proper permission handling: - Add fsGroup: 1000 at pod level for git repo access - Add initContainer to chown emptyDir volumes - Run git-server as root (required for sshd) - Run cgit as user 33 (www-data) - Restore cgit-cache emptyDir volume with proper ownership --- .../helm-chart/templates/deployment.yaml | 26 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 6f47c36..6c424a0 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -15,6 +15,26 @@ spec: labels: app: git-server spec: + securityContext: + fsGroup: 1000 + + initContainers: + - name: setup + image: alpine:3.19 + command: + - /bin/sh + - -c + - | + mkdir -p /cache-init + chown -R 33:1000 /cache-init + mkdir -p /ssh-init + chown -R 0:0 /ssh-init + volumeMounts: + - name: cgit-cache + mountPath: /cache-init + - name: ssh-host-keys + mountPath: /ssh-init + containers: # Container 1: SSH Git Server - name: git-server @@ -39,7 +59,6 @@ spec: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] - add: ["CHOWN", "DAC_OVERRIDE"] resources: requests: cpu: 50m @@ -71,12 +90,11 @@ spec: - name: cgit-cache mountPath: /var/cache/cgit securityContext: - runAsUser: 0 - runAsGroup: 0 + runAsUser: 33 + runAsGroup: 33 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] - add: ["NET_BIND_SERVICE", "CHOWN", "DAC_OVERRIDE"] resources: requests: cpu: 50m -- cgit v1.2.3 From 4fdca7aded2d07941b741eca7231a5f95ba498d7 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:25:30 +0200 Subject: Fix cgit permissions - use UID 101 (nginx user) The cgit image runs as nginx user (UID 101), not www-data (UID 33). - Update initContainer to chown cache to 101:1000 - Update cgit securityContext to runAsUser: 101 --- f3s/git-server/helm-chart/templates/deployment.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 6c424a0..fe999cb 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -26,7 +26,7 @@ spec: - -c - | mkdir -p /cache-init - chown -R 33:1000 /cache-init + chown -R 101:1000 /cache-init mkdir -p /ssh-init chown -R 0:0 /ssh-init volumeMounts: @@ -90,8 +90,8 @@ spec: - name: cgit-cache mountPath: /var/cache/cgit securityContext: - runAsUser: 33 - runAsGroup: 33 + runAsUser: 101 + runAsGroup: 101 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] -- cgit v1.2.3 From f125eda6ebfd62574d987536d23d7f47ce863e77 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:26:34 +0200 Subject: Set USE_CUSTOM_CONFIG=true to skip cgit template substitution The cgit entrypoint tries to write to /etc/cgitrc which is mounted read-only from our ConfigMap. Set USE_CUSTOM_CONFIG=true to use our custom cgitrc directly without template substitution. --- f3s/git-server/helm-chart/templates/deployment.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index fe999cb..1af9770 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -79,6 +79,8 @@ spec: value: "f3s Git Repository Browser" - name: CGIT_DESC value: "Browse git repositories" + - name: USE_CUSTOM_CONFIG + value: "true" volumeMounts: - name: repos mountPath: /repos -- cgit v1.2.3 From 85231f3f755c1eb0607862532d5a0968a818b8a0 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:27:32 +0200 Subject: Run cgit as root - required for nginx and spawn-fcgi cgit image needs root to: - Bind sockets with spawn-fcgi - Run nginx master process - Write to /var/run/nginx.pid The initContainer already sets up cache dir with proper permissions. --- f3s/git-server/helm-chart/templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 1af9770..bfd9387 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -92,8 +92,8 @@ spec: - name: cgit-cache mountPath: /var/cache/cgit securityContext: - runAsUser: 101 - runAsGroup: 101 + runAsUser: 0 + runAsGroup: 0 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] -- cgit v1.2.3 From 7d71a61f877015cb9abbcd89f1adacc7ed2b7e3e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:29:01 +0200 Subject: Disable cgit caching to avoid permission issues Instead of fighting permission issues with the cgit cache directory, disable caching entirely by: - Setting cache-size=0 in cgitrc - Removing cgit-cache emptyDir volume and mounts - Simplifying initContainer (only SSH keys setup needed) cgit will work without caching, just slightly slower for large repos. --- f3s/git-server/helm-chart/templates/configmap-cgit.yaml | 3 +++ f3s/git-server/helm-chart/templates/deployment.yaml | 8 -------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/configmap-cgit.yaml b/f3s/git-server/helm-chart/templates/configmap-cgit.yaml index 840fbd4..08561f3 100644 --- a/f3s/git-server/helm-chart/templates/configmap-cgit.yaml +++ b/f3s/git-server/helm-chart/templates/configmap-cgit.yaml @@ -12,6 +12,9 @@ data: root-title=f3s Git Repository Browser root-desc=Browse git repositories in f3s cluster + # Disable caching to avoid permission issues + cache-size=0 + # Enable git-config for per-repo settings enable-git-config=1 diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index bfd9387..0fa3620 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -25,13 +25,9 @@ spec: - /bin/sh - -c - | - mkdir -p /cache-init - chown -R 101:1000 /cache-init mkdir -p /ssh-init chown -R 0:0 /ssh-init volumeMounts: - - name: cgit-cache - mountPath: /cache-init - name: ssh-host-keys mountPath: /ssh-init @@ -89,8 +85,6 @@ spec: mountPath: /etc/cgitrc subPath: cgitrc readOnly: true - - name: cgit-cache - mountPath: /var/cache/cgit securityContext: runAsUser: 0 runAsGroup: 0 @@ -116,7 +110,5 @@ spec: - name: cgit-config configMap: name: cgit-config - - name: cgit-cache - emptyDir: {} - name: ssh-host-keys emptyDir: {} -- cgit v1.2.3 From 4378d24067545999a281211d5d8a36595e12e790 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:30:03 +0200 Subject: Override cgit entrypoint to skip chown/chmod The cgit image entrypoint always tries to chown /var/cache/cgit which fails with permission errors. Override the entrypoint to directly: 1. Spawn fcgiwrap as nginx user 2. Start nginx in foreground This skips the problematic chown/chmod and template substitution. --- f3s/git-server/helm-chart/templates/deployment.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 0fa3620..0f4d1bd 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -66,6 +66,11 @@ spec: # Container 2: CGit Web UI - name: cgit image: joseluisq/alpine-cgit:latest + command: ["/bin/sh", "-c"] + args: + - | + spawn-fcgi -u nginx -g nginx -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & + exec nginx -g 'daemon off;' ports: - containerPort: 8080 name: http -- cgit v1.2.3 From 01fcc8e47845f24f0ba16fcd14d30051758503d7 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:31:15 +0200 Subject: Run spawn-fcgi as root to avoid setgid errors Remove -u nginx -g nginx from spawn-fcgi command to run as root. This avoids nginx worker process setgid permission errors. --- f3s/git-server/helm-chart/templates/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 0f4d1bd..c077aa5 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -69,7 +69,7 @@ spec: command: ["/bin/sh", "-c"] args: - | - spawn-fcgi -u nginx -g nginx -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & + spawn-fcgi -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & exec nginx -g 'daemon off;' ports: - containerPort: 8080 -- cgit v1.2.3 From 42a21f794f7082d3ddb6e7906d2a373c8135dd9f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:33:53 +0200 Subject: Remove 'user nginx;' directive to fix nginx worker errors When running as root with fsGroup, nginx workers fail trying to setgid. Remove the 'user nginx;' directive from nginx.conf at startup using sed. This allows nginx to run workers as root without permission errors. --- f3s/git-server/helm-chart/templates/deployment.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index c077aa5..12a235a 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -69,6 +69,8 @@ spec: command: ["/bin/sh", "-c"] args: - | + # Remove 'user nginx;' directive to avoid setgid errors when running as root + sed -i 's/^user nginx;//' /etc/nginx/nginx.conf spawn-fcgi -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & exec nginx -g 'daemon off;' ports: -- cgit v1.2.3 From a14bbf6775c1b62c996ea4b63d1fe1de3ddfc39f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:34:58 +0200 Subject: Remove fsGroup to fix nginx worker setgid errors The fsGroup: 1000 was causing nginx workers to fail with setgid errors even after removing 'user nginx;' directive. Since both containers run as root, fsGroup is not needed for repo access. --- f3s/git-server/helm-chart/templates/deployment.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 12a235a..7381178 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -15,9 +15,6 @@ spec: labels: app: git-server spec: - securityContext: - fsGroup: 1000 - initContainers: - name: setup image: alpine:3.19 -- cgit v1.2.3 From 8a109670240c6604767dbf0a3abb933661cbf600 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:36:11 +0200 Subject: Add SETGID and SETUID capabilities to cgit container Nginx workers need these capabilities to drop privileges. Add SETGID and SETUID to allow nginx workers to start properly. --- f3s/git-server/helm-chart/templates/deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 7381178..b876e0f 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -95,6 +95,7 @@ spec: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] + add: ["SETGID", "SETUID"] resources: requests: cpu: 50m -- cgit v1.2.3 From db7d6238a50622ee1428fda475548819189f788b Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:37:55 +0200 Subject: Fix cgit port mapping - nginx listens on port 80 The cgit nginx default.conf listens on port 80, not 8080. Update: - Container port from 8080 to 80 - Service targetPort from 8080 to 80 --- f3s/git-server/helm-chart/templates/deployment.yaml | 2 +- f3s/git-server/helm-chart/templates/service.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index b876e0f..b867add 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -71,7 +71,7 @@ spec: spawn-fcgi -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & exec nginx -g 'daemon off;' ports: - - containerPort: 8080 + - containerPort: 80 name: http protocol: TCP env: diff --git a/f3s/git-server/helm-chart/templates/service.yaml b/f3s/git-server/helm-chart/templates/service.yaml index 9675e86..b866e7b 100644 --- a/f3s/git-server/helm-chart/templates/service.yaml +++ b/f3s/git-server/helm-chart/templates/service.yaml @@ -16,7 +16,7 @@ spec: - name: http protocol: TCP port: 80 - targetPort: 8080 + targetPort: 80 type: ClusterIP --- apiVersion: v1 -- cgit v1.2.3 From 9bcf2d3961c68ebb4ac4d44af0027391f3cf251d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 11:39:34 +0200 Subject: Fix fcgiwrap socket permissions for nginx workers Nginx workers (running as user nginx) couldn't connect to fcgiwrap.sock created by root. Add chmod 666 to make the socket world-accessible. --- f3s/git-server/helm-chart/templates/deployment.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index b867add..ac2a48d 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -68,7 +68,10 @@ spec: - | # Remove 'user nginx;' directive to avoid setgid errors when running as root sed -i 's/^user nginx;//' /etc/nginx/nginx.conf + # Start fcgiwrap and set socket permissions for nginx user spawn-fcgi -s /var/run/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & + sleep 1 + chmod 666 /var/run/fcgiwrap.sock exec nginx -g 'daemon off;' ports: - containerPort: 80 -- cgit v1.2.3 From 723ebbc6ee9a33462a64bad622f5fbf98353555e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 14:40:42 +0200 Subject: Add SSH connectivity for git-server and configure ArgoCD - Enable SYS_CHROOT capability for git-server SSH to work in containers - Configure ArgoCD repo-server to use SSH key for git-server access - Set DEBUG3 logging in sshd for troubleshooting (temp) Co-Authored-By: Claude Sonnet 4.5 --- f3s/argocd/values.yaml | 18 +++++++++++++++++- f3s/git-server/docker-image/sshd_config | 2 +- f3s/git-server/helm-chart/templates/deployment.yaml | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/f3s/argocd/values.yaml b/f3s/argocd/values.yaml index e3b6887..2d5a4c1 100644 --- a/f3s/argocd/values.yaml +++ b/f3s/argocd/values.yaml @@ -52,19 +52,35 @@ server: # Repo Server configuration (clones repos, generates manifests) repoServer: replicas: 1 + # Pod security context - fsGroup ensures secret volumes are readable by argocd user (999) + podSecurityContext: + fsGroup: 999 # Enable persistence for repo cache - mount PVC at /home/argocd/repo-cache # This avoids conflict with default /tmp mount used by ArgoCD volumes: - name: repo-server-data persistentVolumeClaim: claimName: argocd-repo-server-pvc + # SSH private key for git-server access + - name: argocd-git-ssh-key + secret: + secretName: argocd-git-ssh-key + defaultMode: 0444 volumeMounts: - name: repo-server-data mountPath: /home/argocd/repo-cache - # Configure repo-server to use the persistent cache directory + # Mount SSH key for git operations + - name: argocd-git-ssh-key + mountPath: /home/argocd/.ssh/id_ed25519 + subPath: sshPrivateKey + readOnly: true + # Configure repo-server to use the persistent cache directory and SSH key env: - name: XDG_CACHE_HOME value: /home/argocd/repo-cache + # Configure git to use SSH key and accept new host keys + - name: GIT_SSH_COMMAND + value: "ssh -i /home/argocd/.ssh/id_ed25519 -o StrictHostKeyChecking=accept-new" # Resource limits resources: limits: diff --git a/f3s/git-server/docker-image/sshd_config b/f3s/git-server/docker-image/sshd_config index 7e899e0..da9ef33 100644 --- a/f3s/git-server/docker-image/sshd_config +++ b/f3s/git-server/docker-image/sshd_config @@ -28,7 +28,7 @@ PermitTunnel no # Logging SyslogFacility AUTH -LogLevel INFO +LogLevel DEBUG3 # Performance UseDNS no diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index ac2a48d..6f38d88 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -52,6 +52,7 @@ spec: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] + add: ["SYS_CHROOT"] resources: requests: cpu: 50m -- cgit v1.2.3 From 17be526604eb841052e4d347d3ee05043841fa95 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 14:42:22 +0200 Subject: Add SETGID and SETUID capabilities to git-server SSH privilege separation requires setgroups() and setuid() syscalls. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/helm-chart/templates/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 6f38d88..2223d14 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -52,7 +52,7 @@ spec: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] - add: ["SYS_CHROOT"] + add: ["SYS_CHROOT", "SETGID", "SETUID"] resources: requests: cpu: 50m -- cgit v1.2.3 From 94a85bd7756cf02b8d84f7579738f3dbfa96a673 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 14:44:29 +0200 Subject: Fix authorized_keys permissions via initContainer Copy authorized_keys from secret to emptyDir with git user ownership. This allows SSH to read the keys for authentication. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/helm-chart/templates/deployment.yaml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 2223d14..2ebb095 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -22,11 +22,23 @@ spec: - /bin/sh - -c - | + # Setup SSH host keys directory mkdir -p /ssh-init chown -R 0:0 /ssh-init + # Setup authorized_keys with correct ownership + mkdir -p /ssh-git/.ssh + cp /ssh-keys-secret/authorized_keys /ssh-git/.ssh/authorized_keys + chown -R 1000:1000 /ssh-git/.ssh + chmod 700 /ssh-git/.ssh + chmod 600 /ssh-git/.ssh/authorized_keys volumeMounts: - name: ssh-host-keys mountPath: /ssh-init + - name: git-ssh-keys + mountPath: /ssh-keys-secret + readOnly: true + - name: git-ssh-writable + mountPath: /ssh-git containers: # Container 1: SSH Git Server @@ -40,9 +52,8 @@ spec: volumeMounts: - name: repos mountPath: /repos - - name: git-ssh-keys - mountPath: /home/git/.ssh/authorized_keys - subPath: authorized_keys + - name: git-ssh-writable + mountPath: /home/git/.ssh readOnly: true - name: ssh-host-keys mountPath: /etc/ssh @@ -116,6 +127,8 @@ spec: secret: secretName: git-server-authorized-keys defaultMode: 0400 + - name: git-ssh-writable + emptyDir: {} - name: cgit-config configMap: name: cgit-config -- cgit v1.2.3 From 91749524e7c4afc379cd01b6bb85bfb61135553c Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 14:45:31 +0200 Subject: Fix nested .ssh directory issue Remove extra .ssh directory creation in initContainer. The emptyDir mount point itself is /home/git/.ssh. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/helm-chart/templates/deployment.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 2ebb095..7df943e 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -26,11 +26,11 @@ spec: mkdir -p /ssh-init chown -R 0:0 /ssh-init # Setup authorized_keys with correct ownership - mkdir -p /ssh-git/.ssh - cp /ssh-keys-secret/authorized_keys /ssh-git/.ssh/authorized_keys - chown -R 1000:1000 /ssh-git/.ssh - chmod 700 /ssh-git/.ssh - chmod 600 /ssh-git/.ssh/authorized_keys + # The /ssh-git mount point IS the .ssh directory + cp /ssh-keys-secret/authorized_keys /ssh-git/authorized_keys + chown -R 1000:1000 /ssh-git + chmod 700 /ssh-git + chmod 600 /ssh-git/authorized_keys volumeMounts: - name: ssh-host-keys mountPath: /ssh-init -- cgit v1.2.3 From 431a1277c26d33b34ed2a71c1a8ed6ad836512b2 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 16:00:31 +0200 Subject: Add git-shell to /etc/shells for SSH validity SSH requires user shells to be listed in /etc/shells. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/docker-image/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/f3s/git-server/docker-image/Dockerfile b/f3s/git-server/docker-image/Dockerfile index f721fab..f191443 100644 --- a/f3s/git-server/docker-image/Dockerfile +++ b/f3s/git-server/docker-image/Dockerfile @@ -7,7 +7,8 @@ RUN apk add --no-cache openssh git # This restricts the user to git operations only RUN adduser -D -u 1000 -s /usr/bin/git-shell git && \ mkdir -p /home/git/.ssh /repos && \ - chown -R git:git /home/git /repos + chown -R git:git /home/git /repos && \ + echo "/usr/bin/git-shell" >> /etc/shells # Copy sshd configuration to /tmp (will be copied to /etc/ssh by entrypoint) COPY sshd_config /tmp/sshd_config -- cgit v1.2.3 From c623516302da81ebd599ca21ca5c2dcd5dfcbecd Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 16:01:59 +0200 Subject: Remove readOnly flag from git-ssh-writable mount The emptyDir needs to be writable for SSH to access authorized_keys. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/helm-chart/templates/deployment.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index 7df943e..e48fc8e 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -54,7 +54,6 @@ spec: mountPath: /repos - name: git-ssh-writable mountPath: /home/git/.ssh - readOnly: true - name: ssh-host-keys mountPath: /etc/ssh securityContext: -- cgit v1.2.3 From b471ea62bc0fac98c32b58487e56079b9caf81da Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 16:03:02 +0200 Subject: Make .ssh directory world-readable for SELinux compatibility SELinux prevents root from accessing 700 directories in some contexts. Use 755 for directory and 644 for authorized_keys to allow access. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/helm-chart/templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/f3s/git-server/helm-chart/templates/deployment.yaml b/f3s/git-server/helm-chart/templates/deployment.yaml index e48fc8e..7a7ed57 100644 --- a/f3s/git-server/helm-chart/templates/deployment.yaml +++ b/f3s/git-server/helm-chart/templates/deployment.yaml @@ -29,8 +29,8 @@ spec: # The /ssh-git mount point IS the .ssh directory cp /ssh-keys-secret/authorized_keys /ssh-git/authorized_keys chown -R 1000:1000 /ssh-git - chmod 700 /ssh-git - chmod 600 /ssh-git/authorized_keys + chmod 755 /ssh-git + chmod 644 /ssh-git/authorized_keys volumeMounts: - name: ssh-host-keys mountPath: /ssh-init -- cgit v1.2.3 From a711e16b4ee24b2ea5bef3677196ac88dabac0bb Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 16:04:55 +0200 Subject: Unlock git user account for SSH authentication SSH requires user accounts to be unlocked (not have ! in shadow). Use 'passwd -u' to unlock the git user account. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/docker-image/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/f3s/git-server/docker-image/Dockerfile b/f3s/git-server/docker-image/Dockerfile index f191443..5b703c0 100644 --- a/f3s/git-server/docker-image/Dockerfile +++ b/f3s/git-server/docker-image/Dockerfile @@ -8,7 +8,8 @@ RUN apk add --no-cache openssh git RUN adduser -D -u 1000 -s /usr/bin/git-shell git && \ mkdir -p /home/git/.ssh /repos && \ chown -R git:git /home/git /repos && \ - echo "/usr/bin/git-shell" >> /etc/shells + echo "/usr/bin/git-shell" >> /etc/shells && \ + passwd -u git # Copy sshd configuration to /tmp (will be copied to /etc/ssh by entrypoint) COPY sshd_config /tmp/sshd_config -- cgit v1.2.3 From 97da3d02a87494c8add3709dcc8c6e657a934424 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 16:05:13 +0200 Subject: Reduce SSH logging from DEBUG3 to INFO Debug logging was useful for troubleshooting but not needed in production. Co-Authored-By: Claude Sonnet 4.5 --- f3s/git-server/docker-image/sshd_config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/f3s/git-server/docker-image/sshd_config b/f3s/git-server/docker-image/sshd_config index da9ef33..7e899e0 100644 --- a/f3s/git-server/docker-image/sshd_config +++ b/f3s/git-server/docker-image/sshd_config @@ -28,7 +28,7 @@ PermitTunnel no # Logging SyslogFacility AUTH -LogLevel DEBUG3 +LogLevel INFO # Performance UseDNS no -- cgit v1.2.3 From cf09854f0de91cb2957dc5b53c254896a23031a1 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 19:40:43 +0200 Subject: Migrate example-apache-volume-claim to self-hosted git Test migration of first application from Codeberg to internal git-server. Co-Authored-By: Claude Sonnet 4.5 --- f3s/argocd-apps/test/example-apache-volume-claim.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/f3s/argocd-apps/test/example-apache-volume-claim.yaml b/f3s/argocd-apps/test/example-apache-volume-claim.yaml index e918e87..abd7387 100644 --- a/f3s/argocd-apps/test/example-apache-volume-claim.yaml +++ b/f3s/argocd-apps/test/example-apache-volume-claim.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/example-apache-volume-claim/helm-chart destination: -- cgit v1.2.3 From d47fb321a2a2f9bae3355c25d46876d5defdd490 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 9 Jan 2026 19:41:42 +0200 Subject: Migrate all applications from Codeberg to self-hosted git Updated 17 application manifests to use internal git-server: - Monitoring: grafana-ingress, prometheus, pushgateway - Services: anki-sync-server, audiobookshelf, filebrowser, immich, keybr, kobo-sync-server, miniflux, opodsync, radicale, syncthing, tracing-demo, wallabag, webdav - Infra: registry All applications now fetch from: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git Co-Authored-By: Claude Sonnet 4.5 --- f3s/argocd-apps/infra/registry.yaml | 2 +- f3s/argocd-apps/monitoring/grafana-ingress.yaml | 2 +- f3s/argocd-apps/monitoring/prometheus.yaml | 2 +- f3s/argocd-apps/monitoring/pushgateway.yaml | 2 +- f3s/argocd-apps/services/anki-sync-server.yaml | 2 +- f3s/argocd-apps/services/audiobookshelf.yaml | 2 +- f3s/argocd-apps/services/filebrowser.yaml | 2 +- f3s/argocd-apps/services/immich.yaml | 2 +- f3s/argocd-apps/services/keybr.yaml | 2 +- f3s/argocd-apps/services/kobo-sync-server.yaml | 2 +- f3s/argocd-apps/services/miniflux.yaml | 2 +- f3s/argocd-apps/services/opodsync.yaml | 2 +- f3s/argocd-apps/services/radicale.yaml | 2 +- f3s/argocd-apps/services/syncthing.yaml | 2 +- f3s/argocd-apps/services/tracing-demo.yaml | 2 +- f3s/argocd-apps/services/wallabag.yaml | 2 +- f3s/argocd-apps/services/webdav.yaml | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/f3s/argocd-apps/infra/registry.yaml b/f3s/argocd-apps/infra/registry.yaml index 2948233..653f6b6 100644 --- a/f3s/argocd-apps/infra/registry.yaml +++ b/f3s/argocd-apps/infra/registry.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/registry/helm-chart destination: diff --git a/f3s/argocd-apps/monitoring/grafana-ingress.yaml b/f3s/argocd-apps/monitoring/grafana-ingress.yaml index bc99f02..e8764fd 100644 --- a/f3s/argocd-apps/monitoring/grafana-ingress.yaml +++ b/f3s/argocd-apps/monitoring/grafana-ingress.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/prometheus/grafana-ingress destination: diff --git a/f3s/argocd-apps/monitoring/prometheus.yaml b/f3s/argocd-apps/monitoring/prometheus.yaml index 854948f..75d9d56 100644 --- a/f3s/argocd-apps/monitoring/prometheus.yaml +++ b/f3s/argocd-apps/monitoring/prometheus.yaml @@ -161,7 +161,7 @@ spec: readOnly: true # Source 2: Additional manifests from Git repository - - repoURL: https://codeberg.org/snonux/conf.git + - repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/prometheus/manifests diff --git a/f3s/argocd-apps/monitoring/pushgateway.yaml b/f3s/argocd-apps/monitoring/pushgateway.yaml index 46d69ca..fbc58bc 100644 --- a/f3s/argocd-apps/monitoring/pushgateway.yaml +++ b/f3s/argocd-apps/monitoring/pushgateway.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/pushgateway/helm-chart destination: diff --git a/f3s/argocd-apps/services/anki-sync-server.yaml b/f3s/argocd-apps/services/anki-sync-server.yaml index 850af3e..bd4c1bb 100644 --- a/f3s/argocd-apps/services/anki-sync-server.yaml +++ b/f3s/argocd-apps/services/anki-sync-server.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/anki-sync-server/helm-chart destination: diff --git a/f3s/argocd-apps/services/audiobookshelf.yaml b/f3s/argocd-apps/services/audiobookshelf.yaml index e90e374..78bbe69 100644 --- a/f3s/argocd-apps/services/audiobookshelf.yaml +++ b/f3s/argocd-apps/services/audiobookshelf.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/audiobookshelf/helm-chart destination: diff --git a/f3s/argocd-apps/services/filebrowser.yaml b/f3s/argocd-apps/services/filebrowser.yaml index 5e32211..e0cc040 100644 --- a/f3s/argocd-apps/services/filebrowser.yaml +++ b/f3s/argocd-apps/services/filebrowser.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/filebrowser/helm-chart destination: diff --git a/f3s/argocd-apps/services/immich.yaml b/f3s/argocd-apps/services/immich.yaml index 6fac25b..579e63a 100644 --- a/f3s/argocd-apps/services/immich.yaml +++ b/f3s/argocd-apps/services/immich.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/immich/helm-chart destination: diff --git a/f3s/argocd-apps/services/keybr.yaml b/f3s/argocd-apps/services/keybr.yaml index 411ca6a..f3991f6 100644 --- a/f3s/argocd-apps/services/keybr.yaml +++ b/f3s/argocd-apps/services/keybr.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/keybr/helm-chart destination: diff --git a/f3s/argocd-apps/services/kobo-sync-server.yaml b/f3s/argocd-apps/services/kobo-sync-server.yaml index eaae84c..c99006f 100644 --- a/f3s/argocd-apps/services/kobo-sync-server.yaml +++ b/f3s/argocd-apps/services/kobo-sync-server.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: - repoURL: https://codeberg.org/snonux/conf.git + repoURL: ssh://git@git-server.cicd.svc.cluster.local/repos/repos/conf.git targetRevision: master path: f3s/kobo-sync-server/helm-chart destination: diff --git a/f3s/argocd-apps/services/miniflux.yaml b/f3s/argocd-apps/services/miniflux.yaml index 9b3a23a..c37f5cb 100644 --- a/f3s/argocd-apps/services/miniflux.yaml +++ b/f3s/argocd-apps/services/miniflux.yaml @@ -8,7 +8,7 @@ metadata: spec: project: default source: -