diff --git a/README.md b/README.md index 655a5de..e946e1d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ -# infrastructure +# Softvisor GmbH Infrastruktur +# Installation von Kubernetes + +# Installation von Gitea +Um Gitea zu installieren, muss folgendes installiert sein: +- helm +- microk8s + +Der Windows-Server muss im gleichen Tailscale-Netzwerk sein, in dem der AuthKey generiert wurde. + +## Values +Es müssen in der Datei ```gitea/values.yaml``` folgende Attribute gesetzt sein: + +1. tailscale.authKey +2. gitea.gitea.ldap.bindPassword + +Die betroffenen Zeilen sind in der Datei markiert. + +## Befehl +Im Ordner ```infrastructure``` ausführen: +``` +helm upgrade gitea gitea --install \ +-n gitea \ +``` \ No newline at end of file diff --git a/backup/gitea-backup.yaml b/backup/gitea-backup.yaml new file mode 100644 index 0000000..e35fb1c --- /dev/null +++ b/backup/gitea-backup.yaml @@ -0,0 +1,103 @@ +# gitea-backup.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: gitea-backup-script + namespace: gitea +data: + backup.sh: | + #!/bin/bash + set -euo pipefail + + BACKUP_DIR="/backup/gitea" + DATE=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="gitea_${DATE}.zip" + RETENTION_DAYS=30 + + echo "[$(date)] Starting Gitea backup..." + + # Create backup directory (may fail if permissions issue, that's ok) + mkdir -p ${BACKUP_DIR} 2>/dev/null || true + + # Change to temp directory for dump + cd /tmp + + # Run Gitea dump + if gitea dump -c /data/gitea/conf/app.ini --type zip; then + echo "[$(date)] ✓ Gitea dump successful" + + # Find the created dump file + DUMP_FILE=$(ls -t gitea-dump-*.zip 2>/dev/null | head -1) + + if [ -n "$DUMP_FILE" ] && [ -f "$DUMP_FILE" ]; then + # Move to backup directory with our naming convention + mv "$DUMP_FILE" ${BACKUP_DIR}/${BACKUP_FILE} + SIZE=$(du -h ${BACKUP_DIR}/${BACKUP_FILE} | cut -f1) + echo "[$(date)] Backup size: ${SIZE}" + else + echo "[$(date)] ✗ ERROR: Dump file not found!" + exit 1 + fi + else + echo "[$(date)] ✗ Gitea dump failed!" + exit 1 + fi + + # Cleanup old backups + echo "[$(date)] Cleaning up backups older than ${RETENTION_DAYS} days..." + find ${BACKUP_DIR} -name "gitea_*.zip" -mtime +${RETENTION_DAYS} -delete 2>/dev/null || true + + # List recent backups + echo "[$(date)] Recent backups:" + ls -lh ${BACKUP_DIR} 2>/dev/null | tail -5 || echo "Could not list backups" + + echo "[$(date)] Gitea backup completed successfully" +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: gitea-backup + namespace: gitea +spec: + schedule: "0 3 * * *" + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + spec: + securityContext: + runAsUser: 1000 # Run as git user + runAsGroup: 1000 # Run as git group + fsGroup: 1000 # Set filesystem group + containers: + - name: gitea-backup + image: gitea/gitea:1.24 # Match your version + command: ["/bin/bash", "/scripts/backup.sh"] + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + volumeMounts: + - name: backup-script + mountPath: /scripts + - name: gitea-data + mountPath: /data + - name: backup-storage + mountPath: /backup + - name: tmp + mountPath: /tmp + volumes: + - name: backup-script + configMap: + name: gitea-backup-script + defaultMode: 0755 + - name: gitea-data + persistentVolumeClaim: + claimName: gitea-shared-storage # Adjust to your PVC name + - name: backup-storage + hostPath: + path: /mnt/backup/k8s-backups # Fixed path + type: DirectoryOrCreate + - name: tmp + emptyDir: {} + restartPolicy: OnFailure diff --git a/backup/postgres-backup.yaml b/backup/postgres-backup.yaml new file mode 100644 index 0000000..2b78442 --- /dev/null +++ b/backup/postgres-backup.yaml @@ -0,0 +1,92 @@ +# postgres-backup.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgres-backup-script + namespace: gitea +data: + backup.sh: | + #!/bin/bash + set -euo pipefail + + BACKUP_DIR="/backup/postgres" + DATE=$(date +%Y%m%d_%H%M%S) + BACKUP_FILE="postgres_${DATE}.sql.gz" + RETENTION_DAYS=30 + + echo "[$(date)] Starting PostgreSQL backup..." + + # Create backup directory + mkdir -p ${BACKUP_DIR} + # Perform backup + + if pg_dump -h ${POSTGRES_HOST} -U ${POSTGRES_USER} ${POSTGRES_DB} | gzip > ${BACKUP_DIR}/${BACKUP_FILE}; then + echo "[$(date)] ✓ Backup successful: ${BACKUP_FILE}" + + # Verify backup file exists and is not empty + if [ -s ${BACKUP_DIR}/${BACKUP_FILE} ]; then + SIZE=$(du -h ${BACKUP_DIR}/${BACKUP_FILE} | cut -f1) + echo "[$(date)] Backup size: ${SIZE}" + else + echo "[$(date)] ✗ ERROR: Backup file is empty!" + exit 1 + fi + else + echo "[$(date)] ✗ Backup failed!" + exit 1 + fi + + # Cleanup old backups + echo "[$(date)] Cleaning up backups older than ${RETENTION_DAYS} days..." + find ${BACKUP_DIR} -name "postgres_*.sql.gz" -mtime +${RETENTION_DAYS} -delete + + # List recent backups + echo "[$(date)] Recent backups:" + ls -lh ${BACKUP_DIR} | tail -5 + + echo "[$(date)] Backup completed successfully" +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: postgres-backup + namespace: gitea +spec: + schedule: "0 2 * * *" # Daily at 2 AM + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + spec: + containers: + - name: postgres-backup + image: postgres:17 # Match your PostgreSQL version + command: ["/bin/bash", "/scripts/backup.sh"] + env: + - name: POSTGRES_HOST + value: "gitea-postgresql" # Adjust to your service name + - name: POSTGRES_USER + value: "gitea" + - name: POSTGRES_DB + value: "gitea" + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: gitea-postgresql # Adjust to your secret name + key: password + volumeMounts: + - name: backup-script + mountPath: /scripts + - name: backup-storage + mountPath: /backup + volumes: + - name: backup-script + configMap: + name: postgres-backup-script + defaultMode: 0755 + - name: backup-storage + hostPath: + path: /mnt/backup/k8s-backups + type: DirectoryOrCreate + restartPolicy: OnFailure diff --git a/gitea/.helmignore b/gitea/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/gitea/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/gitea/Chart.lock b/gitea/Chart.lock new file mode 100644 index 0000000..af4913c --- /dev/null +++ b/gitea/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: gitea + repository: https://dl.gitea.io/charts/ + version: 12.1.1 +digest: sha256:b405ec6017f5958c8222d326e69e15f7be65f42988767b63d9b5648f4bfd60a8 +generated: "2025-06-28T16:26:16.845530159+02:00" diff --git a/gitea/Chart.yaml b/gitea/Chart.yaml new file mode 100644 index 0000000..846eaa8 --- /dev/null +++ b/gitea/Chart.yaml @@ -0,0 +1,29 @@ +apiVersion: v2 +name: gitea +description: Infrastructure configuration for Softvisor GmbH + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" + +dependencies: + - name: gitea + version: 12.1.1 + repository: https://dl.gitea.io/charts/ \ No newline at end of file diff --git a/gitea/charts/gitea-12.1.1.tgz b/gitea/charts/gitea-12.1.1.tgz new file mode 100644 index 0000000..c8829b7 Binary files /dev/null and b/gitea/charts/gitea-12.1.1.tgz differ diff --git a/gitea/templates/NOTES.txt b/gitea/templates/NOTES.txt new file mode 100644 index 0000000..e69de29 diff --git a/gitea/templates/_helpers.tpl b/gitea/templates/_helpers.tpl new file mode 100644 index 0000000..8b1ee58 --- /dev/null +++ b/gitea/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "gitea.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "gitea.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "gitea.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "gitea.labels" -}} +helm.sh/chart: {{ include "gitea.chart" . }} +{{ include "gitea.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "gitea.selectorLabels" -}} +app.kubernetes.io/name: {{ include "gitea.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "gitea.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "gitea.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/gitea/templates/admin-secret.yaml b/gitea/templates/admin-secret.yaml new file mode 100644 index 0000000..11bf477 --- /dev/null +++ b/gitea/templates/admin-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: gitea-admin-secret +type: Opaque +stringData: + username: softvisor + password: "#softvisor!2024?" + email: "git@softvisor.de" \ No newline at end of file diff --git a/gitea/templates/letsencrypt-issuer.yaml b/gitea/templates/letsencrypt-issuer.yaml new file mode 100644 index 0000000..3a54a2f --- /dev/null +++ b/gitea/templates/letsencrypt-issuer.yaml @@ -0,0 +1,16 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: lets-encrypt +spec: + acme: + email: {{ .Values.global.domain.mail }} + server: https://acme-v02.api.letsencrypt.org/directory + privateKeySecretRef: + # Secret resource that will be used to store the account's private key. + name: lets-encrypt-private-key + # Add a single challenge solver, HTTP01 using nginx + solvers: + - http01: + ingress: + class: public \ No newline at end of file diff --git a/gitea/values.yaml b/gitea/values.yaml new file mode 100644 index 0000000..256c780 --- /dev/null +++ b/gitea/values.yaml @@ -0,0 +1,59 @@ +global: + domain: + git: &domain git.dev.softvisor.de + mail: &mail admin@softvisor.de + +gitea: + valkey-cluster: + enabled: false + valkey: + enabled: true + postgresql: + enabled: true + postgresql-ha: + enabled: false + + persistence: + enabled: true + + ingress: + enabled: true + annotations: + kubernetes.io/ingress.class: public + cert-manager.io/cluster-issuer: lets-encrypt + tls: + - secretName: lets-encrypt-private-key + hosts: + - *domain + hosts: + - host: *domain + paths: + - path: / + pathType: Prefix + + service: + http: + type: ClusterIP + port: 3000 + ssh: + type: LoadBalancer + port: 22 + clusterIP: None + annotations: + metallb.universe.tf/allow-shared-ip: test + + gitea: + admin: + email: *mail + existingSecret: gitea-admin-secret + config: + database: + DB_TYPE: postgres + indexer: + ISSUE_INDEXER_TYPE: bleve + REPO_INDEXER_ENABLED: true + server: + SSH_DOMAIN: *domain + service: + DISABLE_REGISTRATION: true + SHOW_REGISTRATION_BUTTON: false \ No newline at end of file