apiVersion: apps/v1 kind: Deployment metadata: name: git-server namespace: cicd labels: app: git-server spec: replicas: 1 # Recreate so the old pod fully terminates before the new one starts — # avoids NFS-lock races on the hostPath-backed PVC during rolling updates. strategy: type: Recreate selector: matchLabels: app: git-server template: metadata: labels: app: git-server spec: securityContext: fsGroup: 33 initContainers: - name: nfs-check-repos image: busybox:stable command: - sh - -c - | test -f /mnt/.nfs-sentinel || ( echo "ERROR: NFS sentinel missing at /mnt/.nfs-sentinel" echo "refusing to start; node likely has NFS unmounted" echo "pod would otherwise bind-mount the local-XFS shadow" exit 1 ) volumeMounts: - name: repos mountPath: /mnt readOnly: true - name: setup image: alpine:3.19 command: - /bin/sh - -c - | # Install openssh for key generation apk add --no-cache openssh # Setup SSH host keys - only generate if they don't exist (persist across restarts) mkdir -p /ssh-persistent if [ ! -f /ssh-persistent/ssh_host_ed25519_key ]; then echo "Generating new SSH host keys (first time setup)..." ssh-keygen -A -f /ssh-persistent/.. mv /ssh-persistent/../etc/ssh/ssh_host_* /ssh-persistent/ 2>/dev/null || true chown -R 1001:33 /ssh-persistent chmod 600 /ssh-persistent/ssh_host_*_key chmod 644 /ssh-persistent/ssh_host_*_key.pub else echo "SSH host keys already exist, reusing them." fi # Copy sshd_config if not exists if [ ! -f /ssh-persistent/sshd_config ]; then echo "Copying sshd_config to persistent storage..." cp /sshd-config/sshd_config /ssh-persistent/sshd_config chown 1001:33 /ssh-persistent/sshd_config chmod 644 /ssh-persistent/sshd_config fi # Copy SSH host keys from NFS to local emptyDir # OpenSSH refuses to load keys from NFS for security reasons echo "Copying SSH keys to local storage..." cp -a /ssh-persistent/* /ssh-local/ chown -R 1001:33 /ssh-local chmod 755 /ssh-local chmod 600 /ssh-local/ssh_host_*_key chmod 644 /ssh-local/ssh_host_*_key.pub chmod 644 /ssh-local/sshd_config # Setup authorized_keys with correct ownership # The /ssh-git mount point IS the .ssh directory # UID 1001 and GID 33 match the NFS file ownership cp /ssh-keys-secret/authorized_keys /ssh-git/authorized_keys chown -R 1001:33 /ssh-git chmod 755 /ssh-git chmod 644 /ssh-git/authorized_keys volumeMounts: - name: repos mountPath: /ssh-persistent subPath: ssh-keys - name: ssh-host-keys mountPath: /ssh-local - name: git-ssh-keys mountPath: /ssh-keys-secret readOnly: true - name: git-ssh-writable mountPath: /ssh-git - name: sshd-config mountPath: /sshd-config readOnly: true - name: install-git-http-backend image: alpine:3.19 command: - /bin/sh - -c - | # Install git-daemon package (includes git-http-backend) apk add --no-cache git-daemon # Copy git-http-backend to shared volume mkdir -p /git-binaries cp /usr/libexec/git-core/git-http-backend /git-binaries/ chmod 755 /git-binaries/git-http-backend volumeMounts: - name: cgit-runtime mountPath: /git-binaries containers: # 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 protocol: TCP volumeMounts: - name: repos mountPath: /repos subPath: repos - name: git-ssh-writable mountPath: /home/git/.ssh - name: ssh-host-keys mountPath: /etc/ssh securityContext: runAsUser: 1001 runAsGroup: 33 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] resources: requests: cpu: 50m memory: 128Mi limits: cpu: 250m memory: 256Mi # Container 2: CGit Web UI + git-http-backend - name: cgit image: joseluisq/alpine-cgit:latest command: ["/bin/sh", "-c"] args: - | # Add git safe.directory exception for repository ownership git config --global --add safe.directory /repos/conf.git # Configure git repository for HTTP access git config --file /repos/conf.git/config http.receivepack true git config --file /repos/conf.git/config http.uploadpack true # Copy nginx configs to writable location and modify them cp /etc/nginx/nginx.conf /tmp/nginx.conf cp -r /etc/nginx/conf.d /tmp/conf.d cp /etc/nginx/fastcgi_params /tmp/fastcgi_params sed -i 's/^user nginx;//' /tmp/nginx.conf sed -i 's|pid /var/run/nginx.pid;|pid /tmp/nginx.pid;|' /tmp/nginx.conf sed -i 's|/etc/nginx/conf.d/|/tmp/conf.d/|' /tmp/nginx.conf # Update default.conf to use /tmp socket for cgit sed -i 's|unix:/var/run/fcgiwrap.sock|unix:/tmp/fcgiwrap.sock|' /tmp/conf.d/default.conf # Add git-http-backend location to default.conf (insert before the cgit location) # Find the server block and add the location directive # Note: git-http-backend is copied to /tmp by initContainer sed -i '/location \/ {/i \ # Git HTTP backend for clone/fetch/pull operations\ location ~ ^/([^/]+\\.git)/(.*) {\ fastcgi_pass unix:/tmp/fcgiwrap.sock;\ fastcgi_param SCRIPT_FILENAME /tmp/git-http-backend;\ fastcgi_param GIT_PROJECT_ROOT /repos;\ fastcgi_param GIT_HTTP_EXPORT_ALL "";\ fastcgi_param PATH_INFO /$1/$2;\ fastcgi_param REMOTE_USER $remote_user;\ include /tmp/fastcgi_params;\ # Increase timeouts for git operations (clones can be large)\ fastcgi_read_timeout 300s;\ fastcgi_send_timeout 300s;\ fastcgi_buffering off;\ }\ ' /tmp/conf.d/default.conf # Start fcgiwrap with socket in /tmp spawn-fcgi -s /tmp/fcgiwrap.sock -n -- /usr/bin/fcgiwrap & sleep 1 chmod 666 /tmp/fcgiwrap.sock exec nginx -c /tmp/nginx.conf -g 'daemon off;' ports: - containerPort: 80 name: http protocol: TCP env: - name: HOME value: "/tmp" - name: CGIT_TITLE value: "f3s Git Repository Browser" - name: CGIT_DESC value: "Browse git repositories" - name: USE_CUSTOM_CONFIG value: "true" volumeMounts: - name: repos mountPath: /repos subPath: repos readOnly: false - name: cgit-config mountPath: /etc/cgitrc subPath: cgitrc readOnly: true - name: cgit-runtime mountPath: /tmp securityContext: runAsUser: 33 runAsGroup: 33 allowPrivilegeEscalation: false capabilities: drop: ["ALL"] 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: git-ssh-writable emptyDir: {} - name: cgit-config configMap: name: cgit-config - name: sshd-config configMap: name: git-server-sshd-config - name: ssh-host-keys emptyDir: {} - name: cgit-runtime emptyDir: {}