diff options
| author | Paul Buetow <paul@buetow.org> | 2026-01-09 11:06:02 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-01-09 11:06:28 +0200 |
| commit | 634f98a69d398f82800d349489d3c279879aaae6 (patch) | |
| tree | c27f4ea08cb7299478c71bae9d27b493cab9b745 /f3s/git-server | |
| parent | 9ab8ab11ab815f9025ad6e24eac46f481f8f385f (diff) | |
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 <noreply@anthropic.com>
Diffstat (limited to 'f3s/git-server')
| -rw-r--r-- | f3s/git-server/docker-image/Dockerfile | 23 | ||||
| -rw-r--r-- | f3s/git-server/docker-image/Justfile | 7 | ||||
| -rw-r--r-- | f3s/git-server/docker-image/sshd_config | 35 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/Chart.yaml | 5 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/templates/configmap-cgit.yaml | 23 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/templates/deployment.yaml | 97 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/templates/ingress.yaml | 24 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/templates/persistent-volume.yaml | 27 | ||||
| -rw-r--r-- | f3s/git-server/helm-chart/templates/service.yaml | 38 |
9 files changed, 279 insertions, 0 deletions
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 |
