From 9059229acb71a72ac44428836b136aa027f9035d Mon Sep 17 00:00:00 2001 From: justusbunsi Date: Wed, 30 Jun 2021 04:09:16 +0800 Subject: [PATCH] Rewrite init script (#178) These changes rewrite the init script to be error aware, informative and have a bit more security awareness. During rewrite several hidden bugs could be identified and fixed, such as: - LDAP configuration options interpreted by the shell before passed to command - Finding multiple ldap ids instead of one during lookup when their names are almost identical e.g. `_my-ldap-auth` and `my-ldap-auth` - Properly filter auth sources by their types to prevent unintended type converting attempts that fail In addition to that the script is a bit cleaner. Some commands do not exist anymore and would cause false-positive errors during script execution. Helps for: #149 Reviewed-on: https://gitea.com/gitea/helm-chart/pulls/178 Reviewed-by: luhahn Reviewed-by: techknowlogick Co-authored-by: justusbunsi Co-committed-by: justusbunsi --- README.md | 8 ++ templates/_helpers.tpl | 7 +- templates/gitea/init.yaml | 121 ++++++++++++++++++++++--------- templates/gitea/statefulset.yaml | 35 ++++++++- 4 files changed, 130 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index b709d45..2f33f07 100644 --- a/README.md +++ b/README.md @@ -356,6 +356,14 @@ gitea: existingSecret: gitea-ldap-secret ``` +:warning: Some options are just flags and therefore don't any values. If they are defined in `gitea.ldap` configuration, they will be passed to the gitea cli without any value. Affected options: + +- notActive | not-active +- skipTlsVerify | skip-tls-verify +- allowDeactivateAll | allow-deactivate-all +- synchronizeUsers | synchronize-users +- attributesInBind | attributes-in-bind + ### OAuth2 Settings Like the admin user, OAuth2 settings can be updated and disabled but not deleted. Deleting OAuth2 settings has to be done in the ui. diff --git a/templates/_helpers.tpl b/templates/_helpers.tpl index 8cafd79..bd4339d 100644 --- a/templates/_helpers.tpl +++ b/templates/_helpers.tpl @@ -116,6 +116,7 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- $_ := set .Values.gitea.ldap "bindPassword" "" -}} {{- end -}} +{{- $flags := list "not-active" "skip-tls-verify" "allow-deactivate-all" "synchronize-users" "attributes-in-bind" -}} {{- range $key, $val := .Values.gitea.ldap -}} {{- if and (ne $key "enabled") (ne $key "existingSecret") -}} {{- if eq ($key | kebabcase) "bind-dn" -}} @@ -124,8 +125,10 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- printf "--%s %s " ($key | kebabcase) ("${GITEA_LDAP_PASSWORD}" | quote ) -}} {{- else if eq $key "port" -}} {{- printf "--%s %d " ($key | kebabcase) ($val | int) -}} +{{- else if (has ($key | kebabcase) $flags) -}} +{{- printf "--%s " ($key | kebabcase) -}} {{- else -}} -{{- printf "--%s %s " ($key | kebabcase) ($val | quote) -}} +{{- printf "--%s %s " ($key | kebabcase) ($val | squote) -}} {{- end -}} {{- end -}} {{- end -}} @@ -134,7 +137,7 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- define "gitea.oauth_settings" -}} {{- range $key, $val := .Values.gitea.oauth -}} {{- if ne $key "enabled" -}} -{{- printf "--%s %s " ($key | kebabcase) ($val | quote) -}} +{{- printf "--%s %s " ($key | kebabcase) ($val | squote) -}} {{- end -}} {{- end -}} {{- end -}} diff --git a/templates/gitea/init.yaml b/templates/gitea/init.yaml index b44d130..c77c2d2 100644 --- a/templates/gitea/init.yaml +++ b/templates/gitea/init.yaml @@ -6,8 +6,11 @@ metadata: {{- include "gitea.labels" . | nindent 4 }} type: Opaque stringData: - init_gitea.sh: |- - #!/bin/bash + init_directory_structure.sh: |- + #!/usr/bin/env bash + + set -euo pipefail + {{- if .Values.initPreScript }} # BEGIN: initPreScript {{- with .Values.initPreScript -}} @@ -16,6 +19,8 @@ stringData: # END: initPreScript {{- end }} + set -x + {{- if not .Values.image.rootless }} chown 1000:1000 /data {{- end }} @@ -31,44 +36,90 @@ stringData: # Copy config file to writable volume cp /etc/gitea/conf/app.ini /data/gitea/conf/app.ini chmod a+rwx /data/gitea/conf/app.ini + configure_gitea.sh: |- + #!/usr/bin/env bash + + set -euo pipefail + {{- if include "db.servicename" . }} - # Wait for database to become avialble - nc -v -w2 -z {{ include "db.servicename" . }} {{ include "db.port" . }} && \ + # Connection retry inspired by https://gist.github.com/dublx/e99ea94858c07d2ca6de + function test_db_connection() { + local RETRY=0 + local MAX=30 + + echo 'Wait for database to become avialable...' + until [ "${RETRY}" -ge "${MAX}" ]; do + nc -vz -w2 {{ include "db.servicename" . }} {{ include "db.port" . }} && break + RETRY=$[${RETRY}+1] + echo "...not ready yet (${RETRY}/${MAX})" + done + + if [ "${RETRY}" -ge "${MAX}" ]; then + echo "Database not reachable after '${MAX}' attempts!" + exit 1 + fi + } + + test_db_connection {{- end }} - {{- if not .Values.image.rootless }} - su git -c ' \ - {{- end }} - set -x; \ - gitea migrate; \ + + echo '==== BEGIN GITEA CONFIGURATION ====' + + gitea migrate + {{- if or .Values.gitea.admin.existingSecret (and .Values.gitea.admin.username .Values.gitea.admin.password) }} - gitea admin create-user --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" --email {{ .Values.gitea.admin.email }} --admin --must-change-password=false \ - || \ - gitea admin change-password --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" \ - || \ - gitea admin user create --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}" --email {{ .Values.gitea.admin.email }} --admin --must-change-password=false \ - || \ - gitea admin user change-password --username "${GITEA_ADMIN_USERNAME}" --password "${GITEA_ADMIN_PASSWORD}"; \ + 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 {{ .Values.gitea.admin.email | quote }} --must-change-password=false + echo '...created.' + else + echo "Admin account '${GITEA_ADMIN_USERNAME}' already exist. 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 {{- end }} + {{- if .Values.gitea.ldap.enabled }} - gitea admin auth add-ldap \ - {{- include "gitea.ldap_settings" . | nindent 6 }} \ - || \ - ( \ - export GITEA_AUTH_ID=$(gitea admin auth list | grep {{ .Values.gitea.ldap.name | quote }} | awk -F " " "{print \$1}"); \ - gitea admin auth update-ldap --id ${GITEA_AUTH_ID} \ - {{- include "gitea.ldap_settings" . | nindent 6 }} \ - ) \ + function configure_ldap() { + local LDAP_NAME={{ (printf "%s" .Values.gitea.ldap.name) | squote }} + local GITEA_AUTH_ID=$(gitea admin auth list --vertical-bars | grep -E "\|${LDAP_NAME}\s+\|" | grep -iE '\|LDAP \(via BindDN\)\s+\|' | awk -F " " "{print \$1}") + + if [[ -z "${GITEA_AUTH_ID}" ]]; then + echo "No ldap configuration found with name '${LDAP_NAME}'. Installing it now..." + gitea admin auth add-ldap {{- include "gitea.ldap_settings" . | indent 1 }} + echo '...installed.' + else + echo "Existing ldap configuration with name '${LDAP_NAME}': '${GITEA_AUTH_ID}'. Running update to sync settings..." + gitea admin auth update-ldap --id "${GITEA_AUTH_ID}" {{- include "gitea.ldap_settings" . | indent 1 }} + echo '...sync settings done.' + fi + } + + configure_ldap {{- end }} + {{- if .Values.gitea.oauth.enabled }} - gitea admin auth add-oauth \ - {{- include "gitea.oauth_settings" . | nindent 6 }} \ - || \ - ( \ - export GITEA_AUTH_ID=$(gitea admin auth list | grep {{ .Values.gitea.oauth.name | quote }} | awk -F " " "{print \$1}"); \ - gitea admin auth update-oauth --id ${GITEA_AUTH_ID} \ - {{- include "gitea.oauth_settings" . | nindent 6 }} \ - ) \ - {{- end }} - {{- if not .Values.image.rootless }} - ' + function configure_oauth() { + local OAUTH_NAME={{ (printf "%s" .Values.gitea.oauth.name) | squote }} + local AUTH_ID=$(gitea admin auth list --vertical-bars | grep -E "\|${OAUTH_NAME}\s+\|" | grep -iE '\|OAuth2\s+\|' | awk -F " " "{print \$1}") + + if [[ -z "${AUTH_ID}" ]]; then + echo "No oauth configuration found with name '${OAUTH_NAME}'. Installing it now..." + gitea admin auth add-oauth {{- include "gitea.oauth_settings" . | indent 1 }} + echo '...installed.' + else + echo "Existing oauth configuration with name '${OAUTH_NAME}': '${AUTH_ID}'. Running update to sync settings..." + gitea admin auth update-oauth --id "${AUTH_ID}" {{- include "gitea.oauth_settings" . | indent 1 }} + echo '...sync settings done.' + fi + } + + configure_oauth {{- end }} + + echo '==== END GITEA CONFIGURATION ====' diff --git a/templates/gitea/statefulset.yaml b/templates/gitea/statefulset.yaml index 2a53dd9..bf43597 100644 --- a/templates/gitea/statefulset.yaml +++ b/templates/gitea/statefulset.yaml @@ -38,9 +38,38 @@ spec: securityContext: fsGroup: 1000 initContainers: - - name: init + - name: init-directories image: "{{ include "gitea.image" . }}" - command: ["/usr/sbin/init_gitea.sh"] + command: ["/usr/sbin/init_directory_structure.sh"] + env: + - name: GITEA_APP_INI + value: /data/gitea/conf/app.ini + - name: GITEA_CUSTOM + value: /data/gitea + - name: GITEA_WORK_DIR + value: /data + - name: GITEA_TEMP + value: /tmp/gitea + {{- if .Values.statefulset.env }} + {{- toYaml .Values.statefulset.env | nindent 12 }} + {{- end }} + volumeMounts: + - name: init + mountPath: /usr/sbin + - name: temp + mountPath: /tmp + - name: config + mountPath: /etc/gitea/conf + - name: data + mountPath: /data + {{- if .Values.extraVolumeMounts }} + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} + {{- end }} + - name: configure-gitea + image: "{{ include "gitea.image" . }}" + command: ["/usr/sbin/configure_gitea.sh"] + securityContext: + runAsUser: 1000 env: - name: GITEA_APP_INI value: /data/gitea/conf/app.ini @@ -92,8 +121,6 @@ spec: mountPath: /usr/sbin - name: temp mountPath: /tmp - - name: config - mountPath: /etc/gitea/conf - name: data mountPath: /data {{- if .Values.extraVolumeMounts }}