diff --git a/deploy/Makefile b/deploy/Makefile index f3c11d4..8b849e1 100644 --- a/deploy/Makefile +++ b/deploy/Makefile @@ -4,7 +4,8 @@ dev: @kubectl kustomize --enable-helm dev prod: load-prod-env - @kubectl kustomize --enable-helm prod | envsubst + # go install https://git.netflux.io/rob/envfilesubst@latest + @kubectl kustomize --enable-helm prod | envfilesubst -f prod/secrets/env load-prod-env: $(eval include prod/secrets/env) diff --git a/deploy/base/deploy-ingress-nginx.yaml b/deploy/base/deploy-ingress-nginx.yaml new file mode 100644 index 0000000..87584e0 --- /dev/null +++ b/deploy/base/deploy-ingress-nginx.yaml @@ -0,0 +1,8 @@ +# Add port 22 (enabled via the --tcp-services-configmap argument in the +# overlays) to the Pod's port list for informational reasons: +- op: add + path: /spec/template/spec/containers/0/ports/- + value: + containerPort: 22 + name: ssh + protocol: TCP diff --git a/deploy/base/gitea-init-directory-structure.sh b/deploy/base/gitea-init-directory-structure.sh new file mode 100644 index 0000000..62c12dd --- /dev/null +++ b/deploy/base/gitea-init-directory-structure.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Initializing directory structure..." + +mkdir -p /data/git/.ssh +chmod -R 700 /data/git/.ssh + +echo "Creating GITEA_CUSTOM=$GITEA_CUSTOM ..." +mkdir -p "$GITEA_CUSTOM" +chmod 0500 "$GITEA_CUSTOM" + +echo "Creating GITEA_TEMP=$GITEA_TEMP ..." +mkdir -p "$GITEA_TEMP" +chmod ug+rwx "$GITEA_TEMP" + +echo "Done" diff --git a/deploy/base/gitea-setup.sh b/deploy/base/gitea-setup.sh new file mode 100644 index 0000000..40a71c3 --- /dev/null +++ b/deploy/base/gitea-setup.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running Gitea migrations..." + +gitea migrate + +echo "Setting up Gitea admin user..." + +# https://gitea.com/gitea/helm-chart/src/commit/80032dfc5c34950edea384e0227f7ab7c994f4ef/templates/gitea/init.yaml#L54 +function configure_admin_user() { + local account_id=$(gitea admin user list --admin | grep -e "\s\+${GITEA_ADMIN_USERNAME}\s\+" | awk -F " " "{printf \$1}") + if [[ -z "${account_id}" ]]; then + echo "No admin user '${GITEA_ADMIN_USERNAME}' found. Creating now..." + gitea admin user create --admin --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" --email "${GITEA_ADMIN_EMAIL}" --must-change-password=false + echo '...created.' + else + echo "Admin account '${GITEA_ADMIN_USERNAME}' already exists. Running update to sync password..." + gitea admin user change-password --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" + echo '...password sync done.' + fi +} + +configure_admin_user + +echo "Done" diff --git a/deploy/base/inflated/grafana/templates/configmap.yaml b/deploy/base/inflated/grafana/templates/configmap.yaml index 0e526ed..0496497 100644 --- a/deploy/base/inflated/grafana/templates/configmap.yaml +++ b/deploy/base/inflated/grafana/templates/configmap.yaml @@ -48,6 +48,13 @@ data: set -euf mkdir -p /var/lib/grafana/dashboards/default + curl -skf \ + --connect-timeout 60 \ + --max-time 60 \ + -H "Accept: application/json" \ + -H "Content-Type: application/json;charset=UTF-8" \ + "https://grafana.com/api/dashboards/13192/revisions/1/download" | sed '/-- .* --/! s/"datasource":.*,/"datasource": "Prometheus",/g'\ + > "/var/lib/grafana/dashboards/default/gitea.json" curl -skf \ --connect-timeout 60 \ --max-time 60 \ diff --git a/deploy/base/inflated/grafana/templates/deployment.yaml b/deploy/base/inflated/grafana/templates/deployment.yaml index 0353e73..db1c1eb 100644 --- a/deploy/base/inflated/grafana/templates/deployment.yaml +++ b/deploy/base/inflated/grafana/templates/deployment.yaml @@ -26,7 +26,7 @@ spec: app.kubernetes.io/name: grafana app.kubernetes.io/instance: grafana annotations: - checksum/config: 8a616a59613b7d132be6411ac28ec9efa8482e7a6e98dce5a84c1279a03bf35f + checksum/config: da84e3fbedccb40edb325ecae9c60acd826813da426696f663f3ad0ac42d3275 checksum/dashboards-json-config: 60bfce132b37398fa9329494762f049aebef4ba473dabdd67d4f15d6a86a578c checksum/sc-dashboard-provider-config: 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b spec: diff --git a/deploy/base/ingress.yaml b/deploy/base/ingress.yaml index 81fb5d0..2f9005f 100644 --- a/deploy/base/ingress.yaml +++ b/deploy/base/ingress.yaml @@ -36,3 +36,13 @@ spec: name: element port: name: http + - host: gitea + http: + paths: + - pathType: Prefix + path: "/" + backend: + service: + name: gitea + port: + name: http diff --git a/deploy/base/kustomization.yaml b/deploy/base/kustomization.yaml index 4a4657e..8303969 100644 --- a/deploy/base/kustomization.yaml +++ b/deploy/base/kustomization.yaml @@ -77,3 +77,24 @@ resources: - deploy-element.yaml - svc-element.yaml + +- statefulset-gitea.yaml +- svc-gitea.yaml + +configMapGenerator: +- name: gitea-scripts + files: + - init-directory-structure.sh=gitea-init-directory-structure.sh + - setup.sh=gitea-setup.sh + +patches: +# Patch the ingress-nginx service to expose port 22 for Gitea SSH access. +- target: + kind: Service + name: ingress-nginx-controller + path: svc-ingress-nginx.yaml + +- target: + kind: Deployment + name: ingress-nginx-controller + path: deploy-ingress-nginx.yaml diff --git a/deploy/base/statefulset-gitea.yaml b/deploy/base/statefulset-gitea.yaml new file mode 100644 index 0000000..bff496c --- /dev/null +++ b/deploy/base/statefulset-gitea.yaml @@ -0,0 +1,164 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: gitea + labels: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea +spec: + serviceName: gitea + selector: + matchLabels: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + template: + metadata: + labels: + app.kubernetes.io/name: gitea + app.kubernetes.io/instance: gitea + annotations: + prometheus.io/port: "3000" + prometheus.io/scrape: "true" + spec: + securityContext: + fsGroup: 1000 + runAsUser: 1000 + runAsGroup: 1000 + initContainers: + - name: init-gitea + image: gitea/gitea:1.16.7-rootless + imagePullPolicy: IfNotPresent + command: ["/usr/sbin/init-directory-structure.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_CUSTOM + value: /data/gitea/custom + - name: GITEA_TEMP + value: /tmp/gitea + - name: TMPDIR + value: /tmp/gitea + volumeMounts: + - mountPath: /data + name: data + - mountPath: /tmp + name: temp + - mountPath: /usr/sbin/init-directory-structure.sh + subPath: init-directory-structure.sh + name: scripts + - name: setup-gitea + image: gitea/gitea:1.16.7-rootless + imagePullPolicy: IfNotPresent + command: ["/usr/sbin/setup.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_CUSTOM + value: /data/gitea/custom + - name: GITEA_TEMP + value: /tmp/gitea + - name: GITEA_ADMIN_USERNAME + valueFrom: + secretKeyRef: + name: gitea-config + key: admin-username + - name: GITEA_ADMIN_EMAIL + valueFrom: + secretKeyRef: + name: gitea-config + key: admin-email + - name: GITEA_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: gitea-config + key: admin-password + volumeMounts: + - mountPath: /data + name: data + - mountPath: /tmp + name: temp + - mountPath: /data/gitea/conf/app.ini + subPath: config.ini + name: config + - mountPath: /usr/sbin/setup.sh + subPath: setup.sh + name: scripts + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + containers: + - name: gitea + image: gitea/gitea:1.16.7-rootless + imagePullPolicy: IfNotPresent + ports: + - name: http + protocol: TCP + containerPort: 3000 + - name: ssh + protocol: TCP + containerPort: 2222 + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_CUSTOM + value: /data/gitea/custom + - name: GITEA_TEMP + value: /tmp/gitea + - name: TMPDIR + value: /tmp/gitea + volumeMounts: + - mountPath: /tmp + name: temp + - mountPath: /data/gitea/conf/app.ini + subPath: config.ini + name: config + - mountPath: /data + name: data + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "500m" + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: 30 + successThreshold: 1 + failureThreshold: 10 + periodSeconds: 10 + timeoutSeconds: 1 + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + volumes: + - name: temp + emptyDir: {} + - name: config + secret: + secretName: gitea-config + - name: scripts + configMap: + name: gitea-scripts + defaultMode: 0700 + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi diff --git a/deploy/base/svc-gitea.yaml b/deploy/base/svc-gitea.yaml new file mode 100644 index 0000000..6d95cf3 --- /dev/null +++ b/deploy/base/svc-gitea.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: gitea + app.kubernetes.io/name: gitea + name: gitea +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 3000 + - name: ssh + port: 22 + protocol: TCP + targetPort: 2222 + selector: + app.kubernetes.io/instance: gitea + app.kubernetes.io/name: gitea + type: ClusterIP diff --git a/deploy/base/svc-ingress-nginx.yaml b/deploy/base/svc-ingress-nginx.yaml new file mode 100644 index 0000000..561044d --- /dev/null +++ b/deploy/base/svc-ingress-nginx.yaml @@ -0,0 +1,9 @@ +--- +# Force the ingress-nginx service to expose port 22. This allows traffic to be +# routed to the Gitea SSH service. +- op: add + path: /spec/ports/- + value: + nodePort: 32050 + port: 22 + name: ssh diff --git a/deploy/base/svc-invidious.yaml b/deploy/base/svc-invidious.yaml index d0df992..78d6dac 100644 --- a/deploy/base/svc-invidious.yaml +++ b/deploy/base/svc-invidious.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: diff --git a/deploy/base/values/grafana.yaml b/deploy/base/values/grafana.yaml index 0622f04..16c0983 100644 --- a/deploy/base/values/grafana.yaml +++ b/deploy/base/values/grafana.yaml @@ -46,3 +46,7 @@ dashboards: gnetId: 12006 revision: 1 datasource: Prometheus + gitea: + gnetId: 13192 + revision: 1 + datasource: Prometheus diff --git a/deploy/dev/cm-ingress-nginx-tcp-services.yaml b/deploy/dev/cm-ingress-nginx-tcp-services.yaml new file mode 100644 index 0000000..5ad6a7f --- /dev/null +++ b/deploy/dev/cm-ingress-nginx-tcp-services.yaml @@ -0,0 +1,8 @@ +# configMapGenerator does not accept integer keys in the current version of +# Kustomize. See: https://github.com/kubernetes-sigs/kustomize/issues/3446 +apiVersion: v1 +kind: ConfigMap +metadata: + name: ingress-nginx-tcp-services +data: + "22": "default/dev-gitea:ssh" diff --git a/deploy/dev/deploy-ingress-nginx.yaml b/deploy/dev/deploy-ingress-nginx.yaml index 1884b1d..20d8355 100644 --- a/deploy/dev/deploy-ingress-nginx.yaml +++ b/deploy/dev/deploy-ingress-nginx.yaml @@ -5,6 +5,9 @@ - op: replace path: /spec/template/spec/containers/0/args/5 value: "--configmap=$(POD_NAMESPACE)/dev-ingress-nginx-controller" +- op: add + path: /spec/template/spec/containers/0/args/- + value: "--tcp-services-configmap=$(POD_NAMESPACE)/dev-ingress-nginx-tcp-services" - op: replace path: /spec/template/spec/volumes/0/secret/secretName value: dev-ingress-nginx-admission diff --git a/deploy/dev/gitea-config.ini b/deploy/dev/gitea-config.ini new file mode 100644 index 0000000..ef62459 --- /dev/null +++ b/deploy/dev/gitea-config.ini @@ -0,0 +1,78 @@ +APP_NAME = git.netflux.io +RUN_MODE = dev +RUN_USER = git + +[server] +APP_DATA_PATH = /data/gitea +PROTOCOL = http +HTTP_PORT = 3000 +ROOT_URL = http://localhost:3000 +SSH_DOMAIN = localhost +SSH_LISTEN_PORT = 2222 +SSH_PORT = 2222 +START_SSH_SERVER = true +ENABLE_PPROF = false +DOMAIN = localhost:3000 +OFFLINE_MODE = false +LFS_START_SERVER = true +LFS_JWT_SECRET = ebGusL71uqCv_YcAzbhoINeXMj1DtMO3q6StXHDzyi8 + +[log] +LEVEL = debug + +[lfs] +PATH = /data/git/lfs + +[repository] +ROOT = /data/git/repositories + +[repository.local] +LOCAL_COPY_PATH = /tmp/gitea/local-repo + +[repository.upload] +TEMP_PATH = /tmp/gitea/uploads + +[attachment] +PATH = /data/gitea/attachments + +[security] +INSTALL_LOCK = true +SECRET_KEY = xLajy1YZBzgL4eBVPYz8lcnnogaRM9RumGlJr7AayCnGnltwa52js7jzgJ91RZki +INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE2NTIzNTE0MDF9.MHeGn00cfFY42kmK30JKl0OVRiqTdTMEIB0zpVlkitY +LOGIN_REMEMBER_DAYS = 30 + +[metrics] +ENABLED = true + +[oauth2] +JWT_SECRET = kkW6JNMATaBvf9tYDxQ8D3tObKVh7DbI4tBcCQQrBn8 + +[session] +PROVIDER = file +COOKIE_NAME = gitea_login +SAME_SITE = strict + +[picture] +AVATAR_UPLOAD_PATH = /data/gitea/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars +DISABLE_GRAVATAR = false +ENABLE_FEDERATED_AVATAR = true + +[service] +DISABLE_REGISTRATION = false +REQUIRE_SIGNIN_VIEW = false +REGISTER_EMAIL_CONFIRM = false +ENABLE_NOTIFY_MAIL = true +ALLOW_ONLY_EXTERNAL_REGISTRATION = false +ENABLE_CAPTCHA = false +DEFAULT_KEEP_EMAIL_PRIVATE = true +DEFAULT_ALLOW_CREATE_ORGANIZATION = true +DEFAULT_ENABLE_TIMETRACKING = true +NO_REPLY_ADDRESS = noreply.localhost + +[database] +DB_TYPE = postgres +HOST = dev-db +PASSWD = testme +NAME = gitea +USER = postgres diff --git a/deploy/dev/ingress.yaml b/deploy/dev/ingress.yaml index 61dde4e..406faca 100644 --- a/deploy/dev/ingress.yaml +++ b/deploy/dev/ingress.yaml @@ -12,6 +12,7 @@ - grafana.local - invidious.local - element.local + - gitea.local - op: replace path: /spec/rules/0/host value: grafana.local @@ -21,3 +22,6 @@ - op: replace path: /spec/rules/2/host value: element.local +- op: replace + path: /spec/rules/3/host + value: gitea.local diff --git a/deploy/dev/kustomization.yaml b/deploy/dev/kustomization.yaml index d25cd8a..878e4f3 100644 --- a/deploy/dev/kustomization.yaml +++ b/deploy/dev/kustomization.yaml @@ -3,6 +3,7 @@ namePrefix: dev- resources: - ../base - svc-db.yaml +- cm-ingress-nginx-tcp-services.yaml helmCharts: - name: postgresql @@ -47,6 +48,13 @@ secretGenerator: - database-name=invidious - database-user=kemal - database-password=testme +- name: gitea-config + literals: + - admin-username=rob + - admin-password=testme + - admin-email=mail@localhost + files: + - config.ini=gitea-config.ini patches: # Patch the metrics-server to not require TLS in dev cluster. diff --git a/deploy/prod/cm-ingress-nginx-tcp-services.yaml b/deploy/prod/cm-ingress-nginx-tcp-services.yaml new file mode 100644 index 0000000..3ca1a26 --- /dev/null +++ b/deploy/prod/cm-ingress-nginx-tcp-services.yaml @@ -0,0 +1,8 @@ +# configMapGenerator does not accept integer keys in the current version of +# Kustomize. See: https://github.com/kubernetes-sigs/kustomize/issues/3446 +apiVersion: v1 +kind: ConfigMap +metadata: + name: ingress-nginx-tcp-services +data: + "22": "default/prod-gitea:ssh" diff --git a/deploy/prod/deploy-ingress-nginx.yaml b/deploy/prod/deploy-ingress-nginx.yaml index ff9214d..b117dfa 100644 --- a/deploy/prod/deploy-ingress-nginx.yaml +++ b/deploy/prod/deploy-ingress-nginx.yaml @@ -5,6 +5,9 @@ - op: replace path: /spec/template/spec/containers/0/args/5 value: "--configmap=$(POD_NAMESPACE)/prod-ingress-nginx-controller" +- op: add + path: /spec/template/spec/containers/0/args/- + value: "--tcp-services-configmap=$(POD_NAMESPACE)/prod-ingress-nginx-tcp-services" - op: replace path: /spec/template/spec/volumes/0/secret/secretName value: prod-ingress-nginx-admission diff --git a/deploy/prod/ingress.yaml b/deploy/prod/ingress.yaml index 1d9e206..f542f47 100644 --- a/deploy/prod/ingress.yaml +++ b/deploy/prod/ingress.yaml @@ -12,6 +12,7 @@ - grafana.netflux.io - tube.netflux.io - element.netflux.io + - git.netflux.io secretName: prod-ingress-tls - op: replace path: /spec/rules/0/host @@ -22,3 +23,6 @@ - op: replace path: /spec/rules/2/host value: element.netflux.io +- op: replace + path: /spec/rules/3/host + value: git.netflux.io diff --git a/deploy/prod/kustomization.yaml b/deploy/prod/kustomization.yaml index 5de27ff..ef64b1a 100644 --- a/deploy/prod/kustomization.yaml +++ b/deploy/prod/kustomization.yaml @@ -3,6 +3,7 @@ resources: - ../base - svc-db.yaml - svc-netflux.yaml +- cm-ingress-nginx-tcp-services.yaml configMapGenerator: - name: prometheus-server @@ -39,6 +40,12 @@ secretGenerator: files: - database-url=secrets/invidious-database-url - database-password=secrets/invidious-database-password +- name: gitea-config + files: + - admin-username=secrets/gitea-admin-username + - admin-password=secrets/gitea-admin-password + - admin-email=secrets/gitea-admin-email + - config.ini=secrets/gitea-config.ini patches: # Patch the ingress-nginx deployment to allow it to use a service with a diff --git a/deploy/prod/secrets/README.md b/deploy/prod/secrets/README.md index 769d456..b67fd68 100644 --- a/deploy/prod/secrets/README.md +++ b/deploy/prod/secrets/README.md @@ -13,3 +13,11 @@ The credentials used to create the Grafana admin user. See 1password. ### invidious-database-url, invidious-database-password The credentials for the invidious database. See ansible-vault. + +### gitea-admin-username, gitea-admin-email, gitea-admin-password + +The credentials for the Gitea admin user. See 1password. + +### gitea-config.ini + +The full Gitea app.ini file.