diff --git a/Taskfile.yaml b/Taskfile.yaml index 80b6104f..29adbfe3 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -7,6 +7,8 @@ env: CHART_DIR_PATH_INIT: "charts/init" CHART_DIR_PATH_ARGOCD: "charts/argocd" CHART_DIR_PATH_CNI: "charts/cilium" + KUBESEAL_PRIVATE_KEY: "./private.key" + KUBESEAL_PUBLIC_CERT: "./public.cert" tasks: @@ -79,3 +81,86 @@ tasks: REVISION: sh: git rev-parse --abbrev-ref HEAD + seal: + desc: "Kubeseal all *.unsealed.yaml secrets into and over adjacent *.sealed.yaml files." + cmds: + - task: generic-seal + - task: env-specific-seal + vars: + ENV: "dev" + - task: env-specific-seal + vars: + ENV: "stg" + - task: env-specific-seal + vars: + ENV: "prd" + + env-specific-seal: + desc: "Kubeseal environment specific secrets into and over adjacent *.sealed.yaml files." + silent: true + requires: + vars: + - ENV + sources: + - "{{ .CHARTS_DIR }}/**/*.{{ .ENV }}.unsealed.yaml" + generates: + - "{{ .CHARTS_DIR }}/**/*.{{ .ENV }}.sealed.yaml" + cmds: + - for: sources + cmd: | + echo "Sealing - {{ .ITEM }} with {{ .KUBESEAL_PUBLIC_CERT }}" + condition='\{\{- if eq .Values.environment.mode "{{ .ENV }}" \}\}' + outfile=$(sed -e 's/.unsealed.yaml/.sealed.yaml/' <<< "{{ .ITEM }}") + if [ -s "{{ .ITEM }}" ]; then + echo $condition > $outfile + cat {{ .ITEM }} | kubeseal --cert {{ .KUBESEAL_PUBLIC_CERT }} -o yaml >> $outfile + echo "\{\{- end \}\}" >> $outfile + sed -i 's/\\{/{/g' $outfile + sed -i 's/\\}/}/g' $outfile + echo "Sealed - $outfile" + else + echo "WARNING: no content in {{ .ITEM }}. Skipping." + fi + method: none + + generic-seal: + desc: "Kubeseal all *.unsealed.yaml secrets into and over adjacent *.sealed.yaml files." + silent: true + sources: + - "{{ .CHARTS_DIR }}/**/*.unsealed.yaml" + generates: + - "{{ .CHARTS_DIR }}/**/*.sealed.yaml" + cmds: + - for: sources + cmd: | + echo "Sealing - {{ .ITEM }} with {{ .KUBESEAL_PUBLIC_CERT }}" + outfile=$(sed -e 's/.unsealed.yaml/.sealed.yaml/' <<< "{{ .ITEM }}") + if [ -s "{{ .ITEM }}" ]; then + cat {{ .ITEM }} | kubeseal --cert {{ .KUBESEAL_PUBLIC_CERT }} -o yaml > $outfile + echo "Sealed - $outfile" + else + echo "WARNING: no content in {{ .ITEM }}. Skipping." + fi + method: none + + unseal: + desc: "Un-Kubeseal all *.sealed.yaml secrets into and over adjacent *.unsealed.yaml files." + silent: true + sources: + - "{{ .CHARTS_DIR }}/**/*.sealed.yaml" + generates: + - "{{ .CHARTS_DIR }}/**/*.unsealed.yaml" + cmds: + - for: sources + cmd: | + echo "Unsealing - {{ .ITEM }} with {{ .KUBESEAL_PRIVATE_KEY }}" + outfile=$(sed -e 's/.sealed.yaml/.unsealed.yaml/' <<< "{{ .ITEM }}") + if [[ ! $(cat {{ .ITEM }} | yq ' .spec.template.metadata.labels."cromwell-tools.co.uk/binarysecret"') = 'true' ]]; then + cat {{ .ITEM }} | sed 's/.*{-.*//' | kubeseal --recovery-unseal --recovery-private-key {{ .KUBESEAL_PRIVATE_KEY }} -o yaml | yq '.data |= map_values(@base64d) | .stringData = .data | del(.data) | del(.metadata.ownerReferences)' > $outfile + else + cat {{ .ITEM }} | kubeseal --recovery-unseal --recovery-private-key {{ .KUBESEAL_PRIVATE_KEY }} -o yaml > $outfile + echo "WARNING: secret is binary. Skipping base64 decode." + fi + echo "Unsealed - $outfile" + method: none + diff --git a/charts/erpnext/Chart.yaml b/charts/erpnext/Chart.yaml index a1a34336..98e93839 100644 --- a/charts/erpnext/Chart.yaml +++ b/charts/erpnext/Chart.yaml @@ -11,7 +11,7 @@ dependencies: version: "7.0.168" repository: "https://helm.erpnext.com" condition: erpnext.enabled -- name: postgresql - version: "20.4.1" - repository: "oci://registry-1.docker.io/bitnamicharts" - condition: postgresql.enabled +# - name: postgresql +# version: "20.4.1" +# repository: "oci://registry-1.docker.io/bitnamicharts" +# condition: postgresql.enabled diff --git a/charts/erpnext/README.md b/charts/erpnext/README.md index 057253db..0f82980a 100644 --- a/charts/erpnext/README.md +++ b/charts/erpnext/README.md @@ -9,7 +9,6 @@ A Helm chart for Kubernetes | Repository | Name | Version | |------------|------|---------| | https://helm.erpnext.com | erpnext | 7.0.168 | -| oci://registry-1.docker.io/bitnamicharts | postgresql | 20.4.1 | ## Values @@ -23,28 +22,35 @@ A Helm chart for Kubernetes | environment.mode | string | `"production"` | | | environment.name | string | `"unknown"` | | | environment.revision | string | `"main"` | | -| erpnext.dbHost | string | `"1.2.3.4"` | | -| erpnext.dbPort | string | `"3306"` | | -| erpnext.dbRootPassword | string | `"secret"` | | -| erpnext.dbRootUser | string | `"admin"` | | +| erpnext.dbExistingSecret | string | `"psql-erpnext"` | | +| erpnext.dbExistingSecretPasswordKey | string | `"password"` | | +| erpnext.dbHost | string | `"psql"` | | +| erpnext.dbPort | int | `5432` | | +| erpnext.dbRootUser | string | `"erpnext"` | | +| erpnext.enabled | bool | `false` | | | erpnext.jobs.backup.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.backup.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.backup.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | | erpnext.jobs.configure.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.configure.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.configure.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | +| erpnext.jobs.createSite.adminExistingSecretKey | string | `"password"` | | +| erpnext.jobs.createSite.adminExistingsecret | string | `"erpnext"` | | | erpnext.jobs.createSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.createSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.createSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | +| erpnext.jobs.createSite.siteName | string | `"erp.deepcypher.me"` | | | erpnext.jobs.custom.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.custom.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.custom.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | | erpnext.jobs.dropSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.dropSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.dropSite.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | +| erpnext.jobs.dropSite.siteName | string | `"erp.deepcypher.me"` | | | erpnext.jobs.migrate.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.migrate.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.migrate.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | +| erpnext.jobs.migrate.siteName | string | `"erp.deepcypher.me"` | | | erpnext.jobs.volumePermissions.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.jobs.volumePermissions.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.jobs.volumePermissions.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | @@ -69,6 +75,7 @@ A Helm chart for Kubernetes | erpnext.nginx.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.nginx.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | | erpnext.persistence.worker.accessModes[0] | string | `"ReadWriteMany"` | | +| erpnext.persistence.worker.size | string | `"8Gi"` | | | erpnext.persistence.worker.storageClass | string | `"ceph-filesystem"` | | | erpnext.socketio.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.socketio.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | @@ -76,6 +83,4 @@ A Helm chart for Kubernetes | erpnext.worker.default.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key | string | `"kubernetes.io/arch"` | | | erpnext.worker.default.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].operator | string | `"In"` | | | erpnext.worker.default.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0] | string | `"amd64"` | | -| postgresql.auth.existingSecret | string | `"postgres"` | | -| postgresql.enabled | bool | `true` | | diff --git a/charts/erpnext/templates/cnpg/cluster.yaml b/charts/erpnext/templates/cnpg/cluster.yaml new file mode 100644 index 00000000..e9195ea1 --- /dev/null +++ b/charts/erpnext/templates/cnpg/cluster.yaml @@ -0,0 +1,25 @@ +{{- if .Values.psql.enabled }} +# https://blog.palark.com/cloudnativepg-and-other-kubernetes-operators-for-postgresql/ +# https://cloudnative-pg.io/documentation/current/rolling_update/#automated-updates-unsupervised +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: psql +spec: + instances: 3 + imageName: ghcr.io/cloudnative-pg/postgresql:16.4 + primaryUpdateStrategy: unsupervised # enables automated updates + primaryUpdateMethod: switchover # how to handle updates switch to new or restart old primary + storage: + size: 16Gi + + bootstrap: + initdb: + database: erpnext + owner: erpnext + secret: + name: psql-erpnext + dataChecksums: true + #encoding: 'LATIN1' + encoding: 'UTF8' +{{- end }} diff --git a/charts/erpnext/templates/cnpg/erpnext-psql.sealed.yaml b/charts/erpnext/templates/cnpg/erpnext-psql.sealed.yaml new file mode 100644 index 00000000..5fc09ab1 --- /dev/null +++ b/charts/erpnext/templates/cnpg/erpnext-psql.sealed.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: psql-erpnext + namespace: erpnext +spec: + encryptedData: + password: AgB8Lk4KMrJoXb/tUhphH6sPsIOTpXUCyWX+5Rj5ifu0Z7h+/9wzCxtVk3jj+NpNK7KYUcioACBJ6UDhXBn5LeaCxhLxghX6hdguArMDDBy+FqDa/jKPhYhOjt4LaM+pB/UTSX+p01Pae+1qhhMRymhMVfJ/1lecpj42VIkEJhcaJYKh2Wn7dTIkwZon1FD3M5kEwpZMyZ/nLsVsWHr0lM0KVMY0YYU9/hpQGJdgw/lOaXVu2njjAJmxfpLIi7T2klg8oIgl1BCbX1zE+GFEzyxDAbHhbJzw2Xg9VVFFl3TTef1PzSTxAKcTa33ANbJfLMS5OV+bX3cOtfVfrc9Z7GDBjg/Pum00JVUrR+rXOxAAMEAjq84Dp+DtmQPA5Geu/h9uf6IeStI5xHR8lPOrICdNo+j85XGiuFI8cgyBNT9UGMmhxPknftfILMFI22KMU8jj9VEkRRN0uA7vnCDUB1iaWw0ghP9OsO6PCK6++FKOKl9NW48a1P8ZoaJDkyJfqUe088RfhrJ7wK9D+EQSV/h0R8wRIH5EA2lfCvnCzbEHCFTu46quAGo3/6ocQbbwlLki3U6nnBcA90qj8OrkmLA87J41FrNrdnRLntxquXPAIK137pD2XRIjRgLMcYe3psfrxKkh+M5QMVlr5zxUdbx6ikdCXA1j2ztQUmFdxCfy5som2wJ5b1YhffRUoF1nMeP8NcZxXh6WqPcoM+cUJl5x7vE1CixNO/pRilR+UNhDya456+NgoCglcjig1aRwtDgxCNSvNdUCxdo1Yt5Emdzk + username: AgCb7rnvHYbVEn7tgjYf9+iMdkaCIpZb53Y6KMZy++G6IlBGx8ZaGVKdEUUjQwEDZx/94A8MsPZAstXn91mRxRZwvV0uS2nO5p/FEHGUu9ef1xbgrXmjgxmK6Djew+LA+nZipPbp6UbGyuRcFgdZwySXaN+ft8Fdjy/4Lrc0buOaYXSGHioy0D5JylLE4sfAUeCQyhbhC3eTuL+BQyv/sYggDSx+J8HYxWMMGr3qZUfKwv8Z2l78PMGjE0NHOdXGOVGr5lDgD6j4unJrYU2mvC4QX9ItC5AcPsesus/Y1ZYwFUn72rAuw/pM2t7O1DfbvQ14a+DD78LI1KBa6CLFjYFcVBZ9s1q9b6YLgHvMP+P/RCrijJ55R8bg/aiQ2VVn5hmCNEVOQlnVs7uNqtxnb/0fQcLZkwwpZ5oTcYrYQrX08aPj1QA0mV8o4s57D5UQzaS5YGDs8pdrPq1Xw9112sTuVOaE5EYyWBNgphgt4Ayzs5SySt1NeCs+XOJ8USBaDtWiZHVF4yc/UtMjqPVdHY3oJTpG+F1e0b/+pzNcZi1fUQ7U9k/rD1TOPee3vQr2vJPGictajUgtANinVgWaoJzX+C9h9E1McyNIomipWcf6hEI80VFADz16bhhIa+t6uVCCGGEE1tK05O52GhZGYA4OfH6PTcI8QlNl/QhSW6Ozk5DHgiQWfq29z/JqVCaz9U3g6LjpOI16 + template: + metadata: + creationTimestamp: null + name: psql-erpnext + namespace: erpnext + type: kubernetes.io/basic-auth diff --git a/charts/erpnext/templates/erpnext.sealed.yaml b/charts/erpnext/templates/erpnext.sealed.yaml new file mode 100644 index 00000000..01981eee --- /dev/null +++ b/charts/erpnext/templates/erpnext.sealed.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: erpnext + namespace: erpnext +spec: + encryptedData: + password: AgAPvUsNixxeSZ+wklcUiziMAsX2L1mNZ3EhBJCWLtI6/im6up7G70fp9oIaEahrZ8uQ4hyOUKnqZxoulphVEyi691oSxajL6+xWS4j97XOBPNscIJK/7SAZJdkfV6ntL3RNQpZGMml+oQzQoQFdHabPoxfvcU3GszakVTIFO8KZ4zsfo7ms/+kMRSVv6e7IPw2TW3ePCdMOc8HSVfhau9N1th+/Xp0smW9JXyMoQmwZeV+iV0QTUt+thAzhCniXyt+gkng0bVfRSa1a4aPMkttOhd+UVm3mrBZMDXYDXMtpPsVPDo39mY6/n0Y4LxCpKYCs5R0DRYq5BsZgit4lPlG5yzcbhWyML0/m3sF36dk30PqubnYGDfbovXgBQ+5+37QSyEEcoA0YyxlYHVrmwJYwZ09PLy6QzIX1knigOLgm8LKIhf4CtTSqOs/Iyto0DmFMGgk13tmRXx4VROxY4KEVEIrS64ObroD/yeV2TPScxiWgMxL6SD8GD7hEG0umHyRr99+M0P6QYw/GJ2OT1wJ2JgvxtLjUq5m/DWEFoqW7aqTNwTgqcI6GVck667F5EmbHhl468xJD63jQ3vRgrad8HdaUzl4ePyY8pVG4i7UmsChsQDgu2jciYWZ90RQHFgWEpmgHIPs48gmdcl9bXV8UbrMZAZ3PqqyU0+iMG1ay3p9bwpW1sp9v2cR449fJlzHUZfrHnC79jh945m1JyhLCiPfNyyLUUw73041s6VY933nhnIGjdVDtXeyupxgkkFqjtPUbQmPyjfir24k907ay + username: AgBPCHrHaiyNDrGqEahxHj7w9ycrrNs+I6AtDpTrJJ3bAT2BHPfzir35zuAid4Ex+NYnWGr+8y2i1L6Ak8neAyjIiSKUW6pf8QbcHjd7ABK/Za0o9FyKrn/iIaQ3D+28k9svukOr/OM/5g5EL3r1B+rR9hP3Da3jb8MXtdoUDfqdTIvmKvrlc1Tsnxf41H3arV1tsDOa+EVLISdaFx5IU071LzSz3v48jYRCjQ+Asg206D8akgPSdQnlUJrqnqOMRAw6StiW52K+vyksLDGtc3P8wDQoklQJaVAj9H4yLwkIJ21MubIYPhjXSKULCyTmPOACYg+0C2LhkMjwyVP9ocE10A9f2peCk4xGWdr+dR0LxlhUZq+BowIrE/6ts9ugrlsLECHp4b9W3x5M1CoN4fyL9vV6Cr4Beu3c/XIsplqyE1eGopF5vgp1ANd0M1oW+T2CTDgcjx2P1RVyDDJj/ilkiLFj6TcMxpUBzU3FF8AWxIR5EC4+oERUwjSzH7UYdT+nxkg4IaD3T+UEfp1jT1WcD1CjZMEpDnLcmtarL5NoOmnR87zDYH3zlHqpddlr448xlzh9nzXhCCnIUp1rWZQZFHdrvz/T4XzfRwjMGUms55gqd+IU8xNOMcGfr/rV1ekjJWebXmZTZZr6hH9PJM1I8v8s/MHaFTEADcmFrV23bYiRrpCnbZqdAoJ9Q/AKDAFC4FyHeA== + template: + metadata: + creationTimestamp: null + name: erpnext + namespace: erpnext + type: kubernetes.io/basic-auth diff --git a/charts/erpnext/values.yaml b/charts/erpnext/values.yaml index 79276ee0..e2baba1e 100644 --- a/charts/erpnext/values.yaml +++ b/charts/erpnext/values.yaml @@ -1,9 +1,11 @@ -postgresql: - enabled: true - auth: - existingSecret: postgres - erpnext: + enabled: false + + dbHost: "psql" + dbPort: 5432 + dbRootUser: "erpnext" + dbExistingSecret: "psql-erpnext" + dbExistingSecretPasswordKey: "password" mariadb: enabled: false @@ -32,14 +34,10 @@ erpnext: initialDelaySeconds: 10 enabled: true - dbHost: "1.2.3.4" - dbPort: "3306" - dbRootUser: "admin" - dbRootPassword: "secret" - persistence: worker: storageClass: ceph-filesystem + size: 8Gi accessModes: - ReadWriteMany jobs: @@ -67,6 +65,9 @@ erpnext: # - arm64 - amd64 createSite: + siteName: erp.deepcypher.me + adminExistingsecret: erpnext + adminExistingSecretKey: password affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -89,6 +90,7 @@ erpnext: # - arm64 - amd64 dropSite: + siteName: erp.deepcypher.me affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: @@ -100,6 +102,7 @@ erpnext: # - arm64 - amd64 migrate: + siteName: erp.deepcypher.me affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: diff --git a/charts/infrastructure/redundant/erpnext.yaml b/charts/infrastructure/templates/erpnext.yaml similarity index 100% rename from charts/infrastructure/redundant/erpnext.yaml rename to charts/infrastructure/templates/erpnext.yaml