diff --git a/.gitea/workflows/vault.yaml b/.gitea/workflows/vault.yaml new file mode 100644 index 0000000..9efc824 --- /dev/null +++ b/.gitea/workflows/vault.yaml @@ -0,0 +1,58 @@ +--- +# template source: https://github.com/bretfisher/docker-build-workflow/blob/main/templates/call-docker-build.yaml +name: Hashicorp Vault + +on: #[push,pull_request] + push: &vaultPaths + paths: + - 'iac/*.tf' + pull_request: *vaultPaths + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +.vault_step: &vault_step + name: read vault secret + uses: https://gitea.arcodange.duckdns.org/arcodange-org/vault-action.git@main + id: vault-secrets + with: + url: https://vault.arcodange.duckdns.org + jwtGiteaOIDC: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }} + role: gitea_cicd_webapp + method: jwt + path: gitea_jwt + secrets: | + kvv1/google/credentials credentials | GOOGLE_BACKEND_CREDENTIALS ; + kvv1/gitea/tofu_module_reader ssh_private_key | TERRAFORM_SSH_KEY ; + +jobs: + gitea_vault_auth: + name: Auth with gitea for vault + runs-on: ubuntu-latest + outputs: + gitea_vault_jwt: ${{steps.gitea_vault_jwt.outputs.id_token}} + steps: + + - name: Auth with gitea for vault + id: gitea_vault_jwt + run: | + echo -n "${{ secrets.vault_oauth__sh_b64 }}" | base64 -d | bash + + tofu: + name: Tofu - Vault + needs: + - gitea_vault_auth + runs-on: ubuntu-latest + env: + OPENTOFU_VERSION: 1.8.2 + TERRAFORM_VAULT_AUTH_JWT: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }} + steps: + - *vault_step + - uses: actions/checkout@v4 + - name: terraform apply + uses: dflook/terraform-apply@v1 + with: + path: iac + auto_approve: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..594d5de --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.terraform* +.DS_Store +.vscode \ No newline at end of file diff --git a/chart/.helmignore b/chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/chart/.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/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 0000000..0c38928 --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,25 @@ +apiVersion: v2 +name: erp +description: A Helm chart for Kubernetes + +# 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. +#https://github.com/Dolibarr/dolibarr-docker +appVersion: "20.0.1" diff --git a/chart/scripts/custom_entrypoint.sh b/chart/scripts/custom_entrypoint.sh new file mode 100644 index 0000000..2d4af84 --- /dev/null +++ b/chart/scripts/custom_entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +# Fichier original du script Docker +DOCKER_RUN_SCRIPT="/usr/local/bin/docker-run.sh" +APACHE_CONF="/etc/apache2/sites-enabled/000-default.conf" + +# 1. Remplacement de la commande MySQL par PSQL dans docker-run.sh +if [[ ${DOLI_DB_TYPE} == "pgsql" ]]; then + sed -i 's/mysql -u \${DOLI_DB_USER} -p\${DOLI_DB_PASSWORD} -h \${DOLI_DB_HOST} -P \${DOLI_DB_HOST_PORT} \${DOLI_DB_NAME} < \${file}/PGPASSWORD="\${DOLI_DB_PASSWORD}" psql -U "\${DOLI_DB_USER}" -h "\${DOLI_DB_HOST}" -p "\${DOLI_DB_HOST_PORT}" -d "\${DOLI_DB_NAME}" < \${file}/' "$DOCKER_RUN_SCRIPT" +fi + +# 2. Mise à jour de la configuration Apache pour définir dynamiquement ServerName +if [ -n "$DOLI_URL_ROOT" ]; then + # Supprimer le préfixe "https://" ou "http://" si présent + SERVER_NAME=$(echo "$DOLI_URL_ROOT" | sed 's|https\?://||') + + # Utiliser le nom de domaine extrait comme ServerName + sed -i "s|#ServerName www.example.com|ServerName $SERVER_NAME|" "$APACHE_CONF" + echo 'ServerName $SERVER_NAME' >> /etc/apache2/apache2.conf +else + echo "Avertissement: DOLI_URL_ROOT non défini, aucun changement de ServerName effectué." +fi + +# 3. Exécution du script d'origine avec les arguments passés au wrapper +exec "$DOCKER_RUN_SCRIPT" "$@" diff --git a/chart/scripts/update_conf_db_credentials.sh b/chart/scripts/update_conf_db_credentials.sh new file mode 100644 index 0000000..790ce66 --- /dev/null +++ b/chart/scripts/update_conf_db_credentials.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# Chemin vers le fichier de configuration +CONFIG_FILE="/var/www/html/conf/conf.php" + +set -eu +# Variables d'environnement pour les secrets +DB_USER="${DOLI_DB_USER}" +DB_PASS="${DOLI_DB_PASSWORD}" + +# Utilisation de sed pour remplacer les valeurs dans conf.php +if [ -f "$CONFIG_FILE" ]; then + echo "mise à jour du fichier $CONFIG_FILE" + sed -i "s/^\(\$dolibarr_main_db_user\s*=\s*\).*/\1'${DB_USER}';/" "$CONFIG_FILE" + sed -i "s/^\(\$dolibarr_main_db_pass\s*=\s*\).*/\1'${DB_PASS}';/" "$CONFIG_FILE" +else + echo "Fichier de configuration non trouvé : $CONFIG_FILE" + exit 1 +fi diff --git a/chart/scripts/update_ownership.sql b/chart/scripts/update_ownership.sql new file mode 100644 index 0000000..1d2b4df --- /dev/null +++ b/chart/scripts/update_ownership.sql @@ -0,0 +1,21 @@ +-- PGPASSWORD=${DOLI_DB_PASSWORD} psql -U ${DOLI_DB_USER} -h ${DOLI_DB_HOST} -p ${DOLI_DB_HOST_PORT} ${DOLI_DB_NAME} +DO $$ +DECLARE + current_schema_owner VARCHAR; -- Propriétaire actuel du schéma public +BEGIN + + -- Obtenir le nom du propriétaire actuel du schéma 'public' + SELECT tableowner INTO current_schema_owner + FROM pg_tables + WHERE schemaname = 'public' + LIMIT 1; + + -- Si le propriétaire actuel est différent de erp_role + IF current_schema_owner <> 'erp_role' THEN + -- Construire et exécuter la requête REASSIGN OWNED BY + EXECUTE format('REASSIGN OWNED BY %I TO %I', current_schema_owner, 'erp_role'); + RAISE NOTICE 'Ownership of all objects in schema "public" has been reassigned from % to %', current_schema_owner, 'erp_role'; + ELSE + RAISE NOTICE 'No change needed; the owner of schema "public" is already %', 'erp_role'; + END IF; +END $$; diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt new file mode 100644 index 0000000..0ad355a --- /dev/null +++ b/chart/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "erp.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "erp.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "erp.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "erp.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl new file mode 100644 index 0000000..dc3556b --- /dev/null +++ b/chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "erp.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 "erp.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 "erp.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "erp.labels" -}} +helm.sh/chart: {{ include "erp.chart" . }} +{{ include "erp.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "erp.selectorLabels" -}} +app.kubernetes.io/name: {{ include "erp.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "erp.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "erp.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/chart/templates/config.yaml b/chart/templates/config.yaml new file mode 100644 index 0000000..b5bf7e5 --- /dev/null +++ b/chart/templates/config.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "erp.name" . }}-config + namespace: {{ .Release.Namespace }} +# Doc: https://hub.docker.com/r/dolibarr/dolibarr https://github.com/Dolibarr/dolibarr-docker +data: + DOLI_DB_TYPE: pgsql + DOLI_DB_HOST: pgbouncer.tools + DOLI_DB_HOST_PORT: !!str 5432 + # DOLI_DB_USER: root + # DOLI_DB_PASSWORD: root + DOLI_DB_NAME: erp + DOLI_URL_ROOT: 'https://erp.arcodange.duckdns.org' + # DOLI_ADMIN_LOGIN: 'admin' + # DOLI_ADMIN_PASSWORD: 'admininitialpassword' + DOLI_ENABLE_MODULES: Societe,Facture + DOLI_COMPANY_NAME: Arcodange + DOLI_COMPANY_COUNTRYCODE: FR + PHP_INI_DATE_TIMEZONE: Europe/Paris + DOLI_AUTH: dolibarr + DOLI_CRON: !!str 0 + # DOLI_INSTANCE_UNIQUE_ID: \ No newline at end of file diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 0000000..a170225 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,111 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "erp.fullname" . }} + labels: + {{- include "erp.labels" . | nindent 4 }} + annotations: + configmap-hash: {{ include (print .Template.BasePath "/config.yaml") . | sha256sum }} + configmap2-hash: {{ include (print .Template.BasePath "/scripts-config.yaml") . | sha256sum }} + configmap3-hash: {{ include (print .Template.BasePath "/script-entrypoint-config.yaml") . | sha256sum }} +spec: + revisionHistoryLimit: 5 + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "erp.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "erp.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "erp.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + command: ["/bin/bash", "/usr/local/bin/custom-entrypoint.sh", "apache2-foreground"] + imagePullPolicy: {{ .Values.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ include "erp.name" . }}-config + - secretRef: + name: secretkv + env: + - name: DOLI_DB_USER + valueFrom: + secretKeyRef: + name: vso-db-credentials + key: username + - name: DOLI_DB_PASSWORD + valueFrom: + secretKeyRef: + name: vso-db-credentials + key: password + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + - name: dolibarr + mountPath: /var/www/documents + subPath: documents + - name: dolibarr + mountPath: /var/www/html/custom + subPath: custom + - name: dolibarr + mountPath: /var/backups + subPath: backups + - name: before-start-scripts + mountPath: /var/www/scripts/before-starting.d + - name: custom-entrypoint-script + mountPath: /usr/local/bin/custom-entrypoint.sh + subPath: custom-entrypoint.sh + volumes: + - name: dolibarr + persistentVolumeClaim: + claimName: {{ include "erp.fullname" . }} + - name: before-start-scripts + configMap: + name: dolibarr-before-start-scripts + - name: custom-entrypoint-script + configMap: + name: dolibarr-custom-entrypoint-script + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/chart/templates/hpa.yaml b/chart/templates/hpa.yaml new file mode 100644 index 0000000..ef7d6ae --- /dev/null +++ b/chart/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "erp.fullname" . }} + labels: + {{- include "erp.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "erp.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml new file mode 100644 index 0000000..3e38883 --- /dev/null +++ b/chart/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "erp.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "erp.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/chart/templates/pvc.yaml b/chart/templates/pvc.yaml new file mode 100644 index 0000000..7242f82 --- /dev/null +++ b/chart/templates/pvc.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ template "erp.fullname" . }} + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/resource-policy: keep + labels: + {{- include "erp.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 50Gi + storageClassName: nfs-client \ No newline at end of file diff --git a/chart/templates/script-entrypoint-config.yaml b/chart/templates/script-entrypoint-config.yaml new file mode 100644 index 0000000..07cbf1b --- /dev/null +++ b/chart/templates/script-entrypoint-config.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dolibarr-custom-entrypoint-script +data: + custom-entrypoint.sh: | + {{- .Files.Get "scripts/custom_entrypoint.sh" | nindent 4 }} diff --git a/chart/templates/scripts-config.yaml b/chart/templates/scripts-config.yaml new file mode 100644 index 0000000..a02bb11 --- /dev/null +++ b/chart/templates/scripts-config.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: dolibarr-before-start-scripts +data: + update_conf_db_credentials.sh: | + {{- .Files.Get "scripts/update_conf_db_credentials.sh" | nindent 4 }} + + update_table_ownership.sql: | + {{- .Files.Get "scripts/update_ownership.sql" | nindent 4 }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 0000000..dd442e4 --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "erp.fullname" . }} + labels: + {{- include "erp.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "erp.selectorLabels" . | nindent 4 }} diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml new file mode 100644 index 0000000..3ebc34b --- /dev/null +++ b/chart/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "erp.serviceAccountName" . }} + labels: + {{- include "erp.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/chart/templates/tests/test-connection.yaml b/chart/templates/tests/test-connection.yaml new file mode 100644 index 0000000..5a27a55 --- /dev/null +++ b/chart/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "erp.fullname" . }}-test-connection" + labels: + {{- include "erp.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "erp.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/chart/templates/vaultauth.yaml b/chart/templates/vaultauth.yaml new file mode 100644 index 0000000..fe2adf0 --- /dev/null +++ b/chart/templates/vaultauth.yaml @@ -0,0 +1,13 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultAuth +metadata: + name: auth + namespace: {{ .Release.Namespace }} +spec: + method: kubernetes + mount: kubernetes + kubernetes: + role: erp + serviceAccount: {{ include "erp.serviceAccountName" . }} + audiences: + - vault \ No newline at end of file diff --git a/chart/templates/vaultdynamicsecret.yaml b/chart/templates/vaultdynamicsecret.yaml new file mode 100644 index 0000000..3087210 --- /dev/null +++ b/chart/templates/vaultdynamicsecret.yaml @@ -0,0 +1,25 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultDynamicSecret +metadata: + name: vso-db + namespace: {{ .Release.Namespace }} +spec: + + # Mount path of the secrets backend + mount: postgres + + # Path to the secret + path: creds/erp + + # Where to store the secrets, VSO will create the secret + destination: + create: true + name: vso-db-credentials + + # Restart these pods when secrets rotated + rolloutRestartTargets: + - kind: Deployment + name: {{ include "erp.fullname" . }} + + # Name of the CRD to authenticate to Vault + vaultAuthRef: auth \ No newline at end of file diff --git a/chart/templates/vaultsecret.yaml b/chart/templates/vaultsecret.yaml new file mode 100644 index 0000000..de37805 --- /dev/null +++ b/chart/templates/vaultsecret.yaml @@ -0,0 +1,24 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: vault-kv-app + namespace: {{ .Release.Namespace }} +spec: + type: kv-v2 + + # mount path + mount: kvv2 + + # path of the secret + path: erp/config + + # dest k8s secret + destination: + name: secretkv + create: true + + # static secret refresh interval + refreshAfter: 24h + + # Name of the CRD to authenticate to Vault + vaultAuthRef: auth \ No newline at end of file diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 0000000..ed0ad29 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,113 @@ +# Default values for erp. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: dolibarr/dolibarr + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: true + className: "" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt + traefik.ingress.kubernetes.io/router.tls.domains.0.main: arcodange.duckdns.org + traefik.ingress.kubernetes.io/router.tls.domains.0.sans: erp.arcodange.duckdns.org + traefik.ingress.kubernetes.io/router.middlewares: localIp@file + hosts: + - host: erp.arcodange.duckdns.org + paths: + - path: / + pathType: Prefix + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# livenessProbe: +# exec: +# command: [timeout, '10', ls, /var/www/] +# initialDelaySeconds: 5 +# periodSeconds: 5 + +# readinessProbe: + # httpGet: + # path: / + # port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/iac/backend.tf b/iac/backend.tf new file mode 100644 index 0000000..e671c9e --- /dev/null +++ b/iac/backend.tf @@ -0,0 +1,6 @@ +terraform { + backend "gcs" { + bucket = "arcodange-tf" + prefix = "erp/main" + } +} \ No newline at end of file diff --git a/iac/main.tf b/iac/main.tf new file mode 100644 index 0000000..e7339d3 --- /dev/null +++ b/iac/main.tf @@ -0,0 +1,32 @@ +locals { + app = { + name = "erp" + product_name = "dolibarr" # unused + } +} + +module "app_roles" { + source = "git::ssh://git@192.168.1.202:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main" + name = local.app.name +} + +resource "random_password" "admin_initial_password" { + length = 32 +} +resource "random_uuid" "dolibarr_id" { # used for encryption as well as when buying modules + lifecycle { + prevent_destroy = true + } +} + +resource "vault_kv_secret_v2" "dolibarr_admin_setup" { + mount = module.app_roles.mount_paths.kvv2 + name = format("%sconfig", module.app_roles.kvv2_path_prefix) + data_json = jsonencode( + { + DOLI_ADMIN_LOGIN = "admin", + DOLI_ADMIN_PASSWORD = random_password.admin_initial_password.result + DOLI_INSTANCE_UNIQUE_ID = random_uuid.dolibarr_id.result + } + ) +} diff --git a/iac/providers.tf b/iac/providers.tf new file mode 100644 index 0000000..3dfc985 --- /dev/null +++ b/iac/providers.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + vault = { + source = "vault" + version = "4.4.0" + } + } +} + +provider vault { + address = "https://vault.arcodange.duckdns.org" + auth_login_jwt { # TERRAFORM_VAULT_AUTH_JWT environment variable + mount = "gitea_jwt" + role = "gitea_cicd_erp" + } +} \ No newline at end of file