├── tests ├── http.yaml ├── assets │ ├── values │ │ ├── noop │ │ │ ├── secrets.empty.yaml │ │ │ ├── values.yaml │ │ │ ├── secrets.yaml │ │ │ └── some-secrets.yaml │ │ ├── sops │ │ │ ├── secrets.empty.yaml │ │ │ ├── secrets.symlink.yaml │ │ │ ├── files │ │ │ │ ├── file.txt │ │ │ │ ├── file.json │ │ │ │ ├── file.properties │ │ │ │ └── file.yaml │ │ │ ├── .gitattributes │ │ │ ├── values.yaml │ │ │ ├── secrets.trailing-newline.dec.raw │ │ │ ├── .sops.yaml │ │ │ ├── secrets.dec.yaml │ │ │ ├── some-secrets.dec.yaml │ │ │ ├── secrets.trailing-newline.raw │ │ │ ├── secrets.age.yaml │ │ │ ├── secrets.yaml │ │ │ ├── http.secrets.yaml │ │ │ ├── some-secrets.yaml │ │ │ ├── secrets.yaml.gotpl │ │ │ ├── some-secrets.windows.yaml │ │ │ └── secrets.gpg_key.yaml │ │ ├── vals │ │ │ ├── secrets.empty.yaml │ │ │ ├── files │ │ │ │ ├── file.txt │ │ │ │ ├── file.yaml │ │ │ │ └── file.json │ │ │ ├── password.txt │ │ │ ├── values.yaml │ │ │ ├── templates │ │ │ │ ├── error │ │ │ │ │ └── template.yaml │ │ │ │ └── base │ │ │ │ │ └── template.yaml │ │ │ ├── secrets.yaml │ │ │ ├── http.secrets.yaml │ │ │ ├── some-secrets.yaml │ │ │ ├── gpg.txt │ │ │ ├── secrets.dec.yaml │ │ │ └── some-secrets.dec.yaml │ │ └── custom-backend │ │ │ ├── onepassword-secrets.yaml │ │ │ └── secrets.yaml │ ├── mock-editor │ │ └── editor.sh │ ├── age │ │ └── key.txt │ ├── custom-backend.sh │ └── gpg │ │ ├── private.gpg │ │ └── private2.gpg ├── it │ └── setup_suite.bash ├── unit │ ├── setup_suite.bash │ ├── dir.bats │ ├── gitops.bats │ ├── helm-plugin.bats │ ├── edit.bats │ ├── encrypt.bats │ └── secret-backends.bats ├── alpine.Dockerfile ├── ubuntu.Dockerfile ├── lib │ ├── create_encrypted_file.bash │ ├── binaries.bash │ ├── helper.bash │ └── setup_suite.bash └── README.md ├── examples ├── argo-cd │ ├── setup │ │ ├── .gitignore │ │ ├── Chart.yaml │ │ ├── Chart.lock │ │ └── values.yaml │ └── app.yaml ├── sops │ ├── secrets.yaml.dec │ ├── .sops.yaml │ ├── templates │ │ ├── serviceaccount.yaml │ │ ├── service.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── NOTES.txt │ │ ├── deployment.yaml │ │ └── _helpers.tpl │ ├── .helmignore │ ├── Chart.yaml │ ├── files │ │ └── file.properties │ ├── secrets.yaml │ └── values.yaml ├── vals │ ├── templates │ │ ├── serviceaccount.yaml │ │ ├── service.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── NOTES.txt │ │ ├── deployment.yaml │ │ └── _helpers.tpl │ ├── .helmignore │ ├── secrets.yaml │ ├── Chart.yaml │ ├── secrets.sops.yaml │ └── values.yaml ├── terraform │ ├── helm.tf │ └── .gitignore └── backends │ ├── gopass.sh │ ├── doppler.sh │ ├── envsubst.sh │ ├── vault.sh │ └── onepassword.sh ├── plugins ├── helm-secrets-cli │ ├── scripts │ └── plugin.yaml ├── helm-secrets-getter │ ├── scripts │ └── plugin.yaml └── helm-secrets-post-renderer │ ├── scripts │ └── plugin.yaml ├── scripts ├── lib │ ├── file │ │ ├── helm-values-getter │ │ │ ├── values.yaml │ │ │ ├── Chart.yaml │ │ │ └── templates │ │ │ │ └── values.yaml │ │ ├── local.sh │ │ ├── custom.sh │ │ └── http.sh │ ├── backends │ │ ├── noop.sh │ │ ├── vals.sh │ │ ├── _custom.sh │ │ └── sops.sh │ ├── http.sh │ ├── expand_vars_strict.sh │ ├── backend.sh │ ├── file.sh │ └── common.sh ├── commands │ ├── version.sh │ ├── post-renderer.sh │ ├── edit.sh │ ├── encrypt.sh │ ├── help.sh │ └── decrypt.sh └── wrapper │ └── helm.sh ├── docs ├── Home.md ├── ARGOCD.md ├── Secret Driver.md ├── README.md ├── _Footer.md ├── Known Issues.md ├── Feature Flags.md ├── Security in shared environments.md ├── Values.md ├── Installation.md ├── Secret Backends.md └── Cloud Integration.md ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── enhancement.yaml │ └── bug_report.yaml ├── workflows │ ├── stale.yaml │ └── wiki.yaml └── PULL_REQUEST_TEMPLATE.md ├── .gitattributes ├── Gemfile ├── codecov.yml ├── .gitignore ├── completion.yaml ├── .simplecov ├── .editorconfig ├── .gitmodules ├── Gemfile.lock ├── CONTRIBUTING.md ├── artifacthub-repo.yml ├── plugin.yaml ├── docker-compose.yaml ├── Dockerfile ├── USERS.md ├── contrib └── release.sh ├── renovate.json └── CODE_OF_CONDUCT.md /tests/http.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/assets/values/noop/secrets.empty.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.empty.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/assets/values/vals/secrets.empty.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/argo-cd/setup/.gitignore: -------------------------------------------------------------------------------- 1 | charts/ 2 | -------------------------------------------------------------------------------- /plugins/helm-secrets-cli/scripts: -------------------------------------------------------------------------------- 1 | ../../scripts -------------------------------------------------------------------------------- /plugins/helm-secrets-getter/scripts: -------------------------------------------------------------------------------- 1 | ../../scripts -------------------------------------------------------------------------------- /tests/it/setup_suite.bash: -------------------------------------------------------------------------------- 1 | ../lib/setup_suite.bash -------------------------------------------------------------------------------- /tests/unit/setup_suite.bash: -------------------------------------------------------------------------------- 1 | ../lib/setup_suite.bash -------------------------------------------------------------------------------- /plugins/helm-secrets-post-renderer/scripts: -------------------------------------------------------------------------------- 1 | ../../scripts -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.symlink.yaml: -------------------------------------------------------------------------------- 1 | secrets.yaml -------------------------------------------------------------------------------- /scripts/lib/file/helm-values-getter/values.yaml: -------------------------------------------------------------------------------- 1 | content: ~ 2 | -------------------------------------------------------------------------------- /tests/assets/values/sops/files/file.txt: -------------------------------------------------------------------------------- 1 | hello=fromextrafile 2 | -------------------------------------------------------------------------------- /tests/assets/values/vals/files/file.txt: -------------------------------------------------------------------------------- 1 | hello=fromextrafile 2 | -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | # Home 2 | 3 | Welcome to the helm-secrets wiki! 4 | -------------------------------------------------------------------------------- /examples/sops/secrets.yaml.dec: -------------------------------------------------------------------------------- 1 | podAnnotations: 2 | secret: value 3 | -------------------------------------------------------------------------------- /tests/assets/values/vals/files/file.yaml: -------------------------------------------------------------------------------- 1 | hello: ref+echo://fromextrafile 2 | -------------------------------------------------------------------------------- /tests/assets/values/sops/.gitattributes: -------------------------------------------------------------------------------- 1 | some-secrets.windows.yaml text eol=crlf 2 | -------------------------------------------------------------------------------- /docs/ARGOCD.md: -------------------------------------------------------------------------------- 1 | Moved to https://github.com/jkroepke/helm-secrets/wiki/ArgoCD-Integration -------------------------------------------------------------------------------- /tests/assets/values/vals/password.txt: -------------------------------------------------------------------------------- 1 | 1234567!!"§$%&/(?=)(/&%$§"><;:_,;:_'''*'*\ndks 2 | -------------------------------------------------------------------------------- /tests/assets/mock-editor/editor.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | echo "hello: world" >"$1" 4 | -------------------------------------------------------------------------------- /tests/assets/values/vals/files/file.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "ref+echo://fromextrafile" 3 | } 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: jkroepke 4 | -------------------------------------------------------------------------------- /docs/Secret Driver.md: -------------------------------------------------------------------------------- 1 | Moved to https://github.com/jkroepke/helm-secrets/wiki/Secret%20Backends 2 | -------------------------------------------------------------------------------- /tests/assets/values/noop/values.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_values 2 | service: 3 | port: 85 4 | -------------------------------------------------------------------------------- /tests/assets/values/sops/values.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_values 2 | service: 3 | port: 85 4 | -------------------------------------------------------------------------------- /tests/assets/values/vals/values.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_values 2 | service: 3 | port: 85 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf 3 | sh.cmd text eol=crlf 4 | run.cmd text eol=lf 5 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.trailing-newline.dec.raw: -------------------------------------------------------------------------------- 1 | value: |+ 2 | multi 3 | line 4 | 5 | 6 | -------------------------------------------------------------------------------- /scripts/lib/file/helm-values-getter/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | name: helm-values-getter 3 | version: 1.0.0 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'bashcov', '~> 3.0', '>= 3.0.2' 4 | gem 'simplecov-cobertura' 5 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: false 4 | patch: off 5 | ignore: 6 | - "contrib/**/*" 7 | -------------------------------------------------------------------------------- /scripts/lib/file/helm-values-getter/templates/values.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | content: | 3 | {{- .Values.content | nindent 2 -}} 4 | -------------------------------------------------------------------------------- /tests/assets/values/vals/templates/error/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | data: 4 | config: ref+file://notfound 5 | -------------------------------------------------------------------------------- /scripts/commands/version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | version() { 6 | grep version "${SCRIPT_DIR}/../plugin.yaml" | cut -d'"' -f2 7 | } 8 | -------------------------------------------------------------------------------- /tests/assets/values/vals/secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ref+echo://global_bar 2 | key: ref+file://assets/values/vals/gpg.txt 3 | service: 4 | port: ref+echo://81 5 | -------------------------------------------------------------------------------- /tests/assets/values/vals/http.secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ref+echo://global_bar 2 | key: ref+file://assets/values/vals/gpg.txt 3 | service: 4 | port: ref+echo://81 5 | -------------------------------------------------------------------------------- /tests/assets/values/vals/some-secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ref+echo://global_bar 2 | key: ref+file://assets/values/vals/gpg.txt 3 | service: 4 | port: ref+echo://83 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | /.idea/ 3 | /tests/.tmp/ 4 | coverage 5 | .local 6 | 7 | *.dec 8 | *.test 9 | 10 | .netrc 11 | 12 | test*.sh 13 | /vendor 14 | /helm 15 | *.tgz 16 | -------------------------------------------------------------------------------- /examples/sops/.sops.yaml: -------------------------------------------------------------------------------- 1 | creation_rules: 2 | # encrypted using helm-secrets-example-projectx 3 | - pgp: "D6174A02027050E59C711075B430C4E58E2BBBA3" 4 | -------------------------------------------------------------------------------- /examples/argo-cd/setup/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: setup 3 | version: 1.0.0 4 | dependencies: 5 | - name: argo-cd 6 | version: 4.10.7 7 | repository: https://argoproj.github.io/argo-helm 8 | -------------------------------------------------------------------------------- /tests/assets/values/sops/.sops.yaml: -------------------------------------------------------------------------------- 1 | creation_rules: 2 | # encrypted using helm-secrets-example-projectx 3 | - pgp: "D6174A02027050E59C711075B430C4E58E2BBBA3" 4 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation of helm-secrets 2 | 3 | Please note, all markdown files are synced with the helm-secrets wiki to improve the readability of the pages. 4 | 5 | https://github.com/jkroepke/helm-secrets/wiki 6 | -------------------------------------------------------------------------------- /tests/assets/age/key.txt: -------------------------------------------------------------------------------- 1 | # created: 2021-11-05T01:51:40+01:00 2 | # public key: age1rh2xsjg8avja92j3jhtttuczgmqkz89lezxt4e3mmp5ukl5j4f9smvteju 3 | AGE-SECRET-KEY-1E9XAHYWZHY026MSX7FVX98XTXWJS4UC38QG603X9JDM2SPRJ8HZSVGG0LT 4 | -------------------------------------------------------------------------------- /examples/argo-cd/setup/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: argo-cd 3 | repository: https://argoproj.github.io/argo-helm 4 | version: 4.10.7 5 | digest: sha256:7583dbbfaccdcd0e788ff226ca15845a6aedcbb59765f2758d30a2fcc8750bd7 6 | generated: "2022-08-19T19:50:11.470467+02:00" 7 | -------------------------------------------------------------------------------- /scripts/lib/file/local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | _file_local_exists() { 6 | test -f "${1}" 7 | } 8 | 9 | _file_local_get() { 10 | if ! _file_local_exists "$@"; then 11 | exit 1 12 | fi 13 | 14 | printf '%s' "${1}" 15 | } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: "Helm 4: Plugin install won't work" 4 | url: https://github.com/jkroepke/helm-secrets/wiki/Installation 5 | about: helm plugin install has been changed in Helm 4. Please see docs. 6 | -------------------------------------------------------------------------------- /scripts/lib/backends/noop.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | _noop_backend_is_file_encrypted() { 4 | false 5 | } 6 | 7 | _noop_backend_edit_file() { 8 | # shellcheck disable=SC2034 9 | type="${1}" 10 | input="${2}" 11 | 12 | "${EDITOR:-vi}" "${input}" 13 | } 14 | -------------------------------------------------------------------------------- /docs/_Footer.md: -------------------------------------------------------------------------------- 1 | 2 | This wiki is synced with the [`docs`](https://github.com/jkroepke/helm-secrets/tree/main/docs) folder from the code repository! To improve the wiki, create a [pull request](https://github.com/jkroepke/helm-secrets/pulls) against the code repository with the suggested changes. 3 | -------------------------------------------------------------------------------- /completion.yaml: -------------------------------------------------------------------------------- 1 | name: secrets 2 | flags: 3 | - help 4 | - backend 5 | - quiet 6 | commands: 7 | - name: decrypt 8 | - name: encrypt 9 | - name: edit 10 | - name: lint 11 | - name: template 12 | - name: install 13 | - name: upgrade 14 | - name: diff 15 | - name: kubeval 16 | -------------------------------------------------------------------------------- /scripts/commands/post-renderer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | post_renderer() { 6 | if [ "${EVALUATE_TEMPLATES_DECODE_SECRETS}" = "true" ]; then 7 | SECRET_BACKEND_ARGS="${SECRET_BACKEND_ARGS:-} -decode-kubernetes-secrets" 8 | fi 9 | 10 | _vals_backend_decrypt_file "yaml" "-" 11 | } 12 | -------------------------------------------------------------------------------- /.simplecov: -------------------------------------------------------------------------------- 1 | require 'simplecov-cobertura' 2 | 3 | SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter 4 | 5 | SimpleCov.start do 6 | minimum_coverage 5 7 | add_filter "/contrib/" 8 | add_filter "/examples/" 9 | add_filter "/lib/file/helm-values-getter/" 10 | add_filter "/tests/" 11 | add_filter "/.git/" 12 | add_filter "/tmp/" 13 | end 14 | -------------------------------------------------------------------------------- /tests/assets/values/vals/templates/base/template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | data: 4 | config: ref+echo://42 5 | config.env: ref+envsubst://$SECRET_VALUE 6 | --- 7 | apiVersion: v1 8 | kind: Secret 9 | data: 10 | secret: '{{ print "ref+echo://42" | b64enc }}' 11 | secret.env: '{{ print "ref+envsubst://$SECRET_VALUE" | b64enc }}' 12 | --- 13 | -------------------------------------------------------------------------------- /docs/Known Issues.md: -------------------------------------------------------------------------------- 1 | # Known Issues 2 | 3 | # Compatibility issue between gpg 2.2 and gpg 2.3. 4 | 5 | Error: 6 | ``` 7 | Error decrypting tree: Error walking tree: Could not decrypt value: crypto/aes: invalid key size 0 8 | ``` 9 | For possible workarounds see [Issue 1](https://github.com/jkroepke/helm-secrets/issues/158) and [Issue 2](https://github.com/getsops/sops/issues/896) 10 | -------------------------------------------------------------------------------- /scripts/wrapper/helm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ "${HELM_SECRETS_WRAPPER_ENABLED}" = "true" ]; then 4 | if [ "$1" = "install" ] || [ "$1" = "upgrade" ] || [ "$1" = "template" ] || [ "$1" = "lint" ] || [ "$1" = "diff" ]; then 5 | exec "${HELM_SECRETS_HELM_PATH:-${HELM_BIN:-"helm"}}" secrets "$@" 6 | fi 7 | fi 8 | 9 | exec "${HELM_SECRETS_HELM_PATH:-${HELM_BIN:-"helm"}}" "$@" 10 | -------------------------------------------------------------------------------- /examples/sops/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "simple-chart.serviceAccountName" . }} 6 | labels: 7 | {{- include "simple-chart.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /examples/vals/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "simple-chart.serviceAccountName" . }} 6 | labels: 7 | {{- include "simple-chart.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /tests/assets/custom-backend.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC2034 4 | _BACKEND_REGEX='!vault [A-z0-9][A-z0-9/*\.\_\-]*\#[A-z0-9*\.\_\-][A-z0-9*\.\_\-]*' 5 | 6 | . "${HELM_SECRETS_SCRIPT_DIR}/lib/backends/_custom.sh" 7 | 8 | _custom_backend_get_secret() { 9 | _type=$1 10 | _SECRET=$2 11 | 12 | if ! echo "${_SECRET}"; then 13 | fatal "Error while get secret!" 14 | fi 15 | } 16 | -------------------------------------------------------------------------------- /examples/terraform/helm.tf: -------------------------------------------------------------------------------- 1 | data "external" "helm-secrets" { 2 | program = ["helm", "secrets", "decrypt", "--terraform", "../../examples/sops/secrets.yaml"] 3 | } 4 | 5 | resource "helm_release" "example" { 6 | name = "helm-values-getter" 7 | chart = "../../examples/sops/" 8 | 9 | values = [ 10 | file("../../examples/sops/values.yaml"), 11 | base64decode(data.external.helm-secrets.result.content_base64), 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /examples/sops/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "simple-chart.fullname" . }} 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "simple-chart.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /examples/vals/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "simple-chart.fullname" . }} 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "simple-chart.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /examples/sops/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /examples/vals/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /scripts/lib/http.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | download() { 6 | if command -v "${HELM_SECRETS_CURL_PATH:-curl}" >/dev/null; then 7 | "${HELM_SECRETS_CURL_PATH:-curl}" ${NETRC:+--netrc-file "${NETRC}"} -sSfL "$1" 8 | elif command -v "${HELM_SECRETS_WGET_PATH:-wget}" >/dev/null; then 9 | "${HELM_SECRETS_WGET_PATH:-wget}" -q -O- "$1" 10 | else 11 | error "Unable to detect 'curl' or 'wget'." 12 | false 13 | fi 14 | } 15 | -------------------------------------------------------------------------------- /tests/assets/values/custom-backend/onepassword-secrets.yaml: -------------------------------------------------------------------------------- 1 | service: 2 | username: op://Private/helm-secrets test/username 3 | password: op://Private/helm-secrets test/password 4 | data: 5 | owner: op://Private/helm-secrets test/email 6 | names: 7 | - op://Private/helm-secrets test/data/username 8 | - op://Private/helm-secrets test/data 2/email 9 | passwords: 10 | - op://Private/helm-secrets test/data/password 11 | - op://Private/helm-secrets test/data 2/password 12 | -------------------------------------------------------------------------------- /tests/assets/values/custom-backend/secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: !vault secret/production#global_secret 2 | key: !vault secret/gpg#key 3 | service: 4 | port: !vault secret/production#port 5 | data: !vault secret/production#data 6 | domain: 7 | cert: !vault secret/domain#tls.crt 8 | names: 9 | - !vault secret/domain#*.test-example.com 10 | - !vault secret/domain#example_com 11 | keys: 12 | - !vault secret/*.test-example.com#tls.key 13 | - !vault secret/example_com#tls-2.key 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [sh.cmd] 8 | end_of_line = crlf 9 | 10 | [password.txt] 11 | insert_final_newline = false 12 | 13 | [*.{sh,bats,bash}] 14 | indent_style = space 15 | indent_size = 4 16 | max_line_length = 120 17 | trim_trailing_whitespace = true 18 | 19 | [*.sh] 20 | shell_variant = posix 21 | 22 | [*.yaml] 23 | indent_size = 2 24 | 25 | [*.bash] 26 | shell_variant = bash 27 | 28 | [*.bats] 29 | shell_variant = bats 30 | -------------------------------------------------------------------------------- /examples/sops/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "simple-chart.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "simple-chart.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /examples/vals/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "simple-chart.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test-success 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "simple-chart.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /plugins/helm-secrets-post-renderer/plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | type: postrenderer/v1 3 | name: "secrets-post-renderer" 4 | version: "4.8.0-dev" 5 | runtime: subprocess 6 | runtimeConfig: 7 | platformCommand: 8 | - os: windows 9 | command: >- 10 | cmd.exe /D /E:ON /V:ON /C !HELM_PLUGIN_DIR!\scripts\wrapper\run.cmd 11 | - os: linux 12 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 13 | - os: darwin 14 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 15 | - command: "$HELM_PLUGIN_DIR/scripts/run.sh" 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/bats/core"] 2 | path = tests/bats/core 3 | url = https://github.com/bats-core/bats-core.git 4 | [submodule "tests/bats/extensions/bats-assert"] 5 | path = tests/bats/extensions/bats-assert 6 | url = https://github.com/bats-core/bats-assert.git 7 | branch = main 8 | [submodule "tests/bats/extensions/bats-file"] 9 | path = tests/bats/extensions/bats-file 10 | url = https://github.com/bats-core/bats-file.git 11 | [submodule "tests/bats/extensions/bats-support"] 12 | path = tests/bats/extensions/bats-support 13 | url = https://github.com/bats-core/bats-support.git 14 | branch = main 15 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | bashcov (3.2.0) 5 | simplecov (~> 0.21) 6 | docile (1.4.1) 7 | rexml (3.4.2) 8 | simplecov (0.22.0) 9 | docile (~> 1.1) 10 | simplecov-html (~> 0.11) 11 | simplecov_json_formatter (~> 0.1) 12 | simplecov-cobertura (3.1.0) 13 | rexml 14 | simplecov (~> 0.19) 15 | simplecov-html (0.13.2) 16 | simplecov_json_formatter (0.1.4) 17 | 18 | PLATFORMS 19 | x86_64-darwin-22 20 | x86_64-linux 21 | 22 | DEPENDENCIES 23 | bashcov (~> 3.0, >= 3.0.2) 24 | simplecov-cobertura 25 | 26 | BUNDLED WITH 27 | 2.4.10 28 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | permissions: 7 | issues: write 8 | pull-requests: write 9 | 10 | jobs: 11 | stale: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 15 | with: 16 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' 17 | days-before-stale: 30 18 | days-before-close: 5 19 | enable-statistics: true 20 | -------------------------------------------------------------------------------- /examples/argo-cd/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Application 3 | metadata: 4 | name: test 5 | spec: 6 | destination: 7 | namespace: default 8 | server: https://kubernetes.default.svc 9 | project: default 10 | source: 11 | path: examples/sops 12 | repoURL: https://github.com/jkroepke/helm-secrets 13 | targetRevision: HEAD 14 | helm: 15 | valueFiles: 16 | - 'secrets+gpg-import-kubernetes://helm-secrets-private-keys#key.asc?secrets.yaml' 17 | fileParameters: 18 | - name: podAnnotations.fromFile 19 | path: 'secrets+gpg-import-kubernetes://helm-secrets-private-keys#key.asc?./files/file.properties' 20 | -------------------------------------------------------------------------------- /tests/unit/dir.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../bats/extensions/bats-support/load' 5 | load '../bats/extensions/bats-assert/load' 6 | load '../bats/extensions/bats-file/load' 7 | 8 | @test "dir: helm dir" { 9 | if on_windows; then 10 | DS="\\" 11 | else 12 | DS="/" 13 | fi 14 | 15 | run "${HELM_BIN}" secrets dir 16 | assert_success 17 | 18 | if helm_version_greater_or_equal_than 4.0.0; then 19 | assert_output --partial "$("${HELM_BIN}" env HELM_PLUGINS)${DS}helm-secrets-cli" 20 | else 21 | assert_output --partial "$("${HELM_BIN}" env HELM_PLUGINS)${DS}helm-secrets" 22 | fi 23 | } 24 | -------------------------------------------------------------------------------- /examples/vals/secrets.yaml: -------------------------------------------------------------------------------- 1 | podAnnotations: 2 | # https://github.com/variantdev/vals#sops 3 | secret: ref+sops://assets/values/vals/secrets.sops.yaml#/podAnnotations#secret 4 | 5 | # https://github.com/variantdev/vals#vault 6 | secret2: ref+vault://mykv/foo#/bar?address=https://vault1.example.com:8200 7 | 8 | # https://github.com/variantdev/vals#aws-secrets-manager 9 | secret3: ref+awssecrets://PATH/TO/SECRET[?region=REGION&version_stage=STAGE&version_id=ID] 10 | 11 | # https://github.com/variantdev/vals#gcp-secrets-manager 12 | secret4: ref+gcpsecrets://myproject/mysecret?version=3 13 | 14 | # https://github.com/variantdev/vals#file 15 | secret5: ref+file://some.yaml#/foo/bar 16 | -------------------------------------------------------------------------------- /scripts/lib/file/custom.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | _file_custom_exists() { 6 | _file_custom_get "$@" >/dev/null 7 | } 8 | 9 | _file_custom_get() { 10 | _tmp_file=$(_mktemp) 11 | GETTER_CHART_PATH="$(_helm_winpath "${SCRIPT_DIR}/lib/file/helm-values-getter")" 12 | 13 | if ! CONTENT="$(env -u HELM_DEBUG "${HELM_BIN}" template "${GETTER_CHART_PATH}" --set-file "content=${1}")"; then 14 | fatal "helm template command errored on value '%s'" "${1}" 15 | fi 16 | 17 | # shellcheck disable=SC2016 18 | if ! printf '%s' "${CONTENT}" | sed -e '1,3d' -e 's/^ //g' >"${_tmp_file}"; then 19 | fatal "sed command errored on value '%s'" "${1}" 20 | fi 21 | 22 | printf '%s' "${_tmp_file}" 23 | } 24 | -------------------------------------------------------------------------------- /scripts/lib/file/http.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | URL_VARIABLE_EXPANSION="${HELM_SECRETS_URL_VARIABLE_EXPANSION:-false}" 6 | 7 | _file_http_exists() { 8 | _file_http_get "$@" >/dev/null 9 | } 10 | 11 | _file_http_get() { 12 | if [ "${URL_VARIABLE_EXPANSION}" = "true" ]; then 13 | _url="$(printf '%s' "${1}" | expand_vars_strict)" 14 | else 15 | _url="${1}" 16 | fi 17 | 18 | _tmp_file="$(_mktemp)" 19 | 20 | if ! download "${_url}" >"${_tmp_file}"; then 21 | if [ "${IGNORE_MISSING_VALUES}" = "true" ]; then 22 | return 1 23 | else 24 | fatal "Error while download url %s" "${1}" 25 | fi 26 | fi 27 | 28 | printf '%s' "${_tmp_file}" 29 | } 30 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | **What this PR does / why we need it**: 7 | 8 | **Which issue this PR fixes** *(optional, in `fixes #(, fixes #, ...)` format, will close that issue when PR gets merged)*: fixes # 9 | 10 | **Special notes for your reviewer**: 11 | 12 | **PR Readiness Checklist**: 13 | 14 | Complete these before marking the PR as `ready to review`: 15 | 16 | - [ ] the `CHANGELOG.md` release notes have been updated to reflect any significant (and particularly user-facing) changes introduced by this PR 17 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue before making a change. 4 | 5 | Please note we have a code of conduct, please follow it in all your interactions with the project. 6 | 7 | ## Pull Request Process 8 | 9 | 1. Update the [README.md](README.md) or [USAGE.md](USAGE.md) with details of changes to the interface, this includes new environment 10 | variables, useful file locations and other parameters. 11 | 2. If possible add [test cases](tests/README.md) to cover the issues or new feature. 12 | 3. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 13 | do not have permission to do that, you may request the second reviewer to merge it for you. 14 | -------------------------------------------------------------------------------- /examples/backends/gopass.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # shellcheck disable=SC2034 4 | _BACKEND_REGEX='!gopass [A-Za-z0-9\-\_\/]*' 5 | 6 | # shellcheck source=scripts/lib/backends/_custom.sh 7 | . "${SCRIPT_DIR}/lib/backends/_custom.sh" 8 | 9 | _gopass() { 10 | # shellcheck disable=SC2086 11 | set -- ${SECRET_BACKEND_ARGS} "$@" 12 | gopass "$@" 13 | } 14 | 15 | _custom_backend_get_secret() { 16 | _type=$1 17 | _SECRET=$2 18 | 19 | if [ "${_type}" != "yaml" ]; then 20 | echo "Only decryption of yaml files are allowed!" 21 | exit 1 22 | fi 23 | 24 | if ! _gopass show -o "${_SECRET}"; then 25 | echo "Error while get secret from gopass!" >&2 26 | echo gopass show -o "${_SECRET}" "${SECRET_BACKEND_ARGS}" >&2 27 | exit 1 28 | fi 29 | } 30 | -------------------------------------------------------------------------------- /scripts/lib/expand_vars_strict.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | # https://stackoverflow.com/a/40167919 6 | expand_vars_strict() { 7 | _x4=$(printf '\x4') 8 | # the `||` clause ensures that the last line is read even if it doesn't end with \n 9 | while IFS= read -r line || [ -n "${line}" ]; do 10 | # Escape ALL chars. that could trigger an expansion.. 11 | lineEscaped=$( 12 | printf %s "$line" | 13 | tr '`([$' '\1\2\3\4' | 14 | # ... then selectively reenable ${ references 15 | sed -e "s/$_x4{/\${/g" | 16 | # Finally, escape embedded double quotes to preserve them. 17 | sed -e 's/"/\\\"/g' 18 | ) 19 | eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$' 20 | done 21 | } 22 | -------------------------------------------------------------------------------- /artifacthub-repo.yml: -------------------------------------------------------------------------------- 1 | # Artifact Hub repository metadata file 2 | # 3 | # Some settings like the verified publisher flag or the ignored packages won't 4 | # be applied until the next time the repository is processed. Please keep in 5 | # mind that the repository won't be processed if it has not changed since the 6 | # last time it was processed. Depending on the repository kind, this is checked 7 | # in a different way. For Helm http based repositories, we consider it has 8 | # changed if the `index.yaml` file changes. For git based repositories, it does 9 | # when the hash of the last commit in the branch you set up changes. This does 10 | # NOT apply to ownership claim operations, which are processed immediately. 11 | # 12 | repositoryID: bc64e82e-e638-4cb0-8ed0-2428a34a94d5 13 | owners: 14 | - name: jkroepke 15 | email: github@jkroepke.de 16 | -------------------------------------------------------------------------------- /plugin.yaml: -------------------------------------------------------------------------------- 1 | name: "secrets" 2 | version: "4.8.0-dev" 3 | usage: "Secrets encryption in Helm for Git storing" 4 | description: |- 5 | This plugin provides secrets values encryption for Helm charts secure storing 6 | useTunnel: false 7 | platformCommand: 8 | - os: windows 9 | command: >- 10 | cmd.exe /D /E:ON /V:ON /C !HELM_PLUGIN_DIR!\scripts\wrapper\run.cmd 11 | - os: linux 12 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 13 | - os: darwin 14 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 15 | - command: "$HELM_PLUGIN_DIR/scripts/run.sh" 16 | 17 | downloaders: 18 | - command: "scripts/run.sh downloader" 19 | protocols: 20 | - "secrets" 21 | - "secrets+gpg-import" 22 | - "secrets+gpg-import-kubernetes" 23 | - "secrets+age-import" 24 | - "secrets+age-import-kubernetes" 25 | - "secrets+literal" 26 | -------------------------------------------------------------------------------- /examples/backends/doppler.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ "${QUIET}" = "false" ]; then 4 | log 'DEPRECATED: doppler backend is going to be removed in the next major version.' 5 | fi 6 | 7 | _DOPPLER="${HELM_SECRETS_VAULT_PATH:-doppler}" 8 | 9 | # shellcheck disable=SC2034 10 | _BACKEND_REGEX='!doppler [a-z0-9_-]*\#.*\#[A-Z0-9_]*' 11 | 12 | # shellcheck source=scripts/lib/backends/_custom.sh 13 | . "${SCRIPT_DIR}/lib/backends/_custom.sh" 14 | 15 | _doppler() { 16 | # shellcheck disable=SC2086 17 | set -- ${SECRET_BACKEND_ARGS} "$@" 18 | $_DOPPLER "$@" 19 | } 20 | 21 | _custom_backend_get_secret() { 22 | _SECRET=$2 23 | 24 | # Tokenize 25 | IFS="#" 26 | # shellcheck disable=SC2086 27 | set -- $_SECRET 28 | project=$1 29 | config=$2 30 | secret=$3 31 | 32 | _doppler secrets get -p "${project}" -c "${config}" "${secret}" --plain || exit 1 33 | } 34 | -------------------------------------------------------------------------------- /tests/alpine.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | ARG VERSION_HELM=3.11.0 4 | ARG VERSION_SOPS=3.8.1 5 | ARG VERSION_VALS=0.37.1 6 | 7 | RUN apk add git curl gnupg ruby bash \ 8 | && curl -sSfL https://github.com/getsops/sops/releases/download/v${VERSION_SOPS}/sops-v${VERSION_SOPS}.linux -o /usr/local/bin/sops \ 9 | && chmod +x /usr/local/bin/sops \ 10 | && curl -sSfL https://get.helm.sh/helm-v${VERSION_HELM}-linux-amd64.tar.gz | tar xzf - --strip-component 1 -C /usr/local/bin/ \ 11 | && curl -sSfL https://github.com/variantdev/vals/releases/download/v${VERSION_VALS}/vals_${VERSION_VALS}_linux_amd64.tar.gz | tar xzf - -C /usr/local/bin/ vals \ 12 | && gem install bashcov simplecov-cobertura 13 | 14 | ENV BATSLIB_TEMP_PRESERVE="0" BATSLIB_TEMP_PRESERVE_ON_FAILURE="0" 15 | 16 | WORKDIR /helm-secrets/ 17 | ENTRYPOINT ["/helm-secrets/tests/bats/core/bin/bats"] 18 | CMD [ "tests/unit/helm-plugin.bats"] 19 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | helm-secrets: 4 | image: ubuntu:24.04 5 | working_dir: /work/helm-secrets 6 | init: true 7 | stop_grace_period: 0s 8 | volumes: 9 | - ./:/work/helm-secrets 10 | command: 11 | - bash 12 | - -c 13 | - >- 14 | apt-get update 15 | && apt-get install git gnupg ruby curl -y 16 | && curl -sSfL https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64 -o /usr/local/bin/sops 17 | && curl -sSfL https://releases.hashicorp.com/vault/1.10.1/vault_1.10.1_linux_amd64.zip -o /tmp/vault.zip && unzip /tmp/vault.zip && mv vault /usr/local/bin 18 | && curl -sSfL https://get.helm.sh/helm-v3.7.0-linux-amd64.tar.gz | tar zxf - -C /usr/local/bin/ --strip-components 1 linux-amd64/helm 19 | && chmod +x /usr/local/bin/* 20 | && echo Done 21 | && sleep inf 22 | 23 | -------------------------------------------------------------------------------- /scripts/commands/edit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | edit_usage() { 6 | cat < 8 | 9 | Edit encrypted secrets 10 | 11 | Decrypt encrypted file, edit and then encrypt 12 | 13 | You can use plain sops to edit - https://github.com/getsops/sops 14 | 15 | Example: 16 | $ helm secrets edit 17 | or $ sops 18 | $ git add 19 | $ git commit 20 | $ git push 21 | 22 | EOF 23 | } 24 | 25 | edit_helper() { 26 | dir=$(dirname "$1") 27 | file=$(basename "$1") 28 | 29 | if [ ! -d "${dir}" ]; then 30 | fatal 'Directory does not exist: %s' "${dir}" 31 | fi 32 | 33 | cd "$dir" 34 | backend_edit_file "yaml" "${file}" 35 | } 36 | 37 | edit() { 38 | echo "$1" 39 | if is_help "$1"; then 40 | edit_usage 41 | return 42 | fi 43 | 44 | file="$1" 45 | edit_helper "${file}" 46 | } 47 | -------------------------------------------------------------------------------- /tests/ubuntu.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | ARG VERSION_HELM=3.11.0 4 | ARG VERSION_SOPS=3.8.1 5 | ARG VERSION_VALS=0.37.1 6 | 7 | RUN apt-get update -qq && apt-get install -yqq git curl gnupg2 ruby \ 8 | && curl -sSfL https://github.com/getsops/sops/releases/download/v${VERSION_SOPS}/sops-v${VERSION_SOPS}.linux -o /usr/local/bin/sops \ 9 | && chmod +x /usr/local/bin/sops \ 10 | && curl -sSfL https://get.helm.sh/helm-v${VERSION_HELM}-linux-amd64.tar.gz | tar xzf - --strip-component 1 -C /usr/local/bin/ --wildcards '*/helm' \ 11 | && curl -sSfL https://github.com/variantdev/vals/releases/download/v${VERSION_VALS}/vals_${VERSION_VALS}_linux_amd64.tar.gz | tar xzf - -C /usr/local/bin/ vals \ 12 | && gem install bashcov simplecov-cobertura 13 | 14 | ENV BATSLIB_TEMP_PRESERVE="0" BATSLIB_TEMP_PRESERVE_ON_FAILURE="0" 15 | 16 | WORKDIR /helm-secrets/ 17 | ENTRYPOINT ["/helm-secrets/tests/bats/core/bin/bats"] 18 | CMD [ "tests/unit/helm-plugin.bats"] 19 | -------------------------------------------------------------------------------- /tests/unit/gitops.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../lib/create_encrypted_file' 5 | load '../bats/extensions/bats-support/load' 6 | load '../bats/extensions/bats-assert/load' 7 | load '../bats/extensions/bats-file/load' 8 | 9 | @test "gitops: Be silent inside ArgoCD" { 10 | if on_windows || on_wsl; then 11 | skip 12 | fi 13 | 14 | FILE="${TEST_TEMP_DIR}/assets/values/${HELM_SECRETS_BACKEND}/secrets.yaml" 15 | 16 | create_chart "${TEST_TEMP_DIR}" 17 | 18 | env ARGOCD_APP_NAME=helm-secrets "${HELM_BIN}" lint "${TEST_TEMP_DIR}/chart" -f "${FILE}" >"${TEST_TEMP_DIR}/output.helm.txt" 2>&1 19 | env ARGOCD_APP_NAME=helm-secrets "${HELM_BIN}" secrets lint "${TEST_TEMP_DIR}/chart" -f "${FILE}" >"${TEST_TEMP_DIR}/output.secrets.txt" 2>&1 20 | 21 | run diff "${TEST_TEMP_DIR}/output.helm.txt" "${TEST_TEMP_DIR}/output.secrets.txt" 22 | assert_success 23 | 24 | rm -rf "${TEST_TEMP_DIR}/output.*.txt" 25 | } 26 | -------------------------------------------------------------------------------- /tests/assets/values/vals/gpg.txt: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP MESSAGE----- 2 | 3 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 4 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 5 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 6 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 7 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 8 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 9 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 10 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 11 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 12 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 13 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 14 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 15 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 16 | EzAA 17 | =jf7D 18 | -----END PGP MESSAGE----- 19 | -------------------------------------------------------------------------------- /docs/Feature Flags.md: -------------------------------------------------------------------------------- 1 | # Feature Flags 2 | 3 | Some unstable or risky feature in helm-secrets are disabled by default. 4 | 5 | # Environment variable expansion in URL value files 6 | 7 | If the environment variable `HELM_SECRETS_URL_VARIABLE_EXPANSION` is set to `true`, then environment variables inside urls will be substituted. 8 | 9 | ## Example 10 | 11 | - `secrets://https://${GITHUB_TOKEN}@raw.githubusercontent.com/org/repo/ref/pathtofile.yml` 12 | 13 | In this case, `GITHUB_TOKEN` will be substituted with an environment variable named GITHUB_TOKEN. Only `${}` syntax is supported. 14 | 15 | ## Conflicting environments 16 | 17 | Some environment like ArgoCD do the same, but with an [limited](https://argo-cd.readthedocs.io/en/stable/user-guide/build-environment/) subset of environment variables. 18 | 19 | In such situations, the `$` needs escaped to prevent evaluation in environments. For ArgoCD, it's an additional dollar sign like `$${GITHUB_TOKEN}`. Other environments are working with back-slash like `\${GITHUB_TOKEN}` 20 | -------------------------------------------------------------------------------- /tests/lib/create_encrypted_file.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | create_encrypted_file() { 4 | { 5 | file="${2:-secrets.yaml}" 6 | case "${3:-${HELM_SECRETS_BACKEND}}" in 7 | sops) 8 | # shellcheck disable=SC2059 9 | printf "$1" >"${TEST_TEMP_DIR}/${file}" 10 | (cd "${TEST_TEMP_DIR}" && exec "${SOPS_BIN}" -e -i "${file}") 11 | ;; 12 | vals) 13 | # shellcheck disable=SC2059 14 | printf "$1" >"${TEST_TEMP_DIR}/vals.${file}" 15 | yaml_key="$(echo "$1" | cut -d: -f1)" 16 | printf '%s: ref+file://%s#%s' "${yaml_key}" "${TEST_TEMP_DIR}/vals.${file}" "${yaml_key}" >"${TEST_TEMP_DIR}/${file}" 17 | ;; 18 | noop) 19 | # shellcheck disable=SC2059 20 | printf "$1" >"${TEST_TEMP_DIR}/${file}" 21 | ;; 22 | *) 23 | echo "Unknown backend ${HELM_SECRETS_BACKEND}" 24 | exit 1 25 | ;; 26 | esac 27 | } >&2 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/wiki.yaml: -------------------------------------------------------------------------------- 1 | name: Push to GH Wiki 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - docs/** 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 16 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 17 | with: 18 | repository: ${{ github.repository }}.wiki 19 | path: wiki 20 | 21 | - name: sync wiki 22 | run: rsync -av --delete --exclude=ARGOCD.md --exclude=README.md --exclude=.git docs/ wiki/ 23 | 24 | - name: remove header line 25 | run: sed -i '1d' wiki/*.md 26 | 27 | - name: Commit files 28 | run: | 29 | git config --local user.email "action@github.com" 30 | git config --local user.name "GitHub Action" 31 | git add . 32 | git diff-index --quiet HEAD || git commit -m "Automatically publish wiki" && git push 33 | working-directory: wiki 34 | -------------------------------------------------------------------------------- /examples/sops/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "simple-chart.fullname" . }} 6 | labels: 7 | {{- include "simple-chart.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "simple-chart.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /examples/vals/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "simple-chart.fullname" . }} 6 | labels: 7 | {{- include "simple-chart.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "simple-chart.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /examples/backends/envsubst.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ "${QUIET}" = "false" ]; then 4 | log 'DEPRECATED: Envsubst backend is going to be remove in the next major version. Use vals backend instead.' 5 | fi 6 | 7 | _envsubst() { 8 | # shellcheck disable=SC2086 9 | set -- ${SECRET_BACKEND_ARGS} "$@" 10 | envsubst "$@" 11 | } 12 | 13 | _custom_backend_is_file_encrypted() { 14 | input="${1}" 15 | 16 | grep -q '\$' "${input}" 17 | } 18 | 19 | _custom_backend_encrypt_file() { 20 | echo "Encrypting files with envsubst backend is not supported!" 21 | exit 1 22 | } 23 | 24 | _custom_backend_decrypt_file() { 25 | # shellcheck disable=SC2034 26 | type="${1}" 27 | input="${2}" 28 | # if omit then output to stdout 29 | output="${3:-}" 30 | 31 | if [ "${output}" != "" ]; then 32 | _envsubst <"${input}" >"${output}" 33 | else 34 | _envsubst <"${input}" 35 | fi 36 | } 37 | 38 | _custom_backend_edit_file() { 39 | echo "Editing files with envsubst backend is not supported!" 40 | exit 1 41 | } 42 | -------------------------------------------------------------------------------- /tests/lib/binaries.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MacOS have shasum, others have sha1sum 4 | if command -v shasum >/dev/null; then 5 | export SHA1SUM_BIN=shasum 6 | else 7 | export SHA1SUM_BIN=sha1sum 8 | fi 9 | 10 | # cygwin does not have an alias 11 | if command -v gpg2 >/dev/null; then 12 | export GPG_BIN=gpg2 13 | elif command -v gpg.exe >/dev/null; then 14 | export GPG_BIN=gpg.exe 15 | else 16 | export GPG_BIN=gpg 17 | fi 18 | 19 | if command -v gpgconf.exe >/dev/null; then 20 | export GPGCONF_BIN=gpgconf.exe 21 | else 22 | export GPGCONF_BIN=gpgconf 23 | fi 24 | 25 | if command -v git.exe >/dev/null; then 26 | export GIT_BIN=git.exe 27 | else 28 | export GIT_BIN=git 29 | fi 30 | 31 | if command -v helm.exe >/dev/null; then 32 | export HELM_BIN=helm.exe 33 | else 34 | export HELM_BIN=helm 35 | fi 36 | 37 | if command -v sops.exe >/dev/null; then 38 | export SOPS_BIN=sops.exe 39 | else 40 | export SOPS_BIN=sops 41 | fi 42 | 43 | if command -v vals.exe >/dev/null; then 44 | export VALS_BIN=vals.exe 45 | else 46 | export VALS_BIN=vals 47 | fi 48 | -------------------------------------------------------------------------------- /plugins/helm-secrets-getter/plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | type: getter/v1 3 | name: "secrets-getter" 4 | version: "4.8.0-dev" 5 | runtime: subprocess 6 | config: 7 | protocols: 8 | - "secrets" 9 | - "secrets+gpg-import" 10 | - "secrets+gpg-import-kubernetes" 11 | - "secrets+age-import" 12 | - "secrets+age-import-kubernetes" 13 | - "secrets+literal" 14 | runtimeConfig: 15 | protocolCommands: 16 | - protocols: 17 | - "secrets" 18 | - "secrets+gpg-import" 19 | - "secrets+gpg-import-kubernetes" 20 | - "secrets+age-import" 21 | - "secrets+age-import-kubernetes" 22 | - "secrets+literal" 23 | platformCommand: 24 | - os: windows 25 | command: >- 26 | cmd.exe /D /E:ON /V:ON /C !HELM_PLUGIN_DIR!\scripts\wrapper\run.cmd 27 | args: ["downloader"] 28 | - os: linux 29 | command: "scripts/run.sh" 30 | args: ["downloader"] 31 | - os: darwin 32 | command: "scripts/run.sh" 33 | args: ["downloader"] 34 | - command: "scripts/run.sh" 35 | args: ["downloader"] 36 | -------------------------------------------------------------------------------- /examples/terraform/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Terraform template 3 | # Local .terraform directories 4 | **/.terraform/* 5 | 6 | # .tfstate files 7 | *.tfstate 8 | *.tfstate.* 9 | 10 | # Crash log files 11 | crash.log 12 | 13 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as 14 | # password, private keys, and other secrets. These should not be part of version 15 | # control as they are data points which are potentially sensitive and subject 16 | # to change depending on the environment. 17 | # 18 | *.tfvars 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Include override files you do wish to add to version control using negated pattern 28 | # 29 | # !example_override.tf 30 | 31 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 32 | # example: *tfplan* 33 | 34 | # Ignore CLI configuration files 35 | .terraformrc 36 | terraform.rc 37 | 38 | .terraform.lock.hcl 39 | 40 | -------------------------------------------------------------------------------- /tests/assets/values/noop/secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 81 23 | -------------------------------------------------------------------------------- /tests/assets/values/noop/some-secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 83 23 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.dec.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 81 23 | 24 | -------------------------------------------------------------------------------- /tests/assets/values/vals/secrets.dec.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 81 23 | 24 | -------------------------------------------------------------------------------- /tests/assets/values/sops/some-secrets.dec.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 83 23 | 24 | -------------------------------------------------------------------------------- /tests/assets/values/vals/some-secrets.dec.yaml: -------------------------------------------------------------------------------- 1 | global_secret: global_bar 2 | key: |- 3 | -----BEGIN PGP MESSAGE----- 4 | 5 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 6 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 7 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 8 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 9 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 10 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 11 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 12 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 13 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 14 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 15 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 16 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 17 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 18 | EzAA 19 | =jf7D 20 | -----END PGP MESSAGE----- 21 | service: 22 | port: 83 23 | 24 | -------------------------------------------------------------------------------- /examples/sops/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: simple-chart 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 1.16.0 24 | -------------------------------------------------------------------------------- /examples/vals/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: simple-chart 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: 1.16.0 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | ARG VERSION_HELM=3.17.3 4 | ARG VERSION_SOPS=3.10.2 5 | ARG VERSION_VALS=0.40.1 6 | ARG VERSION_KUBECTL=1.33.0 7 | 8 | SHELL ["/bin/sh", "-exc"] 9 | 10 | ENV HOME=/home/user/ 11 | 12 | RUN if [ "$(uname -m)" == "x86_64" ]; then CURL_ARCH=amd64; GO_ARCH=amd64; else CURL_ARCH="aarch64" GO_ARCH="arm64"; fi \ 13 | && apk add --no-cache gnupg curl && adduser -D user \ 14 | && wget -qO /usr/local/bin/sops https://github.com/getsops/sops/releases/download/v${VERSION_SOPS}/sops-v${VERSION_SOPS}.linux.${GO_ARCH} \ 15 | && wget -qO /usr/local/bin/kubectl https://dl.k8s.io/release/v${VERSION_KUBECTL}/bin/linux/${GO_ARCH}/kubectl \ 16 | && wget -qO - https://get.helm.sh/helm-v${VERSION_HELM}-linux-${GO_ARCH}.tar.gz | tar xzvf - -C /usr/local/bin/ --strip-components 1 "linux-${GO_ARCH}/helm" \ 17 | && wget -qO - https://github.com/variantdev/vals/releases/download/v${VERSION_VALS}/vals_${VERSION_VALS}_linux_amd64.tar.gz | tar xzf - -C /usr/local/bin/ vals \ 18 | && chmod +x /usr/local/bin/* 19 | 20 | COPY scripts/ /home/user/.local/share/helm/plugins/helm-plugins/scripts/ 21 | COPY plugin.yaml /home/user/.local/share/helm/plugins/helm-plugins/ 22 | 23 | USER user 24 | -------------------------------------------------------------------------------- /examples/sops/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "simple-chart.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "simple-chart.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /examples/vals/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "simple-chart.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 5 | apiVersion: networking.k8s.io/v1beta1 6 | {{- else -}} 7 | apiVersion: extensions/v1beta1 8 | {{- end }} 9 | kind: Ingress 10 | metadata: 11 | name: {{ $fullName }} 12 | labels: 13 | {{- include "simple-chart.labels" . | nindent 4 }} 14 | {{- with .Values.ingress.annotations }} 15 | annotations: 16 | {{- toYaml . | nindent 4 }} 17 | {{- end }} 18 | spec: 19 | {{- if .Values.ingress.tls }} 20 | tls: 21 | {{- range .Values.ingress.tls }} 22 | - hosts: 23 | {{- range .hosts }} 24 | - {{ . | quote }} 25 | {{- end }} 26 | secretName: {{ .secretName }} 27 | {{- end }} 28 | {{- end }} 29 | rules: 30 | {{- range .Values.ingress.hosts }} 31 | - host: {{ .host | quote }} 32 | http: 33 | paths: 34 | {{- range .paths }} 35 | - path: {{ . }} 36 | backend: 37 | serviceName: {{ $fullName }} 38 | servicePort: {{ $svcPort }} 39 | {{- end }} 40 | {{- end }} 41 | {{- end }} 42 | -------------------------------------------------------------------------------- /examples/backends/vault.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ "${QUIET}" = "false" ]; then 4 | log 'DEPRECATED: Vault backend is going to be remove in the next major version. Use vals backend instead.' 5 | fi 6 | 7 | _VAULT="${HELM_SECRETS_VAULT_PATH:-vault}" 8 | 9 | # shellcheck disable=SC2034 10 | _BACKEND_REGEX='!vault [A-z0-9][A-z0-9/*\.\_\-]*\#[A-z0-9*\.\_\-][A-z0-9*\.\_\-]*' 11 | 12 | # shellcheck source=scripts/lib/backends/_custom.sh 13 | . "${SCRIPT_DIR}/lib/backends/_custom.sh" 14 | 15 | _vault() { 16 | # shellcheck disable=SC2086 17 | set -- ${SECRET_BACKEND_ARGS} "$@" 18 | $_VAULT "$@" 19 | } 20 | 21 | _custom_backend_get_secret() { 22 | _type=$1 23 | _SECRET_PATH="${2%#*}" 24 | _SECRET_FIELD="${2#*#}" 25 | 26 | if [ "${_type}" != "yaml" ]; then 27 | fatal 'Only decryption of yaml files are allowed!' 28 | fi 29 | 30 | if ! _vault kv get -format="${_type}" -field="${_SECRET_FIELD}" "${_SECRET_PATH}"; then 31 | echo 'Error while get secret from vault!' >&2 32 | echo vault kv get -format="${_type}" -field="${_SECRET_FIELD}" "${_SECRET_PATH}" "${SECRET_BACKEND_ARGS}" >&2 33 | exit 1 34 | fi 35 | } 36 | 37 | _custom_backend_is_yaml() { 38 | _type=$1 39 | _SECRET_PATH="${2%#*}" 40 | _SECRET_FIELD="${2#*#}" 41 | 42 | [ "${_SECRET_FIELD}" = "data" ] 43 | } 44 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.trailing-newline.raw: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:LQ4WtOCEu4HNbsT1TfLLhxYZtP2DTgQviYPyDOtikA==,iv:on0qiP3hx7VTcPXfceYMIVKB459FiHN5I0L1dCinJ/8=,tag:2DhwIGWnHgaqdy40oRiMEw==,type:str]", 3 | "sops": { 4 | "lastmodified": "2025-09-11T11:22:41Z", 5 | "mac": "ENC[AES256_GCM,data:6P1KmBiUT1aeslyBuqQgfi8iRgD/iRFjltvwM1x+Bz0gHNlRqw4RFmohMoZe56t/97ZPCzCoV00PDO9vP/URUP7WvRVSbU+i6m+AnpF4rHDvo43P4hTYPfOhsLMhgHMYd+h+xfaND+gWag9330sqjTISxgVfwX38+ZAqnxXIUuk=,iv:lY5RdTCuqgIHEppQqH7sGQCmVDWYtAdcGIcS2oKZahI=,tag:PeoYtWEPcRDfwUmlghhmqg==,type:str]", 6 | "pgp": [ 7 | { 8 | "created_at": "2025-09-11T11:22:41Z", 9 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhQEMA9ce5qCwOO4MAQf/cKFKTQkTuATGEMSEe9PV2Q4JHeRIYASOtqpWkajG9N+w\nCdP9ppl/diOdOKrnlSciw3X2KCP7F1+VRLUBeOIOAdYAyLLODbKa9caXUzCY+jtT\neWNNj/Iwqi0a6qXjNMBXWEB9qcjg23dBpLBn3k299xRhDcbsf0nsnfz/L4Pndg0P\n5F/yd0KiS6NgkM9xd76OprsdFhNUf2XOA4QrckknIjKQKCwTaFYVTvtfEXQmDuwD\naJ8NXq8Ze/vGz//CAPZzjsG9c54XWle4ir8gW/LTc4kvq1tfk6n8Whp2WvtX6MgO\n2p3FS/Ib8B8cPOwdRUJLmtEFnNgd20ARmsFCqX2yeNJeARdFB6ACbD0AgQwU60d6\nv23dR+8Yi/l1pYpMUDIYwWLOeTRJQBooQX0neq2AvsjM/tkWvOZ30zMq0Kflm6Xm\naKmShjSe6PBn8XxHCBHsfS97VFNm9Bmz/OXQXVr1mA==\n=yVAM\n-----END PGP MESSAGE-----", 10 | "fp": "D6174A02027050E59C711075B430C4E58E2BBBA3" 11 | } 12 | ], 13 | "unencrypted_suffix": "_unencrypted", 14 | "version": "3.10.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/assets/values/sops/files/file.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "ENC[AES256_GCM,data:1qs04GPMCSKM2Wug3w==,iv:SAe4eIQY+0SvnZwy6N+NdcjkOerWuU24wlBByyr72uU=,tag:xAfXzet1gXkuzhpYzdF+2w==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": null, 9 | "lastmodified": "2022-08-14T06:22:11Z", 10 | "mac": "ENC[AES256_GCM,data:umwCz8Y6Qt9iWpluJZam+27KAC59G5bUB5oxNDnV+FpMMSAqrpHc064cEff77HMf/1McaAt3U27jwwPySZ8C4limaiE5xJ6bieuwtOB+kz6/u3unBJl5MWvbCinE6cI7n+s7ZDTb1h7iVDeZU+UpNwNO2N48iUloFuExb/YIc5Y=,iv:rmZFnXVwlNdPKfH8LGEDOlIfZ6OcTf6RCR/l2MTNL8Q=,tag:Sbr7ZuYYvJ1d0hkqbm9CNg==,type:str]", 11 | "pgp": [ 12 | { 13 | "created_at": "2022-08-13T20:42:16Z", 14 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhQEMA9ce5qCwOO4MAQgAghUd9MqKsjwexZXcNdetCEwakLpYLCw+c3bF1C8MRsxp\nzsAyDUQniYx3/Oc65gDT/zi7Stt+5ZlKaI09SL890pPLiC33GBgi6Nve3vmOp6h9\nwlyzdokl0lxtSHlN8u8+OQNpj7kAEb15K3zP0OGJi6m2SsRJsF7/KzUR0MjUaJP1\nyWaBd3ku9yDoIkEDK1wLqHeYxsZsxhOH9V7eyjkotDoOmANQE0rrT58deGS74QZ5\nUkT+QSLpinAsyoHAT3RcBTJn8ZCTNoyzI9LjW+T7B44D9KoQwp0q0FPibtwQfbV0\n+TL9FIzhbnbcVPNi+D82nawnzTAevvXQ4PBD4HygudJcAQXl88ZGuQuEj6zWfT1l\nojYHd+QqEbyC5F/w4mR6CnlZeOIypASZ54NmbKvPqs9Dgpwd3fxL2kK3jgpcg9bB\ndYSevFIaTLigMJhv7LYJdQFNtrdhsXsVtUtqDmo=\n=/JHZ\n-----END PGP MESSAGE-----\n", 15 | "fp": "D6174A02027050E59C711075B430C4E58E2BBBA3" 16 | } 17 | ], 18 | "unencrypted_suffix": "_unencrypted", 19 | "version": "3.7.3" 20 | } 21 | } -------------------------------------------------------------------------------- /examples/sops/files/file.properties: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:6j7PoCeZjD8kNotJ2X8dagD0sLs=,iv:A+0fG1AmxKO43IxRZLifdHUVr+gQJEzLW49JoIJBncY=,tag:J04pZL4q8kh9oeAgfWK+7A==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": null, 9 | "lastmodified": "2022-08-14T05:57:59Z", 10 | "mac": "ENC[AES256_GCM,data:2kZ/ILfC0kXvhuIBcPKWHvA8zJqtj68F8gSwF6ticx8N6dxz74GwPQTdNKncT5+6pI2yr4zJMJInai2yAPMH8wEvPdI9b76ZHSURCz3WXw2xIt7WrZ2ICoZTPg/UXiEZA1Zqgwtm7xiwzVOycg+ck3pesBqVs4jznidLTS5NW7s=,iv:PSggVn+lBS3Xni/TTSFFfWzoNkh9UoEpgBdQo/RsnhY=,tag:VbEaJCSaHFLeMk6ADI4gKw==,type:str]", 11 | "pgp": [ 12 | { 13 | "created_at": "2022-08-13T20:42:23Z", 14 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhQEMA9ce5qCwOO4MAQf5AeZ98asB1Dwt5pfO3MUT2eIGUsOovH++MMtj87clinE5\n1j81lAzkaezjcpGztze3iZ0PwEGmgMq+JelVa6lfhCcTtMtJjio3T59NvrTK57dJ\nW6XOZlGUY4OTLmtc6jVzegjIlf4kEWwQPJqoDXBqNSEC1iU2tpRcImZVManyq7CZ\nAGZbkfbPQHqaHAyvOcfIWC+71ZC0u9WD1QQBkP4Je3lhzc52JpO+ZUuoW1SWTpZU\nO4v9HZYoB4KvA0hyK1p7aVQ/9aIrM93/yZHKkFVuDue/4VZVRBvfqZwHcIogiwer\nYObDryhCv4kpJmYvXhxUlIZkN9Ba0xh2m+Ksc/fbW9JeAXwpxXK6soxHyQIDMqXx\n5VJF+XpnnwYpkLGbmS0CKZwxPQpOo9Za3PFIc8AkV6dC9Pk6WAc/NMlYUlziAZ7q\n009dZKJG6gutP//6eToK7r0W9n26rsv9do7+DVikrQ==\n=j8TR\n-----END PGP MESSAGE-----\n", 15 | "fp": "D6174A02027050E59C711075B430C4E58E2BBBA3" 16 | } 17 | ], 18 | "unencrypted_suffix": "_unencrypted", 19 | "version": "3.7.3" 20 | } 21 | } -------------------------------------------------------------------------------- /tests/assets/values/sops/files/file.properties: -------------------------------------------------------------------------------- 1 | { 2 | "data": "ENC[AES256_GCM,data:6j7PoCeZjD8kNotJ2X8dagD0sLs=,iv:A+0fG1AmxKO43IxRZLifdHUVr+gQJEzLW49JoIJBncY=,tag:J04pZL4q8kh9oeAgfWK+7A==,type:str]", 3 | "sops": { 4 | "kms": null, 5 | "gcp_kms": null, 6 | "azure_kv": null, 7 | "hc_vault": null, 8 | "age": null, 9 | "lastmodified": "2022-08-14T05:57:59Z", 10 | "mac": "ENC[AES256_GCM,data:2kZ/ILfC0kXvhuIBcPKWHvA8zJqtj68F8gSwF6ticx8N6dxz74GwPQTdNKncT5+6pI2yr4zJMJInai2yAPMH8wEvPdI9b76ZHSURCz3WXw2xIt7WrZ2ICoZTPg/UXiEZA1Zqgwtm7xiwzVOycg+ck3pesBqVs4jznidLTS5NW7s=,iv:PSggVn+lBS3Xni/TTSFFfWzoNkh9UoEpgBdQo/RsnhY=,tag:VbEaJCSaHFLeMk6ADI4gKw==,type:str]", 11 | "pgp": [ 12 | { 13 | "created_at": "2022-08-13T20:42:23Z", 14 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhQEMA9ce5qCwOO4MAQf5AeZ98asB1Dwt5pfO3MUT2eIGUsOovH++MMtj87clinE5\n1j81lAzkaezjcpGztze3iZ0PwEGmgMq+JelVa6lfhCcTtMtJjio3T59NvrTK57dJ\nW6XOZlGUY4OTLmtc6jVzegjIlf4kEWwQPJqoDXBqNSEC1iU2tpRcImZVManyq7CZ\nAGZbkfbPQHqaHAyvOcfIWC+71ZC0u9WD1QQBkP4Je3lhzc52JpO+ZUuoW1SWTpZU\nO4v9HZYoB4KvA0hyK1p7aVQ/9aIrM93/yZHKkFVuDue/4VZVRBvfqZwHcIogiwer\nYObDryhCv4kpJmYvXhxUlIZkN9Ba0xh2m+Ksc/fbW9JeAXwpxXK6soxHyQIDMqXx\n5VJF+XpnnwYpkLGbmS0CKZwxPQpOo9Za3PFIc8AkV6dC9Pk6WAc/NMlYUlziAZ7q\n009dZKJG6gutP//6eToK7r0W9n26rsv9do7+DVikrQ==\n=j8TR\n-----END PGP MESSAGE-----\n", 15 | "fp": "D6174A02027050E59C711075B430C4E58E2BBBA3" 16 | } 17 | ], 18 | "unencrypted_suffix": "_unencrypted", 19 | "version": "3.7.3" 20 | } 21 | } -------------------------------------------------------------------------------- /USERS.md: -------------------------------------------------------------------------------- 1 | # helm-secrets Users 2 | 3 | Who’s actually using helm-secrets? The following table is compiled from public information, survey response and PRs. It’s 4 | likely this is only a small selection of actual users, but it’s a start. 5 | 6 | If you are using helm-secrets in your company or organization, we would like to invite you to create a PR to add your 7 | information to this file. 8 | 9 | | Organization | Workload | More Info | Location | Added | 10 | |--------------|------------|-----------------------------------------------------------------------------------------------------------|----------|----------| 11 | | adorsys | production | Using helm-secrets for production deployments since 2017 | Germany | Dec 2022 | 12 | | Würth IT GmbH| production | Using helm-secrets to deploy our helm charts to our on premise open shift container platform | Germany | June 2024| 13 | | Sopra Steria | production | Using helm-secrets to deploy helm charts using ArgoCD on several platform | Europe | Jan 2025 | 14 | | Fast Forest | production | Using helm-secrets together with ArgoCD to manage the k3s cluster the team infrastructure runs on | Germany | Jun 2022 | 15 | | Dutch Public Broadcaster (NPO) | production | Using helm-secrets and vals to deploy Helm charts with parameters from AWS SSM | Netherlands | Nov 2025 | -------------------------------------------------------------------------------- /examples/sops/secrets.yaml: -------------------------------------------------------------------------------- 1 | podAnnotations: 2 | secret: ENC[AES256_GCM,data:R5pFRNs=,iv:2OF6j34rusZYulfkalXlgOs4+3M9t57R3rJwy/NJKos=,tag:SQTMAQg1HsTjdnAqeEWWnQ==,type:str] 3 | sops: 4 | kms: [] 5 | gcp_kms: [] 6 | azure_kv: [] 7 | hc_vault: [] 8 | age: [] 9 | lastmodified: "2021-10-04T16:34:43Z" 10 | mac: ENC[AES256_GCM,data:BZLaP0aV0xqU6863VULSFnFiZkDvaWNT91mBvUxbgQVjUQkPwNCtj7bFx7zRLutf684xr6Xvx7EjRc0KmA/q7w9elLpj5XP6lvHwVcw2dYwnc/nXMvfAcHQTv0Gl3Ey+PXZ7lECouciZtyKd9ib9IEMnKrwzqs4ZDPC9Y6DZitU=,iv:BQk4/siDQxkemO3g3SEGzoeuka5BmoAL/23oRT4sM60=,tag:Ef9tL6h2MNAAV7o2RI3SPg==,type:str] 11 | pgp: 12 | - created_at: "2021-10-04T16:34:43Z" 13 | enc: | 14 | -----BEGIN PGP MESSAGE----- 15 | 16 | hQEMA9ce5qCwOO4MAQf/UI8ggX3hR0ZrVeZ4j5MiYsl7O1lDAS6xWLGivRfOfy4l 17 | UYBMZi9E7LYNN47xXgbbGUJ8MXrCEp+vQR0AUqG+K/X6OPP6pmeeAlEGH0o9Fab0 18 | 0f0sU3/h9juST0RBtTDa8YTmjTglD5uAzjYNqVsYe0YLNv6HxDw6Fu/h/sXI3Ekn 19 | PCYw3E+ONjOAQWfCGgkiIQkdPmnB0kZD+bA3U+3EGSnPPljTWYyGuGyonEm4IckV 20 | AGgzhtsPWKmh/SwVa603eVD/+JvBzszyUao9JinijZHJJmcHJg6TjuOOUUlTmRbq 21 | 8Fgf3NUE3G5BQgeH1nFzLzlNYg6MVSceaUIX7vilwtJeAVHt2EIxGz6oZO0vFGYc 22 | NoACwB6FkVEp3jS4QR0wMhtaflpGtoaooc+BIWxrbf9S0XIv0RHdf33/X7vbMRsz 23 | tUw10Hsbl13DeySp+6uwoom3VVGCuisQdewoIf1ntg== 24 | =c0YW 25 | -----END PGP MESSAGE----- 26 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 27 | unencrypted_suffix: _unencrypted 28 | version: 3.7.1 29 | -------------------------------------------------------------------------------- /tests/assets/values/sops/files/file.yaml: -------------------------------------------------------------------------------- 1 | hello: ENC[AES256_GCM,data:N8Loq+4wfdlix+GXWA==,iv:uLn80DUlb9P7z+5shZeoYHN7OhUHPQxEKzD8JEa6Ipc=,tag:3u4Q+l78B9jdKlcq1IANEA==,type:str] 2 | sops: 3 | kms: [] 4 | gcp_kms: [] 5 | azure_kv: [] 6 | hc_vault: [] 7 | age: [] 8 | lastmodified: "2022-08-14T05:56:06Z" 9 | mac: ENC[AES256_GCM,data:sfmPcTAUP/5AR2YI4y80BOMM0JdPaqd1CUWtEcvG6RzzgS2kbNPVHXxckkbCm0is6exg1zn2vowR+np28cJ5apJhLIOsG22NMZ1KwIOCFKgxRQqOedgTSHd3YUPWULnHD1wL3ncPEwq7PdPoRCCqIjMl3cpn7Ch33QJZBKRX704=,iv:x9Nu70HzEf+K5fKKvYUVQfWOJ4TjAXBtqxVEQk3KRVM=,tag:eo3BkCqZ0aaxQWUnFZHyaA==,type:str] 10 | pgp: 11 | - created_at: "2022-08-13T20:42:20Z" 12 | enc: | 13 | -----BEGIN PGP MESSAGE----- 14 | 15 | hQEMA9ce5qCwOO4MAQgAuDNgJatYf2tjlX0gJgWLq6GAoUNiY+e/IJrhgMJ8xPM3 16 | 1F6wu9phTI40qgto5fZ7s0pCzS6r3hVUi/h0y83kCV7UanjzvSgszIr5j5vvu8Jv 17 | TxFbqDcc9KYBmMdnJMhdat3DnfKY2xJtDBo+KeY+PvKkD+KzT2075h5wcFr0sOT9 18 | JF7KSLxU3PbL8Rm8390u1jWECpH7744SmMir0GtV8rRRI6YBQbzjbefYVymMiwMz 19 | 7saRsX8q3nyKHCl1MrzDG5233cXqJvy9uUi+HQtOYQ/7UfCaJFneLNKdmxvJbTPh 20 | W+oeXDs6gKHtzlVzLcZeaIPCQhQcI+kICEyKo6fLd9JeATDhfsNlniXhP+0EbXce 21 | xb+/heSlqCSOp82XmwUxWH1m9fb3ykZJx39YClnDYpn6MHPmeHRt8s1Ic4vWe3QD 22 | uOqxBpTol94q+DTc9AUnRNIqKF17WdgyGPEVQiR/ig== 23 | =u9GD 24 | -----END PGP MESSAGE----- 25 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 26 | unencrypted_suffix: _unencrypted 27 | version: 3.7.3 28 | -------------------------------------------------------------------------------- /tests/unit/helm-plugin.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../bats/extensions/bats-support/load' 5 | load '../bats/extensions/bats-assert/load' 6 | load '../bats/extensions/bats-file/load' 7 | 8 | @test "helm-plugin: helm plugin list" { 9 | run "${HELM_BIN}" plugin list 10 | assert_success 11 | assert_output --partial 'secrets' 12 | } 13 | 14 | @test "helm-plugin: helm secrets" { 15 | run "${HELM_BIN}" secrets 16 | assert_failure 17 | assert_output --partial 'Available Commands:' 18 | } 19 | 20 | @test "helm-plugin: helm secrets --help" { 21 | run "${HELM_BIN}" secrets --help 22 | assert_success 23 | assert_output --partial 'Available Commands:' 24 | } 25 | 26 | @test "helm-plugin: helm secrets -v" { 27 | VERSION=$(grep version "${GIT_ROOT}/plugin.yaml" | cut -d'"' -f2) >&2 28 | 29 | run "${HELM_BIN}" secrets -v 30 | assert_success 31 | assert_output --partial "${VERSION}" 32 | } 33 | 34 | @test "helm-plugin: helm secrets --version" { 35 | VERSION=$(grep version "${GIT_ROOT}/plugin.yaml" | cut -d'"' -f2) >&2 36 | 37 | run "${HELM_BIN}" secrets --version 38 | assert_success 39 | assert_output --partial "${VERSION}" 40 | } 41 | 42 | @test "helm-plugin: helm secrets version + HELM_SECRETS_WRAPPER_ENABLED" { 43 | VERSION=$(grep version "${GIT_ROOT}/plugin.yaml" | cut -d'"' -f2) >&2 44 | 45 | run env HELM_SECRETS_WRAPPER_ENABLED=true "${GIT_ROOT}/scripts/wrapper/helm.sh" version 46 | assert_success 47 | 48 | if helm_version_greater_or_equal_than 4.0.0; then 49 | assert_output --partial "v4" 50 | else 51 | assert_output --partial "v3" 52 | fi 53 | } 54 | -------------------------------------------------------------------------------- /examples/sops/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "simple-chart.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "simple-chart.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "simple-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "simple-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /examples/vals/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "simple-chart.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "simple-chart.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "simple-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "simple-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /examples/backends/onepassword.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # The 1Password CLI (https://developer.1password.com/docs/cli) allows you to get secrets 5 | # from your vaults using secret references (https://developer.1password.com/docs/cli/secrets-reference-syntax). 6 | # Secrets can be referenced in configuration files as described 7 | # by the template syntax documentation (https://developer.1password.com/docs/cli/secrets-template-syntax). 8 | # 9 | # To use this secret backend, you need to install the 1Password CLI and sign in: 10 | # https://developer.1password.com/docs/cli/get-started 11 | # 12 | 13 | set -euf 14 | 15 | _ONEPASSWORD="${HELM_SECRETS_ONEPASSWORD_PATH:-op}" 16 | 17 | # shellcheck disable=SC2034 18 | # https://developer.1password.com/docs/cli/secrets-reference-syntax/#syntax-rules 19 | _BACKEND_REGEX='op://[A-Za-z0-9\-_./ ]*' 20 | 21 | # shellcheck source=scripts/lib/backends/_custom.sh 22 | . "${SCRIPT_DIR}/lib/backends/_custom.sh" 23 | 24 | _onepassword() { 25 | # shellcheck disable=SC2086 26 | set -- ${SECRET_BACKEND_ARGS} "$@" 27 | eval "$($_ONEPASSWORD signin)" 28 | $_ONEPASSWORD "$@" 29 | } 30 | 31 | _custom_backend_get_secret() { 32 | if [ $# -eq 1 ]; then 33 | _SECRET=$1 34 | else 35 | _SECRET=$2 36 | fi 37 | 38 | _onepassword read --force "${_SECRET}" 39 | } 40 | 41 | _custom_backend_decrypt_file() { 42 | input="${2}" 43 | # if omit then output to stdout 44 | output="${3:-}" 45 | 46 | # Templates supported by `op inject`: 47 | # https://developer.1password.com/docs/cli/secrets-template-syntax 48 | 49 | if [ "${output}" = "" ]; then 50 | _onepassword inject --force --in-file "${input}" 51 | else 52 | _onepassword inject --force --in-file "${input}" --out-file "${output}" 53 | fi 54 | } 55 | -------------------------------------------------------------------------------- /scripts/commands/encrypt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | enc_usage() { 6 | cat < 8 | 9 | Encrypt secrets 10 | 11 | It uses your gpg credentials to encrypt .yaml file. If the file is already 12 | encrypted, look for a decrypted file and encrypt that to .yaml. 13 | This allows you to first decrypt the file, edit it, then encrypt it again. 14 | 15 | You can use plain sops to encrypt - https://github.com/getsops/sops 16 | 17 | Example: 18 | $ helm secrets encrypt 19 | $ git add 20 | $ git commit 21 | $ git push 22 | 23 | EOF 24 | } 25 | 26 | encrypt_helper() { 27 | dir=$(dirname "$1") 28 | filename=$(basename "$1") 29 | inline="$2" 30 | 31 | cd "$dir" 32 | 33 | if [ ! -f "${filename}" ]; then 34 | fatal 'File does not exist: %s' "${dir}/${filename}" 35 | fi 36 | 37 | if [ "${inline}" = "true" ]; then 38 | output="${filename}" 39 | else 40 | output="" 41 | fi 42 | 43 | if backend_is_file_encrypted "${filename}"; then 44 | fatal 'Already encrypted: %s' "${filename}" 45 | fi 46 | 47 | backend_encrypt_file "yaml" "${filename}" "${output}" 48 | } 49 | 50 | encrypt() { 51 | if is_help "$1"; then 52 | enc_usage 53 | return 54 | fi 55 | 56 | inline=false 57 | 58 | argc=$# 59 | j=0 60 | 61 | while [ $j -lt $argc ]; do 62 | case "$1" in 63 | -i) 64 | inline=true 65 | ;; 66 | *) 67 | set -- "$@" "$1" 68 | ;; 69 | esac 70 | 71 | shift 72 | j=$((j + 1)) 73 | done 74 | 75 | filepath="$1" 76 | 77 | if [ ! -f "${filepath}" ]; then 78 | fatal 'File does not exist: %s' "${filepath}" 79 | fi 80 | 81 | encrypt_helper "${filepath}" "${inline}" 82 | } 83 | -------------------------------------------------------------------------------- /contrib/release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sedi() { 4 | if [ "$(uname)" = "Darwin" ]; then 5 | sed -i "" "$@" 6 | else 7 | sed -i "$@" 8 | fi 9 | } 10 | 11 | if [ $# -lt 2 ] || [[ ! "${1}" =~ ^[0-9]\.[0-9]+\.[0-9]+ ]] || [[ ! "${2}" =~ ^[0-9]\.[0-9]+\.[0-9]+$ ]]; then 12 | echo "Missing arguments." 13 | echo "$0 1.1.0 1.2.0" 14 | exit 1 15 | fi 16 | 17 | BRANCH=$(git rev-parse --abbrev-ref HEAD) 18 | if [[ "$BRANCH" != "main" ]]; then 19 | echo "Please checkout main" 20 | exit 1 21 | fi 22 | 23 | # https://stackoverflow.com/a/3278427/8087167 24 | UPSTREAM='@{u}' 25 | LOCAL=$(git rev-parse @) 26 | REMOTE=$(git rev-parse "$UPSTREAM") 27 | 28 | if [ "$LOCAL" != "$REMOTE" ]; then 29 | echo "Current branch is no up to date with origin. Please pull or push" 30 | exit 1 31 | fi 32 | 33 | sedi "s/version:.*/version: \"${1}\"/" "$(git rev-parse --show-toplevel)/plugin.yaml" 34 | sedi "s/version:.*/version: \"${1}\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-cli/plugin.yaml" 35 | sedi "s/version:.*/version: \"${1}\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-getter/plugin.yaml" 36 | sedi "s/version:.*/version: \"${1}\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-post-renderer/plugin.yaml" 37 | git commit -am "Release v${1}" 38 | git tag --annotate -m "Release v${1}" "v${1}" 39 | 40 | sedi "s/version:.*/version: \"${2}-dev\"/" "$(git rev-parse --show-toplevel)/plugin.yaml" 41 | sedi "s/version:.*/version: \"${2}-dev\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-cli/plugin.yaml" 42 | sedi "s/version:.*/version: \"${2}-dev\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-getter/plugin.yaml" 43 | sedi "s/version:.*/version: \"${2}-dev\"/" "$(git rev-parse --show-toplevel)/plugins/helm-secrets-post-renderer/plugin.yaml" 44 | git commit -am "Set next version" 45 | 46 | git push --follow-tags --atomic 47 | -------------------------------------------------------------------------------- /scripts/commands/help.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | help_usage() { 6 | cat <<'EOF' 7 | 8 | helm-secrets is a helm plugin for decrypt encrypted helm value files on the fly. 9 | 10 | For more information, see the README.md at https://github.com/jkroepke/helm-secrets 11 | 12 | To decrypt/encrypt/edit locally you need to initialize/first encrypt secrets with 13 | sops - https://github.com/getsops/sops 14 | 15 | Available Commands: 16 | encrypt Encrypt secrets file 17 | decrypt Decrypt secrets file 18 | edit Edit secrets file and encrypt afterwards 19 | dir Get plugin directory 20 | wrapper that decrypts encrypted yaml files before running helm 21 | 22 | Available Options: 23 | --quiet -q Suppress info messages (env: $HELM_SECRETS_QUIET) 24 | --backend -b Secret backend to use for decryption or encryption (env: $HELM_SECRETS_BACKEND) 25 | --backend-args -a Additional args for secret backend (env: $HELM_SECRETS_BACKEND_ARGS) 26 | --ignore-missing-values [true|false] Ignore missing value files (env: $HELM_SECRETS_IGNORE_MISSING_VALUES) 27 | --evaluate-templates [true|false] Evaluate secret expressions inside helm template (only supported by vals backend) (env: $HELM_SECRETS_EVALUATE_TEMPLATES) 28 | --evaluate-templates-decode-secrets [true|false] If --evaluate-templates is set, decode base64 values from secrets to evaluate them (env: $HELM_SECRETS_EVALUATE_TEMPLATES_DECODE_SECRETS) 29 | --decrypt-secrets-in-tmp-dir [true|false] Decrypt secrets in a temp directory. May solve concurrency issues. (env: $HELM_SECRETS_DECRYPT_SECRETS_IN_TMP_DIR) 30 | --help -h Show help 31 | --version -v Display version of helm-secrets 32 | EOF 33 | } 34 | -------------------------------------------------------------------------------- /examples/vals/secrets.sops.yaml: -------------------------------------------------------------------------------- 1 | podAnnotations: 2 | secret: ENC[AES256_GCM,data:ky/H5qo=,iv:HBO2U9E9+QsC7VvSSM3XmvYXOHW+A5UqjBd6HGF1eVQ=,tag:EGh2niWg5whUZ8XhLFwMXw==,type:str] 3 | sops: 4 | kms: [] 5 | gcp_kms: [] 6 | azure_kv: [] 7 | lastmodified: '2020-05-06T20:49:45Z' 8 | mac: ENC[AES256_GCM,data:YbpCUV+Olo5ybb8QNf+BXZaryyNh+mfe6sxAGs/yQIauJejBZE4DvG6Tdaul2QaazaNEe7jdg3e1qjkRZMH6/CQLCNk1hJ6cAy+Wws379C9uczN0fksBSjV8OCR0ua8FcTSwZ6daUqaUohTEqK3BEpnQmD5o1XcaACDOhMcqJ5Q=,iv:jQFmuea50nmMxhe16ayNIQc8fxeVjO+onyQsASZ77lM=,tag:yG2J4s+3qTTjBTyo3/JTYw==,type:str] 9 | pgp: 10 | - created_at: '2020-05-06T20:49:44Z' 11 | enc: |- 12 | -----BEGIN PGP MESSAGE----- 13 | 14 | wcFMAxYpv4YXKfBAARAAgHaPKFVKMObPsRWix3ZKmTjLG+qgzIEZzHf3IpVppLwr 15 | jhDqv7YNlpsdQZyZFcjJXgI7ov3Vs0S83ZfTtlfYMeEND0EE5BGRtA+j79SmR57H 16 | dxeu/k+R1Za83ZjNOqDEnAy4DDoumaZ4J3cDoa5+e0fmAjRasUu6A+Ksnj4Wa4js 17 | CNTKgha78LzXEoL0lTUaoTnL06unUzk9SPXN8evf5/Zn9SMZiHq4fV+JlCiGPcm7 18 | EUe4Wu3MctmbwdGNJ8heQ/wbIs47JeeCTtBLs/8eA31JMXkmPgBvn3n25aoaRRuJ 19 | Gx3WJK6e8tOq7XvWpgRitbj/RlhaRfroGjc0n6EI5b9BVdc8PhSjnqQOWRyqERi3 20 | i+eeCsUW9F7UMUUnT0KmnO8q8J6occWmhdUfWPoWBxBljlRQ28M3juBH2xAwtOIy 21 | nb9rzhefwAzB6ALN1daEjOaWL0Rb+I8BCl/keW7qOw0CNyK5CUkbLwSjY0P9ssTg 22 | pfqD0z5+/9sWEBHR5n0aiSXx1KkoyN92xu5tLkPoTVRWH5fBvHTieA/uXqCvGqDg 23 | 9utfJZzYQP6PDW+FHwbQOepkmfEqVu2tWetk8ZYMxGjlW52HAnwQvnQmPDGF0/ZK 24 | 5CFUsxR0IEAadBAqymLTOoMjzma18lfv4/7YsfQVuR7kkhtyC20sB9EFY1aOJIXS 25 | 4AHku4irpzIM9eHTmSSj4kMl8+F/B+C/4GHh6PPgSuJPwZNq4EflBlI4oaGTSxFF 26 | R1owBOlCFf5YXz9VOMTPTdZZtOozkW/gLOSWfc9SgaICPaDQxbPtY/Dd4nseXlrh 27 | v6UA 28 | =8Xmb 29 | -----END PGP MESSAGE----- 30 | fp: 4434EA5D05F10F59D0DF7399AF1D073646ED4927 31 | unencrypted_suffix: _unencrypted 32 | version: 3.5.0 33 | -------------------------------------------------------------------------------- /scripts/lib/backends/vals.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | _VALS="${HELM_SECRETS_VALS_PATH:-vals}" 6 | 7 | _vals() { 8 | # shellcheck disable=SC2086 9 | set -- "$@" ${SECRET_BACKEND_ARGS} 10 | 11 | # In case of an error, give us stderr 12 | # https://github.com/variantdev/vals/issues/60 13 | # Store stderr in a var - https://stackoverflow.com/a/52587939 14 | if ! { error=$({ $_VALS "$@" 1>&3; } 2>&1); } 3>&1; then 15 | fatal "vals error: $error" 16 | fi 17 | } 18 | 19 | _vals_backend_is_file_encrypted() { 20 | _vals_backend_is_encrypted <"${1}" 21 | } 22 | 23 | _vals_backend_is_encrypted() { 24 | grep -q 'ref+' - 25 | } 26 | 27 | _vals_backend_encrypt_file() { 28 | fatal "Encrypting files is not supported!" 29 | } 30 | 31 | _vals_backend_decrypt_file() { 32 | type="${1}" 33 | input="${2}" 34 | # if omit then output to stdout 35 | output="${3:-}" 36 | 37 | if [ "${type}" = "auto" ]; then 38 | type=$(_vals_get_type "${input}") 39 | fi 40 | 41 | if [ "${input}" = "${output}" ]; then 42 | fatal "vals: inline decryption is not supported!" 43 | elif [ "${input}" = "-" ]; then 44 | _vals eval -o "${type}" 45 | elif [ "${output}" = "" ]; then 46 | _vals eval -o "${type}" <"${input}" 47 | else 48 | _vals eval -o "${type}" <"${input}" >"${output}" 49 | fi 50 | } 51 | 52 | _vals_backend_decrypt_literal() { 53 | if printf '%s' "${1}" | _vals_backend_is_encrypted; then 54 | if ! value="$(_vals get "${1}")"; then 55 | return 1 56 | fi 57 | 58 | printf '%s' "${value}" 59 | else 60 | printf '%s' "${1}" 61 | fi 62 | } 63 | 64 | _vals_backend_edit_file() { 65 | fatal "vals: Editing files is not supported!" 66 | } 67 | 68 | _vals_get_type() { 69 | file_type=$(_file_get_extension "${1}") 70 | if [ "${file_type}" = "json" ]; then 71 | echo "json" 72 | else 73 | echo "yaml" 74 | fi 75 | } 76 | -------------------------------------------------------------------------------- /docs/Security in shared environments.md: -------------------------------------------------------------------------------- 1 | # Security in shared environments 2 | 3 | By default, helm-secrets follow symlinks or accept relative paths for values. In shared environments like Jenkins or ArgoCD, is can be a problem. 4 | Users can use helm-secrets to gain access to other files outside their own build directory. 5 | 6 | To restrict access to file outside the build directory, certain restrictions can be enabled to prevent illegal access to other files. 7 | 8 | # Configurable restrictions 9 | 10 | ## Disable symlinks 11 | 12 | Symlinks can be abused to gain access to file outside the directory. To prevent this, set the environment variable 13 | 14 | ```bash 15 | HELM_SECRETS_VALUES_ALLOW_SYMLINKS=false 16 | ``` 17 | 18 | to through an error, if a referenced value file is a symlink. 19 | 20 | ## Disable absolute paths for value files 21 | 22 | Throw an error, if the path of the value file is an absolute path. To prevent this, set the environment variable 23 | 24 | ```bash 25 | HELM_SECRETS_VALUES_ALLOW_ABSOLUTE_PATH=false 26 | ``` 27 | 28 | ## Disable path traversal 29 | 30 | A path traversal attack (also known as directory traversal) aims to access files and directories that are stored outside the web root folder. 31 | By manipulating variables that reference files with “dot-dot-slash (../)” sequences and its variations or by using absolute file paths, it may be possible 32 | to access arbitrary files and directories stored on file system including application source code or configuration and critical system files. 33 | 34 | To prevent this, set the environment variable 35 | 36 | ```bash 37 | HELM_SECRETS_VALUES_ALLOW_PATH_TRAVERSAL=false 38 | ``` 39 | 40 | ## Key Location prefix 41 | 42 | The ArgoCD integration required to mount the gpg/age keys on a specific location. By default, helm-secrets accept all locations of a gpg key. To restrict 43 | the locations of keys, set an environment variable 44 | 45 | ```bash 46 | HELM_SECRETS_KEY_LOCATION_PREFIX=/secrets/ 47 | ``` 48 | 49 | To allow only gpg/age keys from the path `/secrets/`. 50 | -------------------------------------------------------------------------------- /examples/sops/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "simple-chart.fullname" . }} 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "simple-chart.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "simple-chart.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "simple-chart.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | ports: 37 | - name: http 38 | containerPort: 80 39 | protocol: TCP 40 | livenessProbe: 41 | httpGet: 42 | path: / 43 | port: http 44 | readinessProbe: 45 | httpGet: 46 | path: / 47 | port: http 48 | resources: 49 | {{- toYaml .Values.resources | nindent 12 }} 50 | {{- with .Values.nodeSelector }} 51 | nodeSelector: 52 | {{- toYaml . | nindent 8 }} 53 | {{- end }} 54 | {{- with .Values.affinity }} 55 | affinity: 56 | {{- toYaml . | nindent 8 }} 57 | {{- end }} 58 | {{- with .Values.tolerations }} 59 | tolerations: 60 | {{- toYaml . | nindent 8 }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /examples/vals/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "simple-chart.fullname" . }} 5 | labels: 6 | {{- include "simple-chart.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "simple-chart.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "simple-chart.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "simple-chart.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | ports: 37 | - name: http 38 | containerPort: 80 39 | protocol: TCP 40 | livenessProbe: 41 | httpGet: 42 | path: / 43 | port: http 44 | readinessProbe: 45 | httpGet: 46 | path: / 47 | port: http 48 | resources: 49 | {{- toYaml .Values.resources | nindent 12 }} 50 | {{- with .Values.nodeSelector }} 51 | nodeSelector: 52 | {{- toYaml . | nindent 8 }} 53 | {{- end }} 54 | {{- with .Values.affinity }} 55 | affinity: 56 | {{- toYaml . | nindent 8 }} 57 | {{- end }} 58 | {{- with .Values.tolerations }} 59 | tolerations: 60 | {{- toYaml . | nindent 8 }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /examples/sops/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "simple-chart.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "simple-chart.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "simple-chart.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "simple-chart.labels" -}} 38 | helm.sh/chart: {{ include "simple-chart.chart" . }} 39 | {{ include "simple-chart.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "simple-chart.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "simple-chart.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end }} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "simple-chart.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create }} 59 | {{- default (include "simple-chart.fullname" .) .Values.serviceAccount.name }} 60 | {{- else }} 61 | {{- default "default" .Values.serviceAccount.name }} 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /examples/sops/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for simple-chart. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: nginx 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart version. 11 | tag: "" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Annotations to add to the service account 21 | annotations: {} 22 | # The name of the service account to use. 23 | # If not set and create is true, a name is generated using the fullname template 24 | name: "" 25 | 26 | podAnnotations: {} 27 | 28 | podSecurityContext: {} 29 | # fsGroup: 2000 30 | 31 | securityContext: {} 32 | # capabilities: 33 | # drop: 34 | # - ALL 35 | # readOnlyRootFilesystem: true 36 | # runAsNonRoot: true 37 | # runAsUser: 1000 38 | 39 | service: 40 | type: ClusterIP 41 | port: 80 42 | 43 | ingress: 44 | enabled: false 45 | annotations: {} 46 | # kubernetes.io/ingress.class: nginx 47 | # kubernetes.io/tls-acme: "true" 48 | hosts: 49 | - host: chart-example.local 50 | paths: [] 51 | tls: [] 52 | # - secretName: chart-example-tls 53 | # hosts: 54 | # - chart-example.local 55 | 56 | resources: {} 57 | # We usually recommend not to specify default resources and to leave this as a conscious 58 | # choice for the user. This also increases chances charts run on environments with little 59 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 60 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 61 | # limits: 62 | # cpu: 100m 63 | # memory: 128Mi 64 | # requests: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | 68 | autoscaling: 69 | enabled: false 70 | minReplicas: 1 71 | maxReplicas: 100 72 | targetCPUUtilizationPercentage: 80 73 | # targetMemoryUtilizationPercentage: 80 74 | 75 | nodeSelector: {} 76 | 77 | tolerations: [] 78 | 79 | affinity: {} 80 | -------------------------------------------------------------------------------- /examples/vals/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "simple-chart.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 7 | {{- end }} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "simple-chart.fullname" -}} 15 | {{- if .Values.fullnameOverride }} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 17 | {{- else }} 18 | {{- $name := default .Chart.Name .Values.nameOverride }} 19 | {{- if contains $name .Release.Name }} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 21 | {{- else }} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "simple-chart.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 32 | {{- end }} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "simple-chart.labels" -}} 38 | helm.sh/chart: {{ include "simple-chart.chart" . }} 39 | {{ include "simple-chart.selectorLabels" . }} 40 | {{- if .Chart.AppVersion }} 41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 42 | {{- end }} 43 | app.kubernetes.io/managed-by: {{ .Release.Service }} 44 | {{- end }} 45 | 46 | {{/* 47 | Selector labels 48 | */}} 49 | {{- define "simple-chart.selectorLabels" -}} 50 | app.kubernetes.io/name: {{ include "simple-chart.name" . }} 51 | app.kubernetes.io/instance: {{ .Release.Name }} 52 | {{- end }} 53 | 54 | {{/* 55 | Create the name of the service account to use 56 | */}} 57 | {{- define "simple-chart.serviceAccountName" -}} 58 | {{- if .Values.serviceAccount.create }} 59 | {{- default (include "simple-chart.fullname" .) .Values.serviceAccount.name }} 60 | {{- else }} 61 | {{- default "default" .Values.serviceAccount.name }} 62 | {{- end }} 63 | {{- end }} 64 | -------------------------------------------------------------------------------- /examples/vals/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for simple-chart. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: nginx 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart version. 11 | tag: "" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Annotations to add to the service account 21 | annotations: {} 22 | # The name of the service account to use. 23 | # If not set and create is true, a name is generated using the fullname template 24 | name: "" 25 | 26 | podAnnotations: {} 27 | 28 | podSecurityContext: {} 29 | # fsGroup: 2000 30 | 31 | securityContext: {} 32 | # capabilities: 33 | # drop: 34 | # - ALL 35 | # readOnlyRootFilesystem: true 36 | # runAsNonRoot: true 37 | # runAsUser: 1000 38 | 39 | service: 40 | type: ClusterIP 41 | port: 80 42 | 43 | ingress: 44 | enabled: false 45 | annotations: {} 46 | # kubernetes.io/ingress.class: nginx 47 | # kubernetes.io/tls-acme: "true" 48 | hosts: 49 | - host: chart-example.local 50 | paths: [] 51 | tls: [] 52 | # - secretName: chart-example-tls 53 | # hosts: 54 | # - chart-example.local 55 | 56 | resources: {} 57 | # We usually recommend not to specify default resources and to leave this as a conscious 58 | # choice for the user. This also increases chances charts run on environments with little 59 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 60 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 61 | # limits: 62 | # cpu: 100m 63 | # memory: 128Mi 64 | # requests: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | 68 | autoscaling: 69 | enabled: false 70 | minReplicas: 1 71 | maxReplicas: 100 72 | targetCPUUtilizationPercentage: 80 73 | # targetMemoryUtilizationPercentage: 80 74 | 75 | nodeSelector: {} 76 | 77 | tolerations: [] 78 | 79 | affinity: {} 80 | -------------------------------------------------------------------------------- /scripts/lib/backend.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | # Define the allowed secret backends 6 | ALLOWED_BACKENDS="${HELM_SECRETS_ALLOWED_BACKENDS:-}" 7 | 8 | # shellcheck source=scripts/lib/backends/noop.sh 9 | . "${SCRIPT_DIR}/lib/backends/noop.sh" 10 | 11 | # shellcheck source=scripts/lib/backends/sops.sh 12 | . "${SCRIPT_DIR}/lib/backends/sops.sh" 13 | 14 | # shellcheck source=scripts/lib/backends/vals.sh 15 | . "${SCRIPT_DIR}/lib/backends/vals.sh" 16 | 17 | is_secret_backend() { 18 | [ -f "${SCRIPT_DIR}/lib/backends/${1}.sh" ] || [ -f "${1}" ] 19 | } 20 | 21 | load_secret_backend() { 22 | backend="${1}" 23 | 24 | if [ "${backend}" = "" ]; then 25 | return 26 | fi 27 | 28 | if [ "${ALLOWED_BACKENDS}" != "" ]; then 29 | case "${ALLOWED_BACKENDS}" in 30 | "${backend}" | "${backend},"* | *",${backend}" | *",${backend},"*) ;; 31 | *) 32 | fatal "secret backend '%s' not allowed" "${1}" 33 | ;; 34 | esac 35 | fi 36 | 37 | if [ -f "${SCRIPT_DIR}/lib/backends/${backend}.sh" ]; then 38 | # shellcheck disable=SC2034 39 | SECRET_BACKEND="${backend}" 40 | return 41 | fi 42 | 43 | # Allow to load out of tree backends. 44 | if [ ! -f "${backend}" ]; then 45 | fatal "Can't find secret backend: %s" "${backend}" 46 | fi 47 | 48 | # shellcheck disable=SC2034 49 | SECRET_BACKEND="custom" 50 | 51 | # shellcheck disable=SC2034 52 | HELM_SECRETS_SCRIPT_DIR="${SCRIPT_DIR}" 53 | 54 | # shellcheck source=tests/assets/custom-backend.sh 55 | . "${1}" 56 | } 57 | 58 | backend_is_file_encrypted() { 59 | _"${SECRET_BACKEND}"_backend_is_file_encrypted "$@" 60 | } 61 | 62 | backend_is_encrypted() { 63 | _"${SECRET_BACKEND}"_backend_is_encrypted "$@" 64 | } 65 | 66 | backend_encrypt_file() { 67 | _"${SECRET_BACKEND}"_backend_encrypt_file "$@" 68 | } 69 | 70 | backend_decrypt_file() { 71 | _"${SECRET_BACKEND}"_backend_decrypt_file "$@" 72 | } 73 | 74 | backend_decrypt_literal() { 75 | _"${SECRET_BACKEND}"_backend_decrypt_literal "$@" 76 | } 77 | 78 | backend_edit_file() { 79 | _"${SECRET_BACKEND}"_backend_edit_file "$@" 80 | } 81 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.yaml: -------------------------------------------------------------------------------- 1 | name: ✨ Enhancement / Feature / Task 2 | description: Some feature is missing or incomplete. 3 | labels: [enhancement] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: |- 8 | I would appreciate it if you could add yourself to the 9 | [USERS.md](https://github.com/jkroepke/helm-secrets/blob/main/USERS.md) file. 10 | - type: textarea 11 | attributes: 12 | label: Problem Statement 13 | description: Without specifying a solution, describe what the project is missing today. 14 | placeholder: | 15 | The rotating project logo has a fixed size and color. 16 | There is no way to make it larger and more shiny. 17 | validations: 18 | required: false 19 | - type: textarea 20 | attributes: 21 | label: Proposed Solution 22 | description: Describe the proposed solution to the problem above. 23 | placeholder: | 24 | - Implement 2 new flags CLI: ```--logo-color=FFD700``` and ```--logo-size=100``` 25 | - Let these flags control the size of the rotating project logo. 26 | validations: 27 | required: false 28 | - type: textarea 29 | attributes: 30 | label: Environment 31 | description: | 32 | examples: 33 | - **Helm Version**: 3.7.1 34 | - **Helm Secrets Version**: 3.12.0 35 | - **OS*: Alpine 36 | - **Shell*: busybox 37 | value: | 38 | - Helm Version: 39 | - Helm Secrets Version: 40 | - OS: 41 | - Shell: 42 | validations: 43 | required: false 44 | - type: textarea 45 | attributes: 46 | label: Additional information 47 | placeholder: | 48 | We considered adjusting the logo size to the phase of the moon, but there was no 49 | reliable data source in air-gapped environments. 50 | validations: 51 | required: false 52 | - type: textarea 53 | attributes: 54 | label: Acceptance Criteria 55 | placeholder: | 56 | - [ ] As a user, I can control the size of the rotating logo using a CLI flag. 57 | - [ ] As a user, I can control the color of the rotating logo using a CLI flag. 58 | - [ ] Defaults are reasonably set. 59 | - [ ] New settings are appropriately documented. 60 | - [ ] No breaking change for current users of the rotating logo feature. 61 | validations: 62 | required: false 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug 2 | description: Something is not working as indended. 3 | labels: [bug] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: |- 8 | > [!NOTE] 9 | > If you get the warning `WARNING: both 'platformCommand' and 'command' are set`, please update helm-secrets first! 10 | 11 | Thanks for taking the time to fill out this bug report! 12 | 13 | I would appreciate it if you could add yourself to the 14 | [USERS.md](https://github.com/jkroepke/helm-secrets/blob/main/USERS.md) file. 15 | - type: textarea 16 | attributes: 17 | label: Current Behavior 18 | description: A concise description of what you're experiencing. 19 | placeholder: | 20 | When I do , happens and I see the error message attached below: 21 | ```...``` 22 | validations: 23 | required: false 24 | - type: textarea 25 | attributes: 26 | label: Expected Behavior 27 | description: A concise description of what you expected to happen. 28 | placeholder: When I do , should happen instead. 29 | validations: 30 | required: false 31 | - type: textarea 32 | attributes: 33 | label: Steps To Reproduce 34 | description: Steps to reproduce the behavior. 35 | placeholder: | 36 | 1. In this environment... 37 | 2. With this config... 38 | 3. Run '...' 39 | 4. See error... 40 | render: markdown 41 | validations: 42 | required: false 43 | - type: textarea 44 | attributes: 45 | label: Environment 46 | description: | 47 | examples: 48 | - **Helm Version**: 3.10.1 49 | - **Helm Secrets Version**: 4.2.0 50 | - **ArgoCD Version (if used):**: 2.5.1 51 | - **OS**: Alpine 52 | - **Shell**: busybox 53 | value: | 54 | - Helm Version: 55 | - Helm Secrets Version: 56 | - ArgoCD Version 57 | - OS: 58 | - Shell: 59 | validations: 60 | required: false 61 | - type: textarea 62 | attributes: 63 | label: Anything else? 64 | description: | 65 | Links? References? Anything that will give us more context about the issue you are encountering! 66 | 67 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 68 | validations: 69 | required: false 70 | -------------------------------------------------------------------------------- /plugins/helm-secrets-cli/plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | type: cli/v1 3 | name: "secrets" 4 | version: "4.8.0-dev" 5 | runtime: subprocess 6 | config: 7 | usage: secrets 8 | shortHelp: helm-secrets is a helm plugin for decrypt encrypted helm value files on the fly. 9 | longHelp: | 10 | helm-secrets is a helm plugin for decrypt encrypted helm value files on the fly. 11 | 12 | For more information, see the README.md at https://github.com/jkroepke/helm-secrets 13 | 14 | To decrypt/encrypt/edit locally you need to initialize/first encrypt secrets with 15 | sops - https://github.com/getsops/sops 16 | 17 | Available Commands: 18 | encrypt Encrypt secrets file 19 | decrypt Decrypt secrets file 20 | edit Edit secrets file and encrypt afterwards 21 | dir Get plugin directory 22 | wrapper that decrypts encrypted yaml files before running helm 23 | 24 | Available Options: 25 | --quiet -q Suppress info messages (env: $HELM_SECRETS_QUIET) 26 | --backend -b Secret backend to use for decryption or encryption (env: $HELM_SECRETS_BACKEND) 27 | --backend-args -a Additional args for secret backend (env: $HELM_SECRETS_BACKEND_ARGS) 28 | --ignore-missing-values [true|false] Ignore missing value files (env: $HELM_SECRETS_IGNORE_MISSING_VALUES) 29 | --evaluate-templates [true|false] Evaluate secret expressions inside helm template (only supported by vals backend) (env: $HELM_SECRETS_EVALUATE_TEMPLATES) 30 | --evaluate-templates-decode-secrets [true|false] If --evaluate-templates is set, decode base64 values from secrets to evaluate them (env: $HELM_SECRETS_EVALUATE_TEMPLATES_DECODE_SECRETS) 31 | --decrypt-secrets-in-tmp-dir [true|false] Decrypt secrets in a temp directory. May solve concurrency issues. (env: $HELM_SECRETS_DECRYPT_SECRETS_IN_TMP_DIR) 32 | --help -h Show help 33 | --version -v Display version of helm-secrets 34 | runtimeConfig: 35 | platformCommand: 36 | - os: windows 37 | command: >- 38 | cmd.exe /D /E:ON /V:ON /C !HELM_PLUGIN_DIR!\scripts\wrapper\run.cmd 39 | - os: linux 40 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 41 | - os: darwin 42 | command: "$HELM_PLUGIN_DIR/scripts/run.sh" 43 | - command: "$HELM_PLUGIN_DIR/scripts/run.sh" 44 | -------------------------------------------------------------------------------- /tests/lib/helper.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export WSLENV="${WSLENV:-}" 4 | 5 | setup() { 6 | { 7 | # shellcheck disable=SC2164 8 | cd "${TEST_ROOT}" 9 | 10 | TEST_TEMP_DIR="${BATS_TEST_TMPDIR}" 11 | export TEST_TEMP_DIR 12 | 13 | # copy assets 14 | cp -rP "${TEST_ROOT}/assets" "${TEST_TEMP_DIR}/" 15 | if on_windows; then 16 | # shellcheck disable=SC2016 17 | SPECIAL_CHAR_DIR="${TEST_TEMP_DIR}/$(printf '%s' 'a b')" 18 | else 19 | # shellcheck disable=SC2016 20 | SPECIAL_CHAR_DIR="${TEST_TEMP_DIR}/$(printf '%s' 'a@b§c!d\$e \f(g)h=i^j😀')" 21 | fi 22 | 23 | mkdir "${SPECIAL_CHAR_DIR}" 24 | cp -rP "${TEST_ROOT}/assets" "${SPECIAL_CHAR_DIR}/" 25 | 26 | ln -s "${TEST_ROOT}/assets/values/sops/.sops.yaml" "${TEST_TEMP_DIR}" 27 | } >&2 28 | } 29 | 30 | teardown() { 31 | { 32 | # https://stackoverflow.com/a/13864829/8087167 33 | if [ -n "${RELEASE+x}" ]; then 34 | "${HELM_BIN}" del "${RELEASE}" || true 35 | fi 36 | } >&2 37 | } 38 | 39 | create_chart() { 40 | { 41 | if [ "${2:-}" == "false" ]; then 42 | cp -r "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}" "${1}/chart" 43 | else 44 | ln -s "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}" "${1}/chart" 45 | fi 46 | } >&2 47 | } 48 | 49 | is_backend() { 50 | [[ "${HELM_SECRETS_BACKEND}" == "${1}" ]] 51 | } 52 | 53 | is_custom_backend() { 54 | [[ "${HELM_SECRETS_CUSTOM_BACKEND}" == "custom-${1}" ]] 55 | } 56 | 57 | on_windows() { 58 | # shellcheck disable=SC2154 59 | ! [[ "${_uname}" == "Darwin" || "${_uname}" == "Linux" ]] || on_wsl 60 | } 61 | 62 | on_linux() { 63 | [[ "${_uname}" == "Linux" ]] 64 | } 65 | 66 | on_wsl() { 67 | [[ -f /proc/version ]] && grep -qi microsoft /proc/version 68 | } 69 | 70 | on_cygwin() { 71 | [[ "${_uname}" == "CYGWIN"* ]] 72 | } 73 | 74 | helm_version() { 75 | "${HELM_BIN}" version --short | sed 's/[^0-9.]*\([0-9.]*\).*/\1/' 76 | } 77 | 78 | helm_version_less_than() { 79 | [ "$(printf '%s\n%s\n' "$(helm_version)" "$1" | sort -V | head -n1)" != "$1" ] 80 | } 81 | 82 | helm_version_greater_or_equal_than() { 83 | [ "$(printf '%s\n%s\n' "$(helm_version)" "$1" | sort -V | head -n1)" == "$1" ] 84 | } 85 | 86 | _winpath() { 87 | if on_wsl; then 88 | touch "${1}" 89 | printf '%s' "$(wslpath -w "${1}")" 90 | elif on_cygwin; then 91 | touch "${1}" 92 | printf '%s' "$(cygpath -w -l "${1}")" 93 | else 94 | printf '%s' "${1}" 95 | fi 96 | } 97 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.age.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:8J9WPHdNUszCZQ==,iv:4/cZkfTgRrIZ29wybSHbe+6v0Xi5rsvqCcezesYfFEs=,tag:xrGJHNZVKxCuqTjXsU+Yow==,type:str] 2 | key: ENC[AES256_GCM,data:gXs/PWsVPICrYYDZOBIDuc1PUqlVnGm8pVQ5UFPhbQ/BdntoIyX4sCtbdBOEyGqvbmkLEh++1NyWsNJdkY0KTA0u1BOt7Th+AhUeWjmjOpFWsbcji/GseDHDL4il2cEM81OQn2zOvC9Nm+AVvWeWJDAN7VQX9ynvI5ccYCa8RJDnl7iOryTKTdV9U6biFH/r6j3cRAvuEvbTAsCpMB8dNqeWVOSkhaKWkpOMHE5yfmeG7RBXD5WlY7XZNf8brKL2KoQdmr3yX+HFy0K0neROmyguK66hiNKSmUdXcXpUBp6KLFoULGXwNdI8WoGpfHeivOSVKwV/vwQf8igN/OOQuxNwcTwKFzlhcrLEc36KIqEtlXagN88uWTJayHgNzoqAaarkl54vu9ufw4x5IpGWpfJSDoPLfTmp9Y/E+Q2tN9b7rKJpdbkK20M2bURBulMKPJCBdK7ETh7p1ReP/GbCe5O6wNbHd01S6SKlBpqlkasLqz/2UCq9b7aPWHISTpBnMw+r+Q8sqqlsj0j2KgC8h0qdG6E9uwVlz+P0XWpu/t8Ss9D7mn6QNAF9sRL7LxjF2r6L7v1odUOpFB0hqEN4p2Nv42j6uQdpTor5nayYTbUISSb6xOO7zLOI/j1dssju3gfZVOjeXxApclFnE5O2ifl5YxcEqxRFDNEukuXd0GKIxZYkBTADJfmE7A2K0YELcTJdaHTgS2zqapPSiPlemxgBEKwIJZXw9Eflh0F1adcxn/bwU14v/38atDZ+tXuL3UIOL0AIjZcr1FI+RmorYr8lgGBlylNWYMjXWkHqdpqAeUNlw13ZDgm5eE4oiS7qbUZwJSP4JhTspvdI6yLvYQA/4bgdbhqK732KFIYyFpK0jUmU2kfy2wIMSfcDwmMb1CL+12WmnAE2nRVsRPzALrnNH4jZSrkF5d+juUO//K7bMNBSrlq1gHqT+KcnVC3EE5gEcuNeyuxVlJURLTmrnOnB+8WE+04ZRh+ez5/EiVhisUf9TZkd+d/hh0BhMt3wSsLxWriFZ3R6R766PHFYPE8TOHpKBxudLxXMEHuDOtPZtZYChHCHfhw/BpHJGcsv4m98iq8fyDITb9GA8NW/GwppZrontnOcBW3I+wa2kLNseRccuKx0hzx0vXescN2+0AQWVAg53zXcFpgHENcXG5Ey0zab7CHcyDGYWPabVSGbW4tqcl7gUCnAx7Yapg==,iv:Iwz2S9gpSerlXEIu4hmby0+anIWA63JI8EBxrfA+DVM=,tag:PlYpI2qZ7t+TX8QPELNbVw==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:RPI=,iv:Hwx1wfPCuwXVn6kI2QX2oA5aN0uPdmIQh01hMxZI0bI=,tag:82MzqSJ4hPR9JEa8o5e5EA==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: 11 | - recipient: age1rh2xsjg8avja92j3jhtttuczgmqkz89lezxt4e3mmp5ukl5j4f9smvteju 12 | enc: | 13 | -----BEGIN AGE ENCRYPTED FILE----- 14 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCcVBZcXpuVnRjeFhEWHBG 15 | NS9ZTURBdmxLemN0RXV6eXJhTWl1Zm91d25NCjlBeTFEU3pOSnorZmMzbGNnQlpO 16 | ak5VQUtVU0QrbzNKS1o1dXB4TzdjTXcKLS0tIDFkQzVUTzdJZHYrc2QvRndZeFg3 17 | TlJpd0RKdlJOeXZKZnBSaHAzMDIrVzgK+RODCF5u7C/Y+OrgVzEhZtHfuVolKXEo 18 | uKsWzWBwHAE7aj8SQTHctP1ndcwmkHe+dJkSKbFCcOHp3+z3urcjMA== 19 | -----END AGE ENCRYPTED FILE----- 20 | lastmodified: "2021-11-05T00:56:57Z" 21 | mac: ENC[AES256_GCM,data:X2GqDxShsQpsBAA4aWEfn6w31Pwl6NqlIOi7ZOXF3ZMVA9WuJUDpSKKyKsL0oHjySFDhfZhToSUrUtk0YyMWaoxCNyN0TAkAXAO+0MHwAZLlxCmWzPAgfj8olTrQGxo6Q93eTMtem1v4C2NOmjdiPngx+AsJ+IrQEn0BVCZTeBg=,iv:ccwrruA1zOXzFK+EejHjnwOJFGa+192lo4AEKP8PU+8=,tag:Zi8tlYvtUQTXGE11U5jM+A==,type:str] 22 | pgp: [] 23 | unencrypted_suffix: _unencrypted 24 | version: 3.7.1 25 | -------------------------------------------------------------------------------- /scripts/lib/backends/_custom.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | _custom_backend_is_yaml() { 4 | false 5 | } 6 | 7 | _custom_backend_get_secret() { 8 | fatal "Please override function '_custom_backend_get_secret' in your backend!" 9 | } 10 | 11 | _custom_backend_is_file_encrypted() { 12 | _custom_backend_is_encrypted <"${1}" 13 | } 14 | 15 | _custom_backend_is_encrypted() { 16 | LC_ALL=C.UTF-8 grep -q -e "${_BACKEND_REGEX}" - 17 | } 18 | 19 | _custom_backend_encrypt_file() { 20 | fatal "Encrypting files is not supported!" 21 | } 22 | 23 | _custom_backend_decrypt_file() { 24 | type="${1}" 25 | input="${2}" 26 | # if omit then output to stdout 27 | output="${3:-}" 28 | 29 | output_yaml="$(_mktemp)" 30 | output_yaml_anchors="$(_mktemp)" 31 | 32 | # Strip yaml separator 33 | sed -e '/^---$/d' "${input}" >"${output_yaml}" 34 | 35 | # Grab all patterns, deduplicate and pass it to loop 36 | # https://github.com/koalaman/shellcheck/wiki/SC2013 37 | if ! LC_ALL=C.UTF-8 grep -o -e "${_BACKEND_REGEX}" "${input}" | sort | uniq | while IFS= read -r EXPRESSION; do 38 | # remove prefix 39 | _SECRET="${EXPRESSION#* }" 40 | 41 | if ! SECRET=$(_custom_backend_get_secret "${type}" "${_SECRET}"); then 42 | exit 1 43 | fi 44 | 45 | # generate yaml anchor name 46 | YAML_ANCHOR=$(printf 'helm-secret-%s' "${_SECRET}" | tr '#$/*.' '_') 47 | 48 | # Replace vault expression with yaml anchor 49 | EXPRESSION="$(echo "${EXPRESSION}" | _regex_escape)" 50 | _sed_i "s/${EXPRESSION}/*${YAML_ANCHOR}/g" "${output_yaml}" 51 | 52 | if _custom_backend_is_yaml "${type}" "${_SECRET}"; then 53 | { 54 | printf '.%s: &%s\n' "${YAML_ANCHOR}" "${YAML_ANCHOR}" 55 | printf '%s\n\n' "${SECRET}" | sed -e 's/^/ /g' 56 | } >>"${output_yaml_anchors}" 57 | else 58 | { 59 | printf '.%s: &%s ' "${YAML_ANCHOR}" "${YAML_ANCHOR}" 60 | printf '%s\n\n' "${SECRET}" 61 | } >>"${output_yaml_anchors}" 62 | fi 63 | done; then 64 | # pass exit from pipe/sub shell to main shell 65 | exit 1 66 | fi 67 | 68 | if [ "${input}" = "${output}" ]; then 69 | cat "${output_yaml_anchors}" "${output_yaml}" >"${input}" 70 | elif [ "${output}" = "" ]; then 71 | cat "${output_yaml_anchors}" "${output_yaml}" 72 | else 73 | cat "${output_yaml_anchors}" "${output_yaml}" >"${output}" 74 | fi 75 | } 76 | 77 | _custom_backend_decrypt_literal() { 78 | _custom_backend_get_secret "${1}" 79 | } 80 | 81 | _custom_backend_edit_file() { 82 | fatal "custom: Editing files is not supported!" 83 | } 84 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:QSuqKj9jUft+Ug==,iv:CXfhR2O5l6IF8KI5SSDxMiWQ7kghfHHb1wASAJ7JMPw=,tag:g/n7/KeltD1ODvolNCLD1w==,type:str] 2 | key: ENC[AES256_GCM,data:9nAQWTLrA71vJ82ebiEWOP/bt3B3AjCllGRiAU1szxcN95papkFeNRl3mBJT0fqhbsMD/B3hmO+kRbxxzoDAezXI0gDIrlo9fyxoSwzsRxYBzHqg4USFs6iI+THFQLkxDaCu45Ku2cxksdBTBFpswkyk0ZBFhASLfT59GhWji4+n19MXPwb0tyGiCyIpgteXBA7FdMCiLc4ZRTZechZVpvfu1EJf5UgDT39dBAn9+Rwn0oQpDKTHij908VLsH18XcCgISYnixyoShqQxtmFbLipi1Blzs5zqgYFE7BbZ6ZgdCm69/WSXBiLabG+KYMPeEYXI6IRMbf+5qteWqEriRLmjKKbRFRjIdPI3+6ysySje0xYSUHOD3YjpzzeiQPT0OqzPJ/H7LZAO+/MUhel7h83HxzWYR33ieJLE/MghpQYN4zPgqzK4XQpb5ERHtpGrXSbzcSIcICTDrlNf9T7r3SzmHXulUMAIx/AH0s3Kyuv0BzUKWYNNgwj9j99FxofQsdJHx16qph0vu7y1iKu9/Jy89rPWn/LOh8llCGztQj+TXygnUyUfMHXMBAe0X+abCu7WcWuij9ow+adA9HxCA8ezue1pPPDoPTk0OnM7FUxVdgLgEBZu0gEl3jkhVEhouLIka08fE6eJL/iNmepqFb+x815pPVVsJkDDeKxjxt+YwAYgm3BX+ZZdLZVtg93AIEPUeBgZRGw4bYQWvsKwmC8zZhLvMsyX4NwKTj+gViXsCrSv92i1epZZwrzj+xszExz3k3633VdwMOqNtK0/c9GhzSNSktkw9yKMSGTEp4O8TYepovjM/Vei0M9FQ0+rQfLEaRyPMeCIhysh2ggXxIWr5kQdEH3KCIm1aJDRA9QVOvUeXcenfN7x9pG9mqMNkumVu4ops4rDCOy/NlSvJW+n7vk+wHqSIKtwWZxNX+HRQgSmHZmvQS8IEyK5vQOxD8rQD2Er+w6Io6PvLm2F1Op1mV7ea+Lvg2pStFkQVCgRP/TbejN7gNBEsNTkel1D1uEgT2wvN+j2iFvNyEhUFUylaeUBYmDHJ9ZjQqJ2/3QZzR2yN2i+HN9sVZGdHm6bBH08GtDmjliqh8CKRjh0OMJfCZ1ZcoSBz1/m0E5zAZl3ZMuTDZaW70QsO4FoxA0oUqD14c4nxhqL3B9SxA6ErrYWY1+j3vo/ws6sBzWWzLXC59SwlwMg3PytxNWLpw==,iv:w0KBImdBsS63co+HyQVOYOxOFI/tLeRYnr+L+lCcNo8=,tag:ReQ99Km7LDQwEnlN/ppmxg==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:o3U=,iv:9NmG3nt9dutvAlXUk4cPFIiq69F+5Npf5vFDMLKipuE=,tag:Y/wUBGdB05WfC4FTokGvzw==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | lastmodified: '2020-05-06T20:54:31Z' 10 | mac: ENC[AES256_GCM,data:pgCBRi189SbfeNz1DnI7kAaEC/GwfjjfW+bWTU2OHrBRGmkioC4aaRMdGJJ1i98DFyxAhfSha5wzNsipN8tg4ZYstp8OrIjXbi3KWAzRzWqwECK8W3SgRhReZ48hHd+vVvxK7xFoV2cCtPIvJnXESZ+3PetMULxgjzAnJhIiNGE=,iv:meLE4+eocQV3GhZwLhdTsJbOWmIlZD/VLHGkXUMfUIo=,tag:1qfijLq9RVu7qPi0WdPjfQ==,type:str] 11 | pgp: 12 | - created_at: '2020-05-06T20:54:30Z' 13 | enc: | 14 | -----BEGIN PGP MESSAGE----- 15 | 16 | hQEMA9ce5qCwOO4MAQgAhLFu+zlo/fPrfAVGeQVEIEttihpMzo7CSRJDGYqSqgOO 17 | g/NbA/hDVWkE9jzGnxDY01W9RR4FOss+yd1SHlNzsPDDfkXi8e2PA8tNR6XKyoSq 18 | aCMYE4TP8JnH2hplxWucib5va2EUkgwAF+86I/ISlMLIXqeVE6xKJAuGcPQ8UwDG 19 | YUO5KzcLF8oTyoRGxvakIiCAfCWrzz7wBkT8KG5t8pQvucTtvzOpiexRL/9OU+SA 20 | Spgp8WPds+A9WArkLVQ7lcZhI0XiMxITmZdBgXGIG+1pMoGjajXUk2SA5FXeHkgH 21 | kgfAhsDlEI3mfSwYMwuFP5/659Wl3gWkMIlTpfBY2NJeAUeCmOKYRwTHR8UFa2Gg 22 | wF7wB+aj71S6v4kO932ZFHNNL0JS8OGqg/IigOhgjIC/7ozHehhKNIxCUre2g1Ws 23 | dj7U81vziuDuH/sOrgwYdqfQHa6ytoomZbiYLQl4wg== 24 | =5Jl6 25 | -----END PGP MESSAGE----- 26 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 27 | unencrypted_suffix: _unencrypted 28 | version: 3.5.0 29 | -------------------------------------------------------------------------------- /tests/assets/values/sops/http.secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:QSuqKj9jUft+Ug==,iv:CXfhR2O5l6IF8KI5SSDxMiWQ7kghfHHb1wASAJ7JMPw=,tag:g/n7/KeltD1ODvolNCLD1w==,type:str] 2 | key: ENC[AES256_GCM,data:9nAQWTLrA71vJ82ebiEWOP/bt3B3AjCllGRiAU1szxcN95papkFeNRl3mBJT0fqhbsMD/B3hmO+kRbxxzoDAezXI0gDIrlo9fyxoSwzsRxYBzHqg4USFs6iI+THFQLkxDaCu45Ku2cxksdBTBFpswkyk0ZBFhASLfT59GhWji4+n19MXPwb0tyGiCyIpgteXBA7FdMCiLc4ZRTZechZVpvfu1EJf5UgDT39dBAn9+Rwn0oQpDKTHij908VLsH18XcCgISYnixyoShqQxtmFbLipi1Blzs5zqgYFE7BbZ6ZgdCm69/WSXBiLabG+KYMPeEYXI6IRMbf+5qteWqEriRLmjKKbRFRjIdPI3+6ysySje0xYSUHOD3YjpzzeiQPT0OqzPJ/H7LZAO+/MUhel7h83HxzWYR33ieJLE/MghpQYN4zPgqzK4XQpb5ERHtpGrXSbzcSIcICTDrlNf9T7r3SzmHXulUMAIx/AH0s3Kyuv0BzUKWYNNgwj9j99FxofQsdJHx16qph0vu7y1iKu9/Jy89rPWn/LOh8llCGztQj+TXygnUyUfMHXMBAe0X+abCu7WcWuij9ow+adA9HxCA8ezue1pPPDoPTk0OnM7FUxVdgLgEBZu0gEl3jkhVEhouLIka08fE6eJL/iNmepqFb+x815pPVVsJkDDeKxjxt+YwAYgm3BX+ZZdLZVtg93AIEPUeBgZRGw4bYQWvsKwmC8zZhLvMsyX4NwKTj+gViXsCrSv92i1epZZwrzj+xszExz3k3633VdwMOqNtK0/c9GhzSNSktkw9yKMSGTEp4O8TYepovjM/Vei0M9FQ0+rQfLEaRyPMeCIhysh2ggXxIWr5kQdEH3KCIm1aJDRA9QVOvUeXcenfN7x9pG9mqMNkumVu4ops4rDCOy/NlSvJW+n7vk+wHqSIKtwWZxNX+HRQgSmHZmvQS8IEyK5vQOxD8rQD2Er+w6Io6PvLm2F1Op1mV7ea+Lvg2pStFkQVCgRP/TbejN7gNBEsNTkel1D1uEgT2wvN+j2iFvNyEhUFUylaeUBYmDHJ9ZjQqJ2/3QZzR2yN2i+HN9sVZGdHm6bBH08GtDmjliqh8CKRjh0OMJfCZ1ZcoSBz1/m0E5zAZl3ZMuTDZaW70QsO4FoxA0oUqD14c4nxhqL3B9SxA6ErrYWY1+j3vo/ws6sBzWWzLXC59SwlwMg3PytxNWLpw==,iv:w0KBImdBsS63co+HyQVOYOxOFI/tLeRYnr+L+lCcNo8=,tag:ReQ99Km7LDQwEnlN/ppmxg==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:o3U=,iv:9NmG3nt9dutvAlXUk4cPFIiq69F+5Npf5vFDMLKipuE=,tag:Y/wUBGdB05WfC4FTokGvzw==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | lastmodified: '2020-05-06T20:54:31Z' 10 | mac: ENC[AES256_GCM,data:pgCBRi189SbfeNz1DnI7kAaEC/GwfjjfW+bWTU2OHrBRGmkioC4aaRMdGJJ1i98DFyxAhfSha5wzNsipN8tg4ZYstp8OrIjXbi3KWAzRzWqwECK8W3SgRhReZ48hHd+vVvxK7xFoV2cCtPIvJnXESZ+3PetMULxgjzAnJhIiNGE=,iv:meLE4+eocQV3GhZwLhdTsJbOWmIlZD/VLHGkXUMfUIo=,tag:1qfijLq9RVu7qPi0WdPjfQ==,type:str] 11 | pgp: 12 | - created_at: '2020-05-06T20:54:30Z' 13 | enc: | 14 | -----BEGIN PGP MESSAGE----- 15 | 16 | hQEMA9ce5qCwOO4MAQgAhLFu+zlo/fPrfAVGeQVEIEttihpMzo7CSRJDGYqSqgOO 17 | g/NbA/hDVWkE9jzGnxDY01W9RR4FOss+yd1SHlNzsPDDfkXi8e2PA8tNR6XKyoSq 18 | aCMYE4TP8JnH2hplxWucib5va2EUkgwAF+86I/ISlMLIXqeVE6xKJAuGcPQ8UwDG 19 | YUO5KzcLF8oTyoRGxvakIiCAfCWrzz7wBkT8KG5t8pQvucTtvzOpiexRL/9OU+SA 20 | Spgp8WPds+A9WArkLVQ7lcZhI0XiMxITmZdBgXGIG+1pMoGjajXUk2SA5FXeHkgH 21 | kgfAhsDlEI3mfSwYMwuFP5/659Wl3gWkMIlTpfBY2NJeAUeCmOKYRwTHR8UFa2Gg 22 | wF7wB+aj71S6v4kO932ZFHNNL0JS8OGqg/IigOhgjIC/7ozHehhKNIxCUre2g1Ws 23 | dj7U81vziuDuH/sOrgwYdqfQHa6ytoomZbiYLQl4wg== 24 | =5Jl6 25 | -----END PGP MESSAGE----- 26 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 27 | unencrypted_suffix: _unencrypted 28 | version: 3.5.0 29 | -------------------------------------------------------------------------------- /tests/assets/values/sops/some-secrets.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:rBe1YaFx5B/gkQ==,iv:RJmb3MBk4X8Rk8VXh/zmzfTMnE4EIaSEbSzUXvLmtlk=,tag:YjB+F2taqPGi/52B730YoQ==,type:str] 2 | key: ENC[AES256_GCM,data:x8QQ2xAnLaqJ0W1i0Mqht/xuaqK3vWxa+tbRxWUj64iTQCKOVUHXiln+SDZ1yKArGgQCL9FkNztFdHCUheoDEjmbr/AHFRNrHlf2LmcOcuWlXXlOnuZWQPOMK2qFLjCfNYIAevPdF55AhWDr1iGq9W1/Sub7wDMzl3YpOz8E0Rv4lKCZ2DIx2vnXOxZXgtBLm0ZjkVpmJqd6H9iipAhzBt4TkH8siY2MytX7J4QA6ipbvKIX/pgevz9qG3SApsy1AYKvj2/4Yxbx8iZ9S21H2n7of6KfymBfM3lqKbVqONhGv7epHJW7gDDG21Xr11DYHHnSmxrdk3ZpEAy9QfncSwU4cDyu8XB99eRk180n5p2G4KDvTLd5CMS5FpURegJce+h5d3gR6y+vW7ZqsM55FXuNTZACe6/9i5+MrTliCoQiJaaPej9p4MIEapp0+VkbbIBZTnYxIsgn9PUQ6iKvm35cCU0kEVkpBOPMvTiielnywkJiDku/6/pNl1wIPjqaqiMA0lvNDGDHxdFifpoEJ7LOutOF9ofXHC7ylzHEFu2m7y1xSOcP58Ls7G2OPEW7E8CfN9s6CS2vCycNP3AMxoQC7/dncIzaKuXs8RApP0A/rXjjsOePj816JIXCUWKtTgLKmY8MB9ic9mejNTS+6Gn7LBylzpc7odzmdr1MlwHk7gtUrF+HCBuehiBKiiePEhMTJj7UppEw3BaH4CydHMJ+5Wp7oh5ycXys4HsFn0hE3gZJZe/hWZsRj/Uz/kuAcYUwfCCl51sNMzh8MV1R0bc3juhaeJ8MffZHwHcRemrNMbaHUyhayFneDsceA7+QxdlJnCVYH2EWQjMeLWg+6Wx0M/YBMfZy/biSfih4Qiu1bLYxYOpLyIRnk6RcSvqvnCc3CgHpvu0JeUabFa8FmzIgF3B2FXyh38e6nE5rOU1oyiLIBZCzhJlp6Xgzn5s7uMzdiZ4Q6eqGhyaxF1SdUEhavnS7bR4kxP8bI/2adg1TE1veqd27/hM0eTepd9hrq0GYTpHjA8rmSautSNaXFSvwr+0+8wEamC7DQgqV75X/XXzJQMKiOE8huO2hGFXYYvJVZ7cH/irTkgXUwMUORolrVBT2T/nv6kPx0xKm7CGh7JubFO2j8ls/QKwFQn/VZn+SZUFUgl7D/pyRq/XmSGJaDlimeEch3WbpfYOQ/MF0Iq4+JB5IkMJGK6glLw==,iv:pAIO+Nc5V8g3K5wpUdYaMlmpuSHoV89JiqRQOrNjNC0=,tag:8gqcGNeb72sMq5mxUaQusQ==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:U1o=,iv:YHGc2JAlX0kOY4yocm0sQKr5gCRwZgamEhaIS3HoiJ8=,tag:fEukYoQDZHDB9tu8xwyFJQ==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: [] 11 | lastmodified: "2021-07-13T09:38:36Z" 12 | mac: ENC[AES256_GCM,data:V6JiJ6YApYN5fsElJ2sBhaWJX1XGN/e+0qQFIcoIzdLZg/vnAc0vbqLYmXv69onKPeNrdfs/rjhoR1fY/urCGaPFFrx7k6kFFc1eyB5B3+ZT8RUhTa+Y/RdpzyfrVEBht6Iiu3iqm2P35mRjTKTkm5pUE1YmgfGx9p5hsb3hVKY=,iv:gfMmYjBfuf/gcaZqXtf/pjHNK+G7KkIY8CK6LTflWHU=,tag:+JznqzEBq67b4/WKOri00Q==,type:str] 13 | pgp: 14 | - created_at: "2021-07-13T09:38:36Z" 15 | enc: | 16 | -----BEGIN PGP MESSAGE----- 17 | 18 | hQEMA9ce5qCwOO4MAQf/cpWRY9XHCN3YS7eThaGCWhQfP/S0xvlo39xKcfQxNlJC 19 | mYpy3TYnvVMk0BZ1+i2hXcuudcrsp60xewuTjBasA8gvx19xtD5YAy38FtZuAawe 20 | R611Dzt3SCGz/WPfd1uGxVC3xru3q6efHXFdBsvf8bMIymjnPDGDKyXbEr5/auXj 21 | HKsc/0ohIt+Uz107boq3c00o1M9Jif/aTwhZUQZnn8i3aPEhTDxOR/CEKqLMngUt 22 | 3jvd73iigkhK8ZJJnBudS8hekJhP94bOI0X8e6ngOstTiqE7jyZRnJH0dia4lEDN 23 | sqANAAhBYfzVTWPuZ6qSJ6EJGEKci4uB/gcz29dcVNJeATTtSie6oYE64V9ZqAMA 24 | /3FX6rYcYn9MbzZQQrWkRpuOaarzXx+Dxv/2BXAVAN3Pibo9qk/VaPEMA7MT/PGt 25 | A2ZEIM2tC2Np0MexLoqzVENbtpNA4z+fXFm3p9Uv2A== 26 | =hUbO 27 | -----END PGP MESSAGE----- 28 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 29 | encrypted_regex: ^.+$ 30 | version: 3.7.1 31 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.yaml.gotpl: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:riGN0jCMnwoPqg==,iv:ZtnqQT0NpmR/cKVg3iF4rnf8BY0xjcdX5gtGrLvgkEM=,tag:k/ocyjodAdcnzJTFdg7alw==,type:str] 2 | key: ENC[AES256_GCM,data:cFjHdzqbIzsxAIc11JvOJDhr1thol0Yn5XcjqvjDp3OqcIlpM42XsqsrtbXpocZFmUNFs/G9GaYEPKahOsQ0Nt32qso4fAG1bYV2AA/+wQAR0c6//Q/6nXrowY3ZxrJsIoUF4IeaoDG/BUi+Y+7+bPIM9QB9umjgAfI5W5Gdp9yXJsiDr5JDLF1zXCgRtM37iJITzyNbjUMDwLeIXxRN8XcSC5BKiw2pfELVgjzrR2akHI6xQ3qh5i9WImqxcEjWhtLwyO7c8QY9+aPb2CBBKGoikq7vaVnRM588we6BD8D/eukMlEZJwpJvcbWhB1h3tNvr1oXS2RL8a8jbQND38vegW29AywvZiI+qdBMOLHDAYZlsFZdFj/PmsC9ttPW7qm9nurJfDsye6A6jjxAX+s+wilu/E6PdLoaYtl2fXlh5Zl/GTp7gBg2Sgv6wUvWoAvD+6RfP90DTe+8l81YrJdkZl5DfhGtHO5kom2FYmMmd1piIQYZ+YMy/pYw2trsEMq5re47GyQQCW4Qpn+uhXawrIkVp+vw40guNX5sbiiE5uis8iuvY8/ntKpNX2gFWNWGQRC/1JFUorwwn+yS/0mOQUcon/eeYbtkjZ5uJiKMZqJVR4H0OFZIjEZT1m/H91yqmmqR2yiyQLy3dkOHe9MF662bIKkKoAOIv/Y5Nvqg1nNDsXtwvAyCOl2wz0WR9bXEPjOLcrEm5OLnvTA6QI+erPQx7b7h3grQpX6F5dn+cHROPa8XxS4e9vYJPqH8JTxs/T2Jnotay3JyxesAiLCILWY22gDZW5y/7Fjqhy8zD+Q+ofa4E4bDa+hEig4BHZEPAKHxNuXSiqfYfeTS/luyMeGH3IA5gHfCehNLtEex2gbhZ4qOCtGf/L1hN/cuMX/PmWkOqYqlhca7HivePS2M68yW2Z4/BYqhr6rLUZlkXV+RT0rqAmmgObJ9qb+M6g5SvXeYatfYr/dTPLmg/3XOzOBuyjoocEf95XJ1N3cU3vQcFhawme3mB3cVrUqgp7H104kq9QtertdKJM9mmd/8pAo5X4p9VMF/xmF66F04CvZc9wwWDEX8FeONcD5jmcHtjrfrXGhIGcP7a18dJQLmfQequKmQ16n8fEceeXmxNVwWfSAe2JFYB4pFvKQdfSnBCk7M3ibiOAzOUoZJqd/t+ok0WrkBAwC86Tj+A5ETZvDM1pOWPoyPKh4uc+w==,iv:p+VdZ/9XUfeSMoXnSqNYzcX2v7U3AdjDp/IxthzfTCE=,tag:gnPsHa8DNXJ3z4iYuZJ9Tw==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:dAY=,iv:KkblCDKEXDnoiyVlyiEBXBiBNqC9cB0dGSuBzD/q/9Q=,tag:77+VBG+gdE0PDKUSS52RYQ==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: [] 11 | lastmodified: "2022-08-23T07:36:55Z" 12 | mac: ENC[AES256_GCM,data:5b9lo2Fe5AOos4nKGQPe3cGZBI/eRalTVznIze87689vn0/WASQm97Qtm8LXw0tosdK2H9E+93Z0cr1oUXUZgCGM8SB3OrlGu41b8iF/6jfnMwMIcvD0E7lYsgGom/wWzj4g61bUFe/Rpzf9mIzSHPPQGhoGLyRyKPD/vF0G7es=,iv:tLsNnaJgkKLyAgAzeuIuAEzxA80WTyfIh6d0fQKUDbQ=,tag:JNYDbGDmPnCh49XWv8dhZQ==,type:str] 13 | pgp: 14 | - created_at: "2022-08-23T07:36:55Z" 15 | enc: | 16 | -----BEGIN PGP MESSAGE----- 17 | 18 | hQEMA9ce5qCwOO4MAQgAqeLSDhoHRdzUxRaPD74+dfqCMcb33QYL0R7WenEPWLZe 19 | ElxYsQoe+bq5VVED4JwJRp1hjEshi7kHXc+l1kz5Mm/lfBWA3KkkOK2BsfDRZsHa 20 | 7xiIYaKcctM9eQy4Q6hDJRIka3cUAYQaJ6jyeH0Pv7Ah7MSr+8Bj5gCwB83WSGal 21 | RdOGOW+V1wdBUT2ZwJ4PD1fNNCDtfWiRbK4S1LI9ja19jd5lNmabxSntnobKIGAe 22 | wFUIcb0z+wZIIaKmys+XMFDb5XXBSR/nJEHunL7AI/rJwePEcX2XVudqkSvjBo0F 23 | eBTy1QEMGwFwz6nbYXzq6yyYhv6lLVX4DplRuqhzHNJcAe+HtXp4Ypc7lg4NAOz5 24 | qqei52bEA64UtIyiGyD2ntPfer6Y2eWNcimqI4mdZxAoK78ISDcQ99dJBM3R/VaU 25 | rMePIXZa9GMHjPqDkjin10nYBjyKlbUkRQTvNrI= 26 | =7cpN 27 | -----END PGP MESSAGE----- 28 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 29 | unencrypted_suffix: _unencrypted 30 | version: 3.7.3 31 | -------------------------------------------------------------------------------- /tests/assets/values/sops/some-secrets.windows.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:rBe1YaFx5B/gkQ==,iv:RJmb3MBk4X8Rk8VXh/zmzfTMnE4EIaSEbSzUXvLmtlk=,tag:YjB+F2taqPGi/52B730YoQ==,type:str] 2 | key: ENC[AES256_GCM,data:x8QQ2xAnLaqJ0W1i0Mqht/xuaqK3vWxa+tbRxWUj64iTQCKOVUHXiln+SDZ1yKArGgQCL9FkNztFdHCUheoDEjmbr/AHFRNrHlf2LmcOcuWlXXlOnuZWQPOMK2qFLjCfNYIAevPdF55AhWDr1iGq9W1/Sub7wDMzl3YpOz8E0Rv4lKCZ2DIx2vnXOxZXgtBLm0ZjkVpmJqd6H9iipAhzBt4TkH8siY2MytX7J4QA6ipbvKIX/pgevz9qG3SApsy1AYKvj2/4Yxbx8iZ9S21H2n7of6KfymBfM3lqKbVqONhGv7epHJW7gDDG21Xr11DYHHnSmxrdk3ZpEAy9QfncSwU4cDyu8XB99eRk180n5p2G4KDvTLd5CMS5FpURegJce+h5d3gR6y+vW7ZqsM55FXuNTZACe6/9i5+MrTliCoQiJaaPej9p4MIEapp0+VkbbIBZTnYxIsgn9PUQ6iKvm35cCU0kEVkpBOPMvTiielnywkJiDku/6/pNl1wIPjqaqiMA0lvNDGDHxdFifpoEJ7LOutOF9ofXHC7ylzHEFu2m7y1xSOcP58Ls7G2OPEW7E8CfN9s6CS2vCycNP3AMxoQC7/dncIzaKuXs8RApP0A/rXjjsOePj816JIXCUWKtTgLKmY8MB9ic9mejNTS+6Gn7LBylzpc7odzmdr1MlwHk7gtUrF+HCBuehiBKiiePEhMTJj7UppEw3BaH4CydHMJ+5Wp7oh5ycXys4HsFn0hE3gZJZe/hWZsRj/Uz/kuAcYUwfCCl51sNMzh8MV1R0bc3juhaeJ8MffZHwHcRemrNMbaHUyhayFneDsceA7+QxdlJnCVYH2EWQjMeLWg+6Wx0M/YBMfZy/biSfih4Qiu1bLYxYOpLyIRnk6RcSvqvnCc3CgHpvu0JeUabFa8FmzIgF3B2FXyh38e6nE5rOU1oyiLIBZCzhJlp6Xgzn5s7uMzdiZ4Q6eqGhyaxF1SdUEhavnS7bR4kxP8bI/2adg1TE1veqd27/hM0eTepd9hrq0GYTpHjA8rmSautSNaXFSvwr+0+8wEamC7DQgqV75X/XXzJQMKiOE8huO2hGFXYYvJVZ7cH/irTkgXUwMUORolrVBT2T/nv6kPx0xKm7CGh7JubFO2j8ls/QKwFQn/VZn+SZUFUgl7D/pyRq/XmSGJaDlimeEch3WbpfYOQ/MF0Iq4+JB5IkMJGK6glLw==,iv:pAIO+Nc5V8g3K5wpUdYaMlmpuSHoV89JiqRQOrNjNC0=,tag:8gqcGNeb72sMq5mxUaQusQ==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:U1o=,iv:YHGc2JAlX0kOY4yocm0sQKr5gCRwZgamEhaIS3HoiJ8=,tag:fEukYoQDZHDB9tu8xwyFJQ==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: [] 11 | lastmodified: "2021-07-13T09:38:36Z" 12 | mac: ENC[AES256_GCM,data:V6JiJ6YApYN5fsElJ2sBhaWJX1XGN/e+0qQFIcoIzdLZg/vnAc0vbqLYmXv69onKPeNrdfs/rjhoR1fY/urCGaPFFrx7k6kFFc1eyB5B3+ZT8RUhTa+Y/RdpzyfrVEBht6Iiu3iqm2P35mRjTKTkm5pUE1YmgfGx9p5hsb3hVKY=,iv:gfMmYjBfuf/gcaZqXtf/pjHNK+G7KkIY8CK6LTflWHU=,tag:+JznqzEBq67b4/WKOri00Q==,type:str] 13 | pgp: 14 | - created_at: "2021-07-13T09:38:36Z" 15 | enc: | 16 | -----BEGIN PGP MESSAGE----- 17 | 18 | hQEMA9ce5qCwOO4MAQf/cpWRY9XHCN3YS7eThaGCWhQfP/S0xvlo39xKcfQxNlJC 19 | mYpy3TYnvVMk0BZ1+i2hXcuudcrsp60xewuTjBasA8gvx19xtD5YAy38FtZuAawe 20 | R611Dzt3SCGz/WPfd1uGxVC3xru3q6efHXFdBsvf8bMIymjnPDGDKyXbEr5/auXj 21 | HKsc/0ohIt+Uz107boq3c00o1M9Jif/aTwhZUQZnn8i3aPEhTDxOR/CEKqLMngUt 22 | 3jvd73iigkhK8ZJJnBudS8hekJhP94bOI0X8e6ngOstTiqE7jyZRnJH0dia4lEDN 23 | sqANAAhBYfzVTWPuZ6qSJ6EJGEKci4uB/gcz29dcVNJeATTtSie6oYE64V9ZqAMA 24 | /3FX6rYcYn9MbzZQQrWkRpuOaarzXx+Dxv/2BXAVAN3Pibo9qk/VaPEMA7MT/PGt 25 | A2ZEIM2tC2Np0MexLoqzVENbtpNA4z+fXFm3p9Uv2A== 26 | =hUbO 27 | -----END PGP MESSAGE----- 28 | fp: D6174A02027050E59C711075B430C4E58E2BBBA3 29 | encrypted_regex: ^.+$ 30 | version: 3.7.1 31 | -------------------------------------------------------------------------------- /tests/unit/edit.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../bats/extensions/bats-support/load' 5 | load '../bats/extensions/bats-assert/load' 6 | load '../bats/extensions/bats-file/load' 7 | 8 | @test "edit: helm edit" { 9 | run "${HELM_BIN}" secrets edit 10 | assert_failure 11 | assert_output --partial 'Edit encrypted secrets' 12 | } 13 | 14 | @test "edit: helm edit --help" { 15 | run "${HELM_BIN}" secrets edit --help 16 | assert_success 17 | assert_output --partial 'Edit encrypted secrets' 18 | } 19 | 20 | @test "edit: File if not exits + no valid encryption config" { 21 | if ! is_backend "sops" || on_windows; then 22 | skip 23 | fi 24 | 25 | run "${HELM_BIN}" secrets edit nonexists 26 | assert_failure 27 | assert_output --partial 'config file not found, or has no creation rules, and no keys provided through command line options' 28 | } 29 | 30 | @test "edit: File if not exits + valid encryption config" { 31 | if ! is_backend "sops" || on_windows; then 32 | skip 33 | fi 34 | 35 | EDITOR="${TEST_ROOT}/assets/mock-editor/editor.sh" 36 | 37 | FILE="${TEST_TEMP_DIR}/assets/values/${HELM_SECRETS_BACKEND}/nonexists.yaml" 38 | 39 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets edit "${FILE}" 40 | assert_success 41 | 42 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets decrypt "${FILE}" 43 | assert_success 44 | assert_output "hello: world" 45 | } 46 | 47 | @test "edit: secrets.yaml" { 48 | if ! is_backend "sops" || on_windows; then 49 | skip 50 | fi 51 | 52 | EDITOR="${TEST_ROOT}/assets/mock-editor/editor.sh" 53 | 54 | FILE="${TEST_TEMP_DIR}/assets/values/${HELM_SECRETS_BACKEND}/secrets.yaml" 55 | 56 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets edit "${FILE}" 57 | assert_success 58 | 59 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets decrypt "${FILE}" 60 | assert_success 61 | assert_output "hello: world" 62 | } 63 | 64 | @test "edit: some-secrets.yaml" { 65 | if ! is_backend "sops" || on_windows; then 66 | skip 67 | fi 68 | 69 | EDITOR="${TEST_ROOT}/assets/mock-editor/editor.sh" 70 | 71 | FILE="${TEST_TEMP_DIR}/assets/values/${HELM_SECRETS_BACKEND}/some-secrets.yaml" 72 | 73 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets edit "${FILE}" 74 | assert_success 75 | 76 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets decrypt "${FILE}" 77 | assert_success 78 | assert_output "hello: world" 79 | } 80 | 81 | @test "edit: secrets.yaml + special path" { 82 | if ! is_backend "sops" || on_windows; then 83 | skip 84 | fi 85 | 86 | EDITOR="${TEST_ROOT}/assets/mock-editor/editor.sh" 87 | 88 | FILE="${SPECIAL_CHAR_DIR}/assets/values/${HELM_SECRETS_BACKEND}/secrets.yaml" 89 | 90 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets edit "${FILE}" 91 | assert_success 92 | 93 | run env EDITOR="${EDITOR}" "${HELM_BIN}" secrets decrypt "${FILE}" 94 | assert_success 95 | assert_output "hello: world" 96 | } 97 | -------------------------------------------------------------------------------- /scripts/commands/decrypt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | dec_usage() { 6 | cat < 8 | 9 | Decrypt secrets 10 | 11 | It uses your gpg credentials to decrypt previously encrypted values file. 12 | 13 | You can use plain sops to decrypt specific files - https://github.com/getsops/sops 14 | 15 | Typical usage: 16 | $ helm secrets decrypt secrets/project/secrets.yaml 17 | 18 | # Decrypt file inline 19 | 20 | $ helm secrets decrypt -i secrets/project/secrets.yaml 21 | 22 | EOF 23 | } 24 | 25 | decrypt_helper() { 26 | encrypted_file_path="${1}" 27 | type="${2:-"yaml"}" 28 | output="${3:-""}" 29 | 30 | if ! backend_is_file_encrypted "${encrypted_file_path}"; then 31 | return 1 32 | fi 33 | 34 | if [ "${output}" = "stdout" ]; then 35 | encrypted_file_dec="" 36 | elif [ "${output}" != "" ]; then 37 | encrypted_file_dec="${output}" 38 | else 39 | encrypted_file_dec="$(_file_dec_name "${encrypted_file_path}")" 40 | fi 41 | 42 | if ! backend_decrypt_file "${type}" "${encrypted_file_path}" "${encrypted_file_dec}"; then 43 | if [ "${output}" = "" ] && [ "${output}" != "stdout" ]; then 44 | rm -rf "$(_file_dec_name "${encrypted_file_path}")" 45 | fi 46 | 47 | fatal 'Error while decrypting file: %s' "${encrypted_file_path}" 48 | fi 49 | 50 | return 0 51 | } 52 | 53 | decrypt() { 54 | if is_help "$1"; then 55 | dec_usage 56 | return 57 | fi 58 | 59 | inline=false 60 | terraform=false 61 | 62 | argc=$# 63 | j=0 64 | 65 | while [ $j -lt $argc ]; do 66 | case "$1" in 67 | -i) 68 | inline=true 69 | ;; 70 | --terraform) 71 | terraform=true 72 | ;; 73 | *) 74 | set -- "$@" "$1" 75 | ;; 76 | esac 77 | 78 | shift 79 | j=$((j + 1)) 80 | done 81 | 82 | filepath="$1" 83 | 84 | if [ "${terraform}" = "true" ] || [ "${inline}" = "false" ]; then 85 | output="stdout" 86 | else 87 | output="${filepath}" 88 | fi 89 | 90 | if ! encrypted_filepath=$(_file_get "${filepath}"); then 91 | fatal 'File does not exist: %s' "${filepath}" 92 | fi 93 | 94 | # Append an underscore to the end of the content to prevent the stripping of trailing newlines 95 | # occurring during command substitution. 96 | if ! content=$(decrypt_helper "${encrypted_filepath}" "auto" "${output}" && printf '_'); then 97 | fatal 'File is not encrypted: %s' "${encrypted_filepath}" 98 | fi 99 | 100 | # Remove the underscore again. 101 | content="${content%_}" 102 | 103 | if [ "${terraform}" = "true" ]; then 104 | printf '{"content_base64":"%s"}' "$(printf '%s' "${content}" | base64 | tr -d \\n)" 105 | else 106 | printf '%s' "${content}" 107 | fi 108 | } 109 | -------------------------------------------------------------------------------- /scripts/lib/backends/sops.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | _SOPS="${HELM_SECRETS_SOPS_PATH:-${HELM_SECRETS_SOPS_BIN:-sops}}" 4 | 5 | _sops() { 6 | # shellcheck disable=SC2086 7 | set -- ${SECRET_BACKEND_ARGS} "$@" 8 | $_SOPS "$@" 9 | } 10 | 11 | _sops_backend_is_file_encrypted() { 12 | _sops_backend_is_encrypted <"${1}" 13 | } 14 | 15 | _sops_backend_is_encrypted() { 16 | grep -q 'mac.*,type:str]' - 17 | } 18 | 19 | _sops_backend_encrypt_file() { 20 | type="${1}" 21 | input="${2}" 22 | output="${3}" 23 | 24 | if [ "${type}" = "auto" ]; then 25 | type=$(_sops_enc_get_type "${input}") 26 | fi 27 | 28 | if [ "${input}" = "${output}" ]; then 29 | _sops --encrypt --input-type "${type}" --output-type "${type}" --in-place "$(_sops_winpath "${input}")" 30 | elif [ "${output}" = "" ]; then 31 | _sops --encrypt --input-type "${type}" --output-type "${type}" "$(_sops_winpath "${input}")" 32 | else 33 | _sops --encrypt --input-type "${type}" --output-type "${type}" --output "$(_sops_winpath "${output}")" "$(_sops_winpath "${input}")" 34 | fi 35 | } 36 | 37 | _sops_backend_decrypt_file() { 38 | type="${1}" 39 | input="${2}" 40 | # if omit then output to stdout 41 | output="${3:-}" 42 | 43 | if [ "${type}" = "auto" ]; then 44 | type=$(_sops_dec_get_type "${input}") 45 | fi 46 | 47 | if [ "${input}" = "${output}" ]; then 48 | _sops --decrypt --input-type "${type}" --output-type "${type}" --in-place "$(_sops_winpath "${input}")" 49 | elif [ "${output}" = "" ]; then 50 | _sops --decrypt --input-type "${type}" --output-type "${type}" "$(_sops_winpath "${input}")" 51 | else 52 | _sops --decrypt --input-type "${type}" --output-type "${type}" --output "$(_sops_winpath "${output}")" "$(_sops_winpath "${input}")" 53 | fi 54 | } 55 | 56 | _sops_backend_decrypt_literal() { 57 | if printf '%s' "${1}" | _sops_backend_is_encrypted; then 58 | printf '%s' "${1}" | _sops --decrypt --input-type 'json' --output-type 'json' /dev/stdin 59 | else 60 | printf '%s' "${1}" 61 | fi 62 | } 63 | 64 | _sops_backend_edit_file() { 65 | type="${1}" 66 | input="${2}" 67 | 68 | _sops --input-type yaml --output-type yaml "$(_sops_winpath "${input}")" 69 | } 70 | 71 | _sops_winpath() { 72 | if on_cygwin; then 73 | _winpath "$@" 74 | elif on_wsl; then 75 | case "${_SOPS}" in 76 | *.exe) _winpath "$@" ;; 77 | *) printf '%s' "$@" ;; 78 | esac 79 | else 80 | printf '%s' "$@" 81 | fi 82 | } 83 | 84 | _sops_dec_get_type() { 85 | if grep -xq 'sops:\s*' "${1}"; then 86 | echo 'yaml' 87 | elif grep -q '"data": "ENC' "${1}"; then 88 | echo 'binary' 89 | else 90 | echo 'json' 91 | fi 92 | } 93 | 94 | _sops_enc_get_type() { 95 | file_type=$(_file_get_extension "${1}") 96 | if [ "${file_type}" = "other" ]; then 97 | echo 'binary' 98 | else 99 | echo "${file_type}" 100 | fi 101 | } 102 | -------------------------------------------------------------------------------- /examples/argo-cd/setup/values.yaml: -------------------------------------------------------------------------------- 1 | argo-cd: 2 | server: 3 | config: 4 | helm.valuesFileSchemes: >- 5 | secrets+gpg-import, secrets+gpg-import-kubernetes, 6 | secrets+age-import, secrets+age-import-kubernetes, 7 | secrets, 8 | https 9 | repoServer: 10 | serviceAccount: 11 | create: true 12 | name: argocd-repo-server 13 | rbac: 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - secrets 18 | verbs: 19 | - get 20 | env: 21 | - name: HELM_PLUGINS 22 | value: /custom-tools/helm-plugins/ 23 | - name: HELM_SECRETS_SOPS_PATH 24 | value: /custom-tools/sops 25 | - name: HELM_SECRETS_VALS_PATH 26 | value: /custom-tools/vals 27 | - name: HELM_SECRETS_KUBECTL_PATH 28 | value: /custom-tools/kubectl 29 | - name: HELM_SECRETS_CURL_PATH 30 | value: /custom-tools/curl 31 | # https://github.com/jkroepke/helm-secrets/wiki/Security-in-shared-environments 32 | - name: HELM_SECRETS_VALUES_ALLOW_SYMLINKS 33 | value: "false" 34 | - name: HELM_SECRETS_VALUES_ALLOW_ABSOLUTE_PATH 35 | value: "false" 36 | - name: HELM_SECRETS_VALUES_ALLOW_PATH_TRAVERSAL 37 | value: "false" 38 | # helm secrets wrapper mode installation (optional) 39 | # - name: HELM_SECRETS_HELM_PATH 40 | # value: /usr/local/bin/helm 41 | volumes: 42 | - name: custom-tools 43 | emptyDir: {} 44 | volumeMounts: 45 | - mountPath: /custom-tools 46 | name: custom-tools 47 | # helm secrets wrapper mode installation (optional) 48 | # - mountPath: /usr/local/sbin/helm 49 | # subPath: helm 50 | # name: custom-tools 51 | 52 | initContainers: 53 | - name: download-tools 54 | image: alpine:latest 55 | command: [ sh, -ec ] 56 | env: 57 | - name: HELM_SECRETS_VERSION 58 | value: "3.17.3" 59 | - name: KUBECTL_VERSION 60 | value: "1.33.0" 61 | - name: VALS_VERSION 62 | value: "0.40.1" 63 | - name: SOPS_VERSION 64 | value: "3.10.2" 65 | args: 66 | - | 67 | mkdir -p /custom-tools/helm-plugins 68 | wget -qO- https://github.com/jkroepke/helm-secrets/releases/download/v${HELM_SECRETS_VERSION}/helm-secrets.tar.gz | tar -C /custom-tools/helm-plugins -xzf-; 69 | 70 | wget -qO /custom-tools/sops https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux 71 | wget -qO /custom-tools/kubectl https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl 72 | 73 | wget -qO- https://github.com/variantdev/vals/releases/download/v${VALS_VERSION}/vals_${VALS_VERSION}_linux_amd64.tar.gz | tar -xzf- -C /custom-tools/ vals; 74 | 75 | # helm secrets wrapper mode installation (optional) 76 | # RUN printf '#!/usr/bin/env sh\nexec %s secrets "$@"' "${HELM_SECRETS_HELM_PATH}" >"/usr/local/sbin/helm" && chmod +x "/custom-tools/helm" 77 | 78 | chmod +x /custom-tools/* 79 | volumeMounts: 80 | - mountPath: /custom-tools 81 | name: custom-tools 82 | -------------------------------------------------------------------------------- /scripts/lib/file.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | VALUES_ALLOW_SYMLINKS="${HELM_SECRETS_VALUES_ALLOW_SYMLINKS:-true}" 6 | VALUES_ALLOW_ABSOLUTE_PATH="${HELM_SECRETS_VALUES_ALLOW_ABSOLUTE_PATH:-true}" 7 | VALUES_ALLOW_PATH_TRAVERSAL="${HELM_SECRETS_VALUES_ALLOW_PATH_TRAVERSAL:-true}" 8 | 9 | # shellcheck source=scripts/lib/file/local.sh 10 | . "${SCRIPT_DIR}/lib/file/local.sh" 11 | 12 | # shellcheck source=scripts/lib/file/http.sh 13 | . "${SCRIPT_DIR}/lib/file/http.sh" 14 | 15 | # shellcheck source=scripts/lib/file/custom.sh 16 | . "${SCRIPT_DIR}/lib/file/custom.sh" 17 | 18 | _file_get_protocol() { 19 | case "$1" in 20 | http://* | https://*) 21 | echo "http" 22 | ;; 23 | *://*) 24 | echo "custom" 25 | ;; 26 | *) 27 | echo "local" 28 | ;; 29 | esac 30 | } 31 | 32 | _file_exists() { 33 | file_type=$(_file_get_protocol "${1}") 34 | 35 | _file_"${file_type}"_exists "$@" 36 | } 37 | 38 | _file_get() { 39 | file_type=$(_file_get_protocol "${1}") 40 | 41 | if [ "${file_type}" = "local" ]; then 42 | if [ "${VALUES_ALLOW_SYMLINKS}" = "false" ] && [ -L "${1}" ]; then 43 | fatal "Values file '%s' is a symlink. Symlinks are not allowed." "${1}" 44 | fi 45 | 46 | if [ "${VALUES_ALLOW_ABSOLUTE_PATH}" = "false" ]; then 47 | case "${1}" in 48 | /*) fatal "Values filepath '%s' is an absolute path. Absolute paths are not allowed." "${1}" ;; 49 | \\*) fatal "Values filepath '%s' is an absolute path. Absolute paths are not allowed." "${1}" ;; 50 | *:*) fatal "Values filepath '%s' is an absolute path. Absolute paths are not allowed." "${1}" ;; 51 | esac 52 | fi 53 | 54 | if [ "${VALUES_ALLOW_PATH_TRAVERSAL}" = "false" ]; then 55 | case "${1}" in 56 | *../*) fatal "Values filepath '%s' contains '..'. Path traversal is not allowed." "${1}" ;; 57 | */..*) fatal "Values filepath '%s' contains '..'. Path traversal is not allowed." "${1}" ;; 58 | esac 59 | fi 60 | fi 61 | 62 | _file_"${file_type}"_get "$@" 63 | } 64 | 65 | _file_dec_name() { 66 | _basename="$(basename "${1}")" 67 | _dirname="$(dirname "${1}")" 68 | _dirname="$(printf '%s' "${_dirname}" | sed 's/\//_/g')" 69 | if [ "${DEC_DIR}" != "" ]; then 70 | printf '%s/%s_%s%s%s' "${DEC_DIR}" "${_dirname}" "${DEC_PREFIX}" "${_basename}" "${DEC_SUFFIX}" 71 | elif [ "${DECRYPT_SECRETS_IN_TMP_DIR}" = "true" ]; then 72 | printf '%s/%s_%s%s%s' "${TMPDIR}" "${_dirname}" "${DEC_PREFIX}" "${_basename}" "${DEC_SUFFIX}" 73 | elif [ "${1}" != "${_basename}" ]; then 74 | printf '%s/%s%s%s' "$(dirname "${1}")" "${DEC_PREFIX}" "${_basename}" "${DEC_SUFFIX}" 75 | else 76 | printf '%s%s%s' "${DEC_PREFIX}" "${_basename}" "${DEC_SUFFIX}" 77 | fi 78 | } 79 | 80 | _file_get_extension() { 81 | case "${1}" in 82 | *.yaml | *.yaml*) 83 | echo "yaml" 84 | ;; 85 | *.json | *.json*) 86 | echo "json" 87 | ;; 88 | *) 89 | if grep -Fxq -- "---" "${1}"; then 90 | echo "yaml" 91 | else 92 | echo "other" 93 | fi 94 | ;; 95 | esac 96 | } 97 | -------------------------------------------------------------------------------- /tests/assets/values/sops/secrets.gpg_key.yaml: -------------------------------------------------------------------------------- 1 | global_secret: ENC[AES256_GCM,data:RHCdqGp8raW+eg==,iv:Q/B0ZxXNOUF8xhOZe8jaCO/Rd8kfhS4SKv4xbksDfK0=,tag:fQbLp5QZjh/6pHboDpT8VQ==,type:str] 2 | key: ENC[AES256_GCM,data:1vwOET7a3YVetxllvbYHdkCeH2UJS9klHbexD+TvXgAi0pQR0AN6r7cOh9pPKsFpeUVvzqC+XCwA19lNzqQogI7+g1AcxiZje0P1zbTqCbstsvVtzR2AMiThAlLzOjybdIWQUetBSz1gIA5SMDPnOXSg6w5nSLx4q8m4aEZEZ3Xwv/zXiHb604HpSwNf7rkSyc0Y01oQTCEMUNnsYZN++qZBPcPIXY9jnXWVoyWPZFKhs9fBhUw96QnZMhTK3Qex6VGXaOZMiVYtHfU68gf2/lzpWCOfYrj7MxsBiYJ9qesvRMceJjhQTtzF2VkoABQ+fn53hbNJKHdecJcYOkmmaqJXWyp7LvvfF+XEi2OKjT0N3DyTZVFsZgCVLmLHy/NHmE3m7DX5XClFlGycQyku9yEUFuMBMTbaynG3WyOWIID0TBmWCLjZIl1aymIa2QTFvz3URQUjyS2S7incoL6lH7DgpnCl+yD8Mf25xbo9l9ttv8pNdkvSqh89SOcq6VWnh4qB66HzZBGuklF+hbPA+GrIJm79IfGYhrZ5Akd44e9ZH3Mn7J9WG4HhiQ7V9S3XDi2grjTHxFGT92DielkaR4gpyVgR94CoSlcsk3eOMbf9ATyQc1BSsN9gilbMqLuZiig3qnbZTboR3rUkqLodsVMY/EEWenlypkFgSAVFDY53Szz444tYeY2Um2YPskOSB/pSKQAAJKVmvfLg2DK7Y+9BXSeEAef0FayNSlw3Zu21xTu41i+D4bU5ca704J3ZWU1zhiDYihxkbzP5LbBGHLByXpyuo0skS2szbG27Q4OlkexDbdyPowlsuzK9p+iO7XZRU5Efm4KtcOAIoJfsPrlUB37JN/rVnbmsAeHA0jjjTlKyEhOATsOo1WVOD5J9EQcBY6pyThP/wytxwkhDC2viQx8xeKm+0RVvPgYUmAO8yz45ZP+DqLtqvI7/g28pa6DvQ0cV9abHgDjnO4IekrTeDs7WCpKwTrR06xvrmgmqNagZkAwPws3oF+BgvEVeoK7y4OAb66T6OX6xdO0XKlQUQvBbC2nuyp+kFSadKdxxg9jhSBB039dKIf8DMrZ80C2PLq8+2A1uUEJEjDpETgmi84+9RexMlEFTo8ofqUhunQyB4nRkR1RKa6uQVpelNzBc81YgRONCYnt6E1zGvAmg9lKni8m1HGJnp+CgnzQL75AM72i5ym9RP+VreQ==,iv:J8NFnO5sPUPQlT0ctnsSI9yq6MQSLfM8Hnq1o335Xqs=,tag:2sWuxzAlJFbSAIEuJstDxw==,type:str] 3 | service: 4 | port: ENC[AES256_GCM,data:f20=,iv:NxHKv2vPhrCNXsPKPQDgpuo9XwIP7jWVny6Hvq1ouuY=,tag:/N0FieGqsCGjia0jFx0hhA==,type:int] 5 | sops: 6 | kms: [] 7 | gcp_kms: [] 8 | azure_kv: [] 9 | hc_vault: [] 10 | age: [] 11 | lastmodified: "2021-10-04T18:06:33Z" 12 | mac: ENC[AES256_GCM,data:nAA9tegrSk1rUN5Oz9Bra7wlZktuVSehdpJ1BIZPV3RLPxEhEcJoqXp5RzFXVmCjcEbO24vMW/vKPuOzlIAH/S1NGyVikEsUXKURQ4Su7sNHrmBNVg5CfQdCl850NHptnjjRT1UobnaUUl1YEEiUgWsmQgWjYRw6SdSGxv7/ORI=,iv:xnSve1j63ZA6ab1+U4+CyPw9RFpYWRNToHE7ziRg9sU=,tag:Dp2pKd3tRaOqrgSLUhlfOQ==,type:str] 13 | pgp: 14 | - created_at: "2021-10-04T18:06:33Z" 15 | enc: | 16 | -----BEGIN PGP MESSAGE----- 17 | 18 | hQIMA1eRrmsdQTPbAQ/+NTVD6PNE0Z0xZVnX1+hYyQD2fmvy11Y1SqgOQCO4CXHT 19 | M8ebHiCAGydbd/QjGWenfi/tvkvtQzKX3XwzYbnR7BsKz05ByGyHDxTqHF5+QkI7 20 | l2vZMHX8uInRMZo01wjNLb0+Z+4sOuasKMwW9iJ1kdwjl0vvwyR34hIo938E+fGf 21 | ZC4kmD3fTdSXyhkIqWkT17Tjn78/3BsxCRK/9S3AvLc5wh3nSN/rHaqj+zSDh/1B 22 | VDuXG+7+lt3N1V1rs56B9MCo5Bfwf+L2ecqZWApI3QA9jvpOpD3CwsFguZ8yD3jM 23 | Y24biGj12Xhbc9gDpyHC6lzOzW8nYP5PKzVBPj5G8/Sn57lAMbudLXxIeXr7ANbZ 24 | YXi24IwfmuHY1x7uBzBApFiGm4TYZt/aaUY+X3w3GHAobMoCJmWqccvV5hmqKsvJ 25 | gyEcu98b0iy2vzmt8jawyQcCtoSpV+BLeMXyuQG2XESn1mryqk41WMaH1wX8Rkmi 26 | 4Swp1wZGH0MHFuvdWt3XhPF4yZR0csBHewp9KyjvyeAsBDLN4xBr3H4SzndgK7A2 27 | B+3df3PUIj4YFm6yoPoYLPnierpi0t8zG4YWtfXEvoOtgA6DW6rVTxpzHvOGnms/ 28 | BB6OsrLfFmpfo8h7Ycv+335boDPdSVsJkQjusyVgWlhq1wYl9x7bwea3c0XjifjS 29 | XgFUO2mFBFaJ/3rWkZPefmLtOKd0BmMai/3ZMNs9lxvG9fVr2itwsx6K5d2BGAH1 30 | AfPgv2LbjE47p+IBEj6B5m6gOLziecnMLN/ajET/4+N6i8H9b07TFWMAvb/rTe4= 31 | =Z1A8 32 | -----END PGP MESSAGE----- 33 | fp: 030CB93CA6080585BB7B4453CD92EAD13B038F38 34 | unencrypted_suffix: _unencrypted 35 | version: 3.7.1 36 | -------------------------------------------------------------------------------- /tests/unit/encrypt.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../bats/extensions/bats-support/load' 5 | load '../bats/extensions/bats-assert/load' 6 | load '../bats/extensions/bats-file/load' 7 | 8 | @test "encrypt: helm encrypt" { 9 | run "${HELM_BIN}" secrets encrypt 10 | assert_failure 11 | assert_output --partial 'Error: secrets file required.' 12 | } 13 | 14 | @test "encrypt: helm encrypt --help" { 15 | run "${HELM_BIN}" secrets encrypt --help 16 | assert_success 17 | assert_output --partial 'Encrypt secrets' 18 | } 19 | 20 | @test "encrypt: File not exits" { 21 | run "${HELM_BIN}" secrets encrypt nonexists 22 | assert_failure 23 | assert_output --partial '[helm-secrets] File does not exist: nonexists' 24 | } 25 | 26 | @test "encrypt: Encrypt secrets.yaml" { 27 | if ! is_backend "sops"; then 28 | skip 29 | fi 30 | 31 | VALUES="assets/values/${HELM_SECRETS_BACKEND}/secrets.dec.yaml" 32 | VALUES_PATH="${TEST_TEMP_DIR}/${VALUES}" 33 | 34 | run "${HELM_BIN}" secrets encrypt "${VALUES_PATH}" 35 | 36 | assert_output --partial 'global_secret: ENC' 37 | assert_success 38 | } 39 | 40 | @test "encrypt: Encrypt inline secrets.yaml" { 41 | if ! is_backend "sops"; then 42 | skip 43 | fi 44 | 45 | VALUES="assets/values/${HELM_SECRETS_BACKEND}/secrets.dec.yaml" 46 | VALUES_PATH="${TEST_TEMP_DIR}/${VALUES}" 47 | 48 | run "${HELM_BIN}" secrets encrypt -i "${VALUES_PATH}" 49 | refute_output --regex '.+' 50 | assert_success 51 | 52 | assert_file_contains "${VALUES_PATH}" 'global_secret: ENC' 53 | 54 | run "${HELM_BIN}" secrets decrypt "${VALUES_PATH}" 55 | assert_output --partial 'global_secret: ' 56 | assert_output --partial 'global_bar' 57 | assert_success 58 | } 59 | 60 | @test "encrypt: Encrypt some-secrets.yaml" { 61 | if ! is_backend "sops"; then 62 | skip 63 | fi 64 | 65 | VALUES="assets/values/${HELM_SECRETS_BACKEND}/some-secrets.dec.yaml" 66 | VALUES_PATH="${TEST_TEMP_DIR}/${VALUES}" 67 | 68 | run "${HELM_BIN}" secrets encrypt "${VALUES_PATH}" 69 | 70 | assert_output --partial 'global_secret: ENC' 71 | assert_success 72 | } 73 | 74 | @test "encrypt: Encrypt secrets.yaml + special char directory name" { 75 | if ! is_backend "sops"; then 76 | skip 77 | fi 78 | 79 | if on_windows; then 80 | skip "Skip on Windows" 81 | fi 82 | 83 | VALUES="assets/values/${HELM_SECRETS_BACKEND}/secrets.dec.yaml" 84 | VALUES_PATH="${SPECIAL_CHAR_DIR}/${VALUES}" 85 | 86 | run "${HELM_BIN}" secrets encrypt "${VALUES_PATH}" 87 | 88 | assert_output --partial 'global_secret: ENC' 89 | assert_success 90 | } 91 | 92 | @test "encrypt: Encrypt secrets.tmp.yaml" { 93 | if ! is_backend "sops"; then 94 | skip 95 | fi 96 | 97 | VALUES="assets/values/${HELM_SECRETS_BACKEND}/secrets.tmp.yaml" 98 | VALUES_PATH="${TEST_TEMP_DIR}/${VALUES}" 99 | 100 | YAML="hello: world" 101 | echo "${YAML}" >"${VALUES_PATH}" 102 | 103 | run "${HELM_BIN}" secrets encrypt -i "${VALUES_PATH}" 104 | assert_success 105 | 106 | run "${HELM_BIN}" secrets decrypt -i "${VALUES_PATH}" 107 | 108 | assert_file_exists "${VALUES_PATH}" 109 | assert_file_contains "${VALUES_PATH}" 'hello: world' 110 | assert_success 111 | } 112 | -------------------------------------------------------------------------------- /tests/assets/gpg/private.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | 3 | 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 57 | =YUJL 58 | -----END PGP PRIVATE KEY BLOCK----- 59 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>jkroepke/renovate-config" 5 | ], 6 | "git-submodules": { 7 | "enabled": true 8 | }, 9 | "packageRules": [ 10 | { 11 | "matchFileNames": [ 12 | "/(^|/).+\\.md$/" 13 | ], 14 | "groupName": "docs dependencies", 15 | "semanticCommitScope": "docs-deps", 16 | "addLabels": [ 17 | "chore" 18 | ], 19 | "separateMajorMinor": false 20 | }, 21 | { 22 | "matchManagers": ["git-submodules"], 23 | "groupName": "all bats dependencies", 24 | "semanticCommitScope": "dev-deps", 25 | "addLabels": [ 26 | "chore" 27 | ] 28 | }, 29 | { 30 | "matchFileNames": ["Gemfile"], 31 | "semanticCommitScope": "dev-deps", 32 | "addLabels": [ 33 | "chore" 34 | ] 35 | }, 36 | { 37 | "matchPackageNames": ["helm/helm", "helm"], 38 | "allowedVersions": "<4" 39 | } 40 | ], 41 | "customManagers": [ 42 | { 43 | "customType": "regex", 44 | "managerFilePatterns": [ 45 | "/(^|/).+\\.md$/" 46 | ], 47 | "matchStrings": [ 48 | "ARG\\s+ARGOCD_VERSION=\"v(?.*?)\"" 49 | ], 50 | "depNameTemplate": "quay.io/argoproj/argocd", 51 | "datasourceTemplate": "docker", 52 | "versioningTemplate": "semver" 53 | }, 54 | { 55 | "customType": "regex", 56 | "managerFilePatterns": [ 57 | "/(^|/).+\\.md$/" 58 | ], 59 | "matchStrings": [ 60 | "ARG\\s+SOPS_VERSION=(?.*?)\\s+", 61 | "- name: SOPS_VERSION\\n\\s+value: \"(?.*?)\"" 62 | ], 63 | "depNameTemplate": "getsops/sops", 64 | "datasourceTemplate": "github-releases", 65 | "versioningTemplate": "semver" 66 | }, 67 | { 68 | "customType": "regex", 69 | "managerFilePatterns": [ 70 | "/(^|/).+\\.md$/" 71 | ], 72 | "matchStrings": [ 73 | "ARG\\s+KUBECTL_VERSION=(?.*?)\\s+", 74 | "- name: KUBECTL_VERSION\\n\\s+value: \"(?.*?)\"" 75 | ], 76 | "depNameTemplate": "kubernetes/kubernetes", 77 | "datasourceTemplate": "github-releases", 78 | "versioningTemplate": "semver" 79 | }, 80 | { 81 | "customType": "regex", 82 | "managerFilePatterns": [ 83 | "/(^|/).+\\.md$/" 84 | ], 85 | "matchStrings": [ 86 | "ARG\\s+VALS_VERSION=(?.*?)\\s+", 87 | "- name: VALS_VERSION\\n\\s+value: \"(?.*?)\"" 88 | ], 89 | "depNameTemplate": "helmfile/vals", 90 | "datasourceTemplate": "github-releases", 91 | "versioningTemplate": "semver" 92 | }, 93 | { 94 | "customType": "regex", 95 | "managerFilePatterns": [ 96 | "/(^|/).+\\.md$/" 97 | ], 98 | "matchStrings": [ 99 | "ARG\\s+HELM_SECRETS_VERSION=(?.*?)\\s+", 100 | "- name: HELM_SECRETS_VERSION\\n\\s+value: \"(?.*?)\"" 101 | ], 102 | "depNameTemplate": "jkroepke/helm-secrets", 103 | "datasourceTemplate": "github-releases", 104 | "versioningTemplate": "semver" 105 | }, 106 | { 107 | "customType": "regex", 108 | "managerFilePatterns": [ 109 | "/(^|/).+\\.md$/" 110 | ], 111 | "matchStrings": [ 112 | "ARG\\s+AGE_VERSION=(?.*?)\\s+", 113 | "- name: AGE_VERSION\\n\\s+value: \"(?.*?)\"" 114 | ], 115 | "depNameTemplate": "FiloSottile/age", 116 | "datasourceTemplate": "github-releases", 117 | "versioningTemplate": "semver" 118 | } 119 | ] 120 | } 121 | -------------------------------------------------------------------------------- /docs/Values.md: -------------------------------------------------------------------------------- 1 | # Values 2 | 3 | helm-secrets natively support all values that are support by helm, including 4 | [downloader plugins](https://helm.sh/docs/topics/plugins/#downloader-plugins). 5 | 6 | # Remote values from http 7 | 8 | When curl or wget is available, helm-secrets is able to fetch value files from various remote locations. 9 | 10 | ```bash 11 | helm template -f secrets://https://raw.githubusercontent.com/jkroepke/helm-secrets/main/examples/sops/secrets.yaml 12 | ``` 13 | 14 | ## Secured remote values 15 | 16 | While helm does not support any authentication mechanism, helm-secret does support at least basic auth. 17 | ```bash 18 | helm template -f secrets://https://user:password@raw.githubusercontent.com/jkroepke/helm-secrets/main/examples/sops/secrets.yaml 19 | ``` 20 | 21 | Additionally, the authentication details can be provided by environment variables or from a file system using the 22 | [.netrc](https://everything.curl.dev/usingcurl/netrc) standard this is useful inside CD systems. 23 | 24 | 25 | ### Via environment variables 26 | 27 | _Note: is feature is turned off by default and requires the environment variables `HELM_SECRETS_URL_VARIABLE_EXPANSION=true`._ 28 | 29 | ```bash 30 | # can be also defined via kubernetes PodSpec or CI secrets 31 | export HELM_SECRETS_URL_VARIABLE_EXPANSION=true 32 | export GH_TOKEN=ghp_xxxxxx 33 | 34 | helm template -f secrets://https://${GH_TOKEN}@raw.githubusercontent.com/jkroepke/helm-secrets/main/examples/sops/secrets.yaml 35 | ``` 36 | 37 | ### Via .netrc file 38 | 39 | To enable this feature, an environment `NETRC` needs to defined which holds the path to the .netrc file. This is required 40 | even the standard location `~/.netrc` is used. The .netrc file can hold multiple credentials for different hostnames. 41 | 42 | The `wget` command on alpine linux does not support `.netrc` and `curl` is required and automatically preferred over `wget`. 43 | 44 | Example `.netrc` file: 45 | 46 | ``` 47 | # cat .netrc 48 | machine raw.githubusercontent.com 49 | login ghp_xxxxxx 50 | password 51 | ``` 52 | 53 | Then run 54 | 55 | ```bash 56 | export NETRC="${PWD}/.netrc" # needs to be defined 57 | helm template -f secrets://https://raw.githubusercontent.com/jkroepke/helm-secrets/main/examples/sops/secrets.yaml 58 | ``` 59 | 60 | # Remote values from git 61 | 62 | helm-secrets support [helm-git](https://github.com/aslafy-z/helm-git). 63 | With this combination, you can fetch secret from other git repositories. 64 | 65 | ```bash 66 | helm template -f secrets://git+https:///[@path/to/charts][?[ref=git-ref][&sparse=0][&depupdate=0][&package=0]] 67 | ``` 68 | 69 | Other plugins like [helm-s3, helm-gcs](https://helm.sh/docs/community/related/#helm-plugins) are supported as well. 70 | 71 | ## Pass secrets through --set / --set-file 72 | 73 | helm-secrets support pass secrets' trough `--set` or `--set-file`. 74 | 75 | Examples 76 | 77 | ```bash 78 | helm secrets -b vals template bitnami/mysql --name-template mysql \ 79 | --set auth.rootPassword=ref+vault://secret/mysql#/rootPassword 80 | ``` 81 | 82 | ```bash 83 | helm secrets template bitnami/mysql --name-template mysql \ 84 | --set-file auth.rootPassword=secret.yaml 85 | ``` 86 | 87 | or through downloader syntax (`--set-file` only); 88 | 89 | ```bash 90 | helm template bitnami/mysql --name-template mysql \ 91 | --set-file auth.rootPassword=secrets://secret.yaml 92 | ``` 93 | 94 | ## Ignore missing value files 95 | 96 | If `HELM_SECRETS_IGNORE_MISSING_VALUES=true` is set, helm-secrets ignore all not found errors. This mimics ArgoCD's `ignoreMissingValueFile` setting. 97 | Alternatively, if a value file beginnings with a question mark, all not found errors related to that values file only are ignored. 98 | 99 | Example: 100 | 101 | ```bash 102 | helm upgrade -i release . -f secrets://?dev/file-not-found.yaml 103 | ``` 104 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # helm-secrets test suite 2 | 3 | This tests suite use the [bats-core](https://github.com/bats-core/bats-core) framework. 4 | 5 | Some test extension libraries are included in this project as git submodule. 6 | 7 | Run 8 | ```bash 9 | git submodule update --init --force 10 | ``` 11 | to checkout the submodules. 12 | 13 | ## Wording 14 | 15 | Inside helm-secrets we have 2 groups of tests: 16 | 17 | * **unit tests** 18 | 19 | Can be run without an reachable kubernetes cluster 20 | Located under [./unit/](./unit) 21 | 22 | * **integration tests** 23 | 24 | Depends against a reachable kubernetes cluster 25 | Located under [./it/](./it) 26 | 27 | ## Requirements 28 | 29 | To execute the tests have to install some utilities first. 30 | 31 | ### bats 32 | Then follow the installation instruction for bats here: https://github.com/bats-core/bats-core#installation 33 | 34 | More information's here: https://github.com/bats-core/bats-core 35 | 36 | ### sops 37 | Can be downloaded here: https://github.com/getsops/sops/releases 38 | 39 | Alternately available via [homebrew](https://brew.sh/): 40 | 41 | ```bash 42 | brew install sops 43 | ``` 44 | 45 | More information's here: https://github.com/getsops/sops 46 | 47 | ### gpg 48 | sops only non-public cloud encryption method based on gpg. 49 | 50 | Alternately available via [homebrew](https://brew.sh/): 51 | ```bash 52 | brew info gnupg 53 | ``` 54 | 55 | On Linux use your package manager to install gpg if it's not already installed. 56 | 57 | ### vault-cli (optional) 58 | The vault cli is only required to run the tests with the `HELM_SECRETS_BACKEND=vault` environment variable. 59 | 60 | You could download vault here: https://www.vaultproject.io/downloads 61 | 62 | Alternately available via [homebrew](https://brew.sh/): 63 | ```bash 64 | brew info vault 65 | ``` 66 | 67 | ### onepassword (optional) 68 | 69 | The 1Password CLI is only required to run the tests with the `HELM_SECRETS_BACKEND=custom-onepassword` environment variable. 70 | 71 | Instructions on how to install and set up the 1Password CLI can be found here: https://developer.1password.com/docs/cli/get-started 72 | 73 | Create the following test item before running the tests: 74 | 75 | ```shell 76 | op item create --category=login \ 77 | --title='helm-secrets test' \ 78 | --vault='Private' \ 79 | 'username=test-username' \ 80 | 'password=mytestpassword123' \ 81 | 'email[email]=test@example.com' \ 82 | 'data.username[text]=a-test-name' \ 83 | 'data.password[password]=testthispassword' \ 84 | 'data 2.email[email]=my-test@example.com' \ 85 | 'data 2.password[password]=my-test-could-be-different!' 86 | ``` 87 | 88 | ## Run 89 | 90 | If possible start the tests from the root of the repository. Then execute: 91 | 92 | ```bash 93 | # Unit Tests 94 | bats -r tests/unit 95 | 96 | # IT Tests 97 | bats -r tests/it 98 | ``` 99 | 100 | If bats is not installed locally, you could run bats directory from this repo: 101 | 102 | ```bash 103 | # Unit Tests 104 | ./tests/bats/core/bin/bats -r tests/unit 105 | 106 | # IT Tests 107 | ./tests/bats/core/bin/bats -r tests/it 108 | ``` 109 | 110 | This method is described as "Run bats from source" inside the bats-core documentation. 111 | 112 | More information about running single tests or filtering tests can be found here: https://github.com/bats-core/bats-core#usage 113 | 114 | By default, the sops backend is selected for tests. 115 | If you want to test another secret backend like [vals](../scripts/lib/backends/vals.sh), you could do it by env variable `HELM_SECRETS_BACKEND=vals`. 116 | 117 | ```bash 118 | # Unit Tests 119 | HELM_SECRETS_BACKEND=vault bats -r tests/unit 120 | 121 | # IT Tests 122 | HELM_SECRETS_BACKEND=vault bats -r tests/it 123 | ``` 124 | 125 | The vault tests require a reachable vault server. Start one on your local machine by running: 126 | 127 | ```bash 128 | vault server -dev -dev-root-token-id=test 129 | ``` 130 | 131 | The tests will seed the vault server as needed. 132 | -------------------------------------------------------------------------------- /docs/Installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | # Using Helm plugin manager 4 | 5 | ## Helm 4 6 | 7 | Helm 4 introduced a new plugin system that requires splitting plugins into multiple packages when they have multiple capabilities. Therefore, `helm-secrets` is now distributed as three separate plugins: 8 | - `helm-secrets`: The core plugin that provides the main functionality. 9 | - `helm-secrets-getter`: A plugin that adds support for secret getters, e.g. `secrets://`. 10 | - `helm-secrets-post-renderer`: A plugin that adds support for post-rendering. 11 | 12 | ### Verification 13 | 14 | Verification of plugins is supported in Helm 4 and **enabled by default**. You can choose 15 | to verify the plugins during installation by omitting the `--verify=false` flag. 16 | 17 | Public Key for verification can be found here: https://github.com/jkroepke.gpg 18 | 19 | ### Install a specific version (recommend). 20 | 21 | The `--version` flag is not supported in Helm 4, so you need to specify the exact download URL for the desired version. 22 | 23 | Click [here](https://github.com/jkroepke/helm-secrets/releases/latest) for the latest version. 24 | 25 | ```bash 26 | helm plugin install https://github.com/jkroepke/helm-secrets/releases/download/v4.7.4/secrets-4.7.4.tgz 27 | helm plugin install https://github.com/jkroepke/helm-secrets/releases/download/v4.7.4/secrets-getter-4.7.4.tgz 28 | helm plugin install https://github.com/jkroepke/helm-secrets/releases/download/v4.7.4/secrets-post-renderer-4.7.4.tgz 29 | ``` 30 | 31 | ### Install latest version 32 | 33 | Helm forces `-.tgz` naming convention for plugin packages. 34 | Therefore, to install the latest version, 35 | you need to fetch the latest version number first and then construct the download URLs accordingly. 36 | 37 | ## Helm 3 38 | 39 | Install a specific version (recommend). 40 | Click [here](https://github.com/jkroepke/helm-secrets/releases/latest) for the latest version. 41 | ```bash 42 | helm plugin install https://github.com/jkroepke/helm-secrets --version v4.7.4 43 | ``` 44 | 45 | Install latest unstable version from main branch 46 | ```bash 47 | helm plugin install https://github.com/jkroepke/helm-secrets 48 | ``` 49 | 50 | Find the latest version here: https://github.com/jkroepke/helm-secrets/releases/latest 51 | 52 | See [Secret Backend manual](https://github.com/jkroepke/helm-secrets/wiki/Secret-Backends#list-of-implemented-secret-backends) for additional installation tasks. 53 | 54 | # Manual installation 55 | 56 | Works for Helm 2 and Helm 3. 57 | 58 | ## Latest version 59 | 60 | Windows (inside cmd, need to be verified) 61 | ```bash 62 | curl -LsSf https://github.com/jkroepke/helm-secrets/releases/latest/download/helm-secrets.tar.gz | tar -C "%APPDATA%\helm\plugins" -xzf- 63 | ``` 64 | MacOS / Linux 65 | ```bash 66 | curl -LsSf https://github.com/jkroepke/helm-secrets/releases/latest/download/helm-secrets.tar.gz | tar -C "$(helm env HELM_PLUGINS)" -xzf- 67 | ``` 68 | 69 | ## Specific version 70 | 71 | Windows (inside cmd, need to be verified) 72 | ```bash 73 | curl -LsSf https://github.com/jkroepke/helm-secrets/releases/download/v3.12.0/helm-secrets.tar.gz | tar -C "%APPDATA%\helm\plugins" -xzf- 74 | ``` 75 | MacOS / Linux 76 | ```bash 77 | curl -LsSf https://github.com/jkroepke/helm-secrets/releases/download/v3.12.0/helm-secrets.tar.gz | tar -C "$(helm env HELM_PLUGINS)" -xzf- 78 | ``` 79 | 80 | # Helm 2 81 | 82 | Helm 2 doesn't support downloading plugins. Since unknown keys in `plugin.yaml` are fatal plugin installation needs special handling. 83 | 84 | Error on Helm 2 installation: 85 | 86 | ``` 87 | # helm plugin install https://github.com/jkroepke/helm-secrets 88 | Error: yaml: unmarshal errors: 89 | line 12: field platformCommand not found in type plugin.Metadata 90 | ``` 91 | 92 | ## Installation on Helm 2 93 | 94 | 1. Install helm-secrets via [manual installation](#manual-installation) but extract inside helm2 plugin directory e.g.: `$(helm home)/plugins/` 95 | 2. Strip `platformCommand` from `plugin.yaml` like: 96 | ``` 97 | sed -i '/platformCommand:/,+2 d' "${HELM_HOME:-"${HOME}/.helm"}/plugins/helm-secrets*/plugin.yaml" 98 | ``` 99 | 3. Done 100 | 101 | Click [here](https://github.com/adorsys-containers/ci-helm/blob/f9a8a5bf8953ab876266ca39ccbdb49228e9f117/images/2.17/Dockerfile#L91), for an example! 102 | -------------------------------------------------------------------------------- /scripts/lib/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -euf 4 | 5 | is_help() { 6 | case "$1" in 7 | -h | --help | help) 8 | true 9 | ;; 10 | *) 11 | false 12 | ;; 13 | esac 14 | } 15 | 16 | log() { 17 | if [ $# -le 1 ]; then 18 | printf '[helm-secrets] %s\n' "${1:-}" >&2 19 | else 20 | format="${1}" 21 | shift 22 | 23 | # shellcheck disable=SC2059 24 | printf "[helm-secrets] $format\n" "$@" >&2 25 | fi 26 | } 27 | 28 | error() { 29 | log "$@" 30 | } 31 | 32 | fatal() { 33 | error "$@" 34 | 35 | exit 1 36 | } 37 | 38 | _regex_escape() { 39 | # This is a function because dealing with quotes is a pain. 40 | # http://stackoverflow.com/a/2705678/120999 41 | sed -e 's/[]\/()$*.^|[]/\\&/g' 42 | } 43 | 44 | _trap() { 45 | if command -v _trap_hook >/dev/null; then 46 | _trap_hook 47 | fi 48 | 49 | if [ -n "${_GNUPGHOME+x}" ]; then 50 | if [ -f "${_GNUPGHOME}/.helm-secrets" ]; then 51 | # On CentOS 7, there is no kill option 52 | case $(gpgconf --help 2>&1) in 53 | *--kill*) 54 | gpgconf --kill gpg-agent 55 | ;; 56 | esac 57 | fi 58 | fi 59 | 60 | rm -rf "${TMPDIR}" 61 | } 62 | 63 | # MacOS syntax and behavior is different for mktemp 64 | # https://unix.stackexchange.com/a/555214 65 | _mktemp() { 66 | # ksh/posh - @: parameter not set 67 | # https://stackoverflow.com/a/35242773 68 | if [ $# -eq 0 ]; then 69 | mktemp "${TMPDIR}/XXXXXX" 70 | else 71 | mktemp "$@" "${TMPDIR}/XXXXXX" 72 | fi 73 | } 74 | 75 | _gpg_load_keys() { 76 | _GNUPGHOME=$(_mktemp -d) 77 | touch "${_GNUPGHOME}/.helm-secrets" 78 | 79 | export GNUPGHOME="${_GNUPGHOME}" 80 | for key in ${LOAD_GPG_KEYS}; do 81 | if [ -d "${key}" ]; then 82 | set +f 83 | for file in "${key%%/}/"*; do 84 | gpg --batch --no-permission-warning --quiet --import "${file}" 85 | done 86 | set -f 87 | else 88 | gpg --batch --no-permission-warning --quiet --import "${key}" 89 | fi 90 | done 91 | } 92 | 93 | on_wsl() { false; } 94 | on_cygwin() { false; } 95 | _sed_i() { sed -i "$@"; } 96 | _winpath() { printf '%s' "${1}"; } 97 | _helm_winpath() { printf '%s' "${1}"; } 98 | _helm_bin() { printf '%s' "${HELM_BIN}"; } 99 | 100 | case "$(uname -s)" in 101 | CYGWIN* | MINGW64_NT*) 102 | HELM_BIN="$(cygpath -u "${HELM_BIN}")" 103 | 104 | on_cygwin() { true; } 105 | _winpath() { 106 | if [ "${2:-0}" = "1" ]; then 107 | printf '%s' "${1}" | cygpath -w -l -f - | sed -e 's!\\!\\\\!g' 108 | else 109 | printf '%s' "${1}" | cygpath -w -l -f - 110 | fi 111 | } 112 | 113 | _helm_winpath() { _winpath "$@"; } 114 | _helm_bin() { _winpath "${HELM_BIN}"; } 115 | 116 | _sed_i 's! - command: .*! - command: "scripts/wrapper/run.cmd downloader"!' "${HELM_PLUGIN_DIR}/plugin.yaml" 117 | ;; 118 | Darwin) 119 | case $(sed --help 2>&1) in 120 | *BusyBox* | *GNU*) ;; 121 | *) _sed_i() { sed -i '' "$@"; } ;; 122 | esac 123 | ;; 124 | *) 125 | # Check of WSL 126 | if [ -f /proc/version ] && grep -qi microsoft /proc/version; then 127 | on_wsl() { true; } 128 | _winpath() { 129 | touch "${1}" 130 | if [ "${2:-0}" = "1" ]; then 131 | wslpath -w "${1}" | sed -e 's!\\!\\\\!g' 132 | else 133 | wslpath -w "${1}" 134 | fi 135 | } 136 | 137 | # We are on a Linux VM, but helm.exe (Win32) is called 138 | case "${HELM_BIN}" in 139 | *.exe) 140 | _helm_winpath() { _winpath "$@"; } 141 | 142 | _sed_i 's! - command: .*! - command: "scripts/wrapper/run.cmd downloader"!' "${HELM_PLUGIN_DIR}/plugin.yaml" 143 | ;; 144 | esac 145 | fi 146 | ;; 147 | esac 148 | 149 | case $("${HELM_BIN}" version --short) in 150 | v2*) 151 | _helm_version() { echo 2; } 152 | ;; 153 | v3*) 154 | _helm_version() { echo 3; } 155 | ;; 156 | v4*) 157 | _helm_version() { echo 4; } 158 | ;; 159 | *) 160 | fatal "Unsupported helm version: $("${HELM_BIN}" version --short)" 161 | ;; 162 | esac 163 | -------------------------------------------------------------------------------- /docs/Secret Backends.md: -------------------------------------------------------------------------------- 1 | # Secret Backends 2 | 3 | helm-secret support multiple backend. [sops](https://github.com/getsops/sops) and [vals](https://github.com/variantdev/vals). 4 | sops is good for on-premise installation. vals can be used to fetch secrets from external systems like AWS Secrets Manager or Azure KeyVault. 5 | 6 | Example for in-tree backends via an CLI option 7 | ```bash 8 | helm secrets -b sops decrypt ./tests/assets/helm_vars/secrets.yaml 9 | ``` 10 | 11 | Example for in-tree backends via environment variable 12 | ```bash 13 | HELM_SECRETS_BACKEND=vals helm secrets decrypt ./tests/assets/helm_vars/secrets.yaml 14 | ``` 15 | 16 | Example for out-of-tree backends 17 | ```bash 18 | helm secrets -b ./path/to/backend.sh decrypt ./tests/assets/helm_vars/secrets.yaml 19 | ``` 20 | 21 | The backend option is a global one. A file level switch is supported, too. 22 | 23 | ```bash 24 | helm secrets template . -f 'sops!tests/assets/helm_vars/secrets.yaml' 25 | ``` 26 | 27 | For more information, read [USAGE.md](./Usage.md#override-backend-per-value-file) 28 | 29 | ## Implement an own secret backend 30 | 31 | Start by a copy of [sops backend](https://github.com/jkroepke/helm-secrets/blob/main/scripts/lib/backends/sops.sh) and adjust to your own needs. 32 | The custom backend can be load via `HELM_SECRETS_BACKEND` parameter or `-d` option (higher preference). 33 | 34 | ## Pass additional arguments to a secret backend 35 | 36 | ```bash 37 | helm secrets -a "--verbose" decrypt ./tests/assets/helm_vars/secrets.yaml 38 | ``` 39 | 40 | results into: 41 | 42 | ``` 43 | [PGP] INFO[0000] Decryption succeeded fingerprint=D6174A02027050E59C711075B430C4E58E2BBBA3 44 | [SOPS] INFO[0000] Data key recovered successfully 45 | [SOPS] DEBU[0000] Decrypting tree 46 | [helm-secrets] Decrypt: tests/assets/values/sops/secrets.yaml 47 | ==> Linting examples/sops 48 | [INFO] Chart.yaml: icon is recommended 49 | 50 | 1 chart(s) linted, 0 chart(s) failed 51 | 52 | [helm-secrets] Removed: tests/assets/values/sops/secrets.yaml.dec 53 | ``` 54 | 55 | ## Explicitly specify a binary path 56 | 57 | If e.g. `sops` is installed at the non-default location or if you have multiple versions of sops on your system, 58 | you can use `HELM_SECRETS_$BACKEND_PATH` to explicitly specify the sops binary to be used. 59 | 60 | ```bash 61 | # Example for in-tree backends via environment variable 62 | HELM_SECRETS_SOPS_PATH=/custom/location/sops helm secrets decrypt ./tests/assets/helm_vars/secrets.yaml 63 | HELM_SECRETS_VALS_PATH=/custom/location/vals helm secrets decrypt ./tests/assets/helm_vars/secrets.yaml 64 | ``` 65 | 66 | # List of implemented secret backends 67 | 68 | ## sops 69 | 70 | If you use sops with helm-secrets, the sops CLI tool is needed. 71 | sops 3.2.0 is required at a minimum. 72 | 73 | Download: https://github.com/getsops/sops/releases/latest 74 | 75 | Before starting using sops with gpg, consider starting to use [age](https://github.com/getsops/sops#encrypting-using-age). 76 | 77 | The sops secret store is enabled by default. 78 | 79 | ## vals 80 | 81 | [vals](https://github.com/variantdev/vals) is a tool for managing configuration values and secrets form various sources. 82 | 83 | To use vals with helm-secrets, the vals CLI binary is needed. vals 0.22.0 or higher is required. 84 | 85 | It supports various backends: 86 | 87 | * [Vault](https://github.com/variantdev/vals#vault) 88 | * [AWS SSM Parameter Store](https://github.com/variantdev/vals#aws-ssm-parameter-store) 89 | * [AWS Secrets Manager](https://github.com/variantdev/vals#aws-secrets-manager) 90 | * [AWS S3](https://github.com/variantdev/vals#aws-s3) 91 | * [GCP Secrets Manager](https://github.com/variantdev/vals#gcp-secrets-manager) 92 | * [Azure Key Vault](https://github.com/variantdev/vals#azure-key-vault) 93 | * [SOPS-encrypted files](https://github.com/variantdev/vals#sops) 94 | * [Terraform State](https://github.com/variantdev/vals#terraform-tfstate) 95 | * [Plain File](https://github.com/variantdev/vals#file) 96 | 97 | All clients are integrated into vals, no additional tools required. 98 | 99 | Download: https://github.com/variantdev/vals/releases/latest 100 | 101 | The vals secret backend can be enabled by define `HELM_SECRETS_BACKEND=vals`. 102 | 103 | Example file: [examples/vals/secrets.yaml](https://github.com/jkroepke/helm-secrets/blob/main/examples/vals/secrets.yaml) 104 | -------------------------------------------------------------------------------- /tests/lib/setup_suite.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | load '../lib/helper' 4 | load '../lib/binaries' 5 | 6 | setup_suite() { 7 | { 8 | export HELM_SECRETS_BACKEND="${HELM_SECRETS_BACKEND:-"sops"}" 9 | export HELM_SECRETS_CUSTOM_BACKEND=${HELM_SECRETS_CUSTOM_BACKEND:-""} 10 | 11 | if [[ "${HELM_SECRETS_BACKEND}" == "custom-"* ]]; then 12 | HELM_SECRETS_CUSTOM_BACKEND="${HELM_SECRETS_BACKEND}" 13 | unset HELM_SECRETS_BACKEND 14 | fi 15 | 16 | REAL_HOME="${HOME}" 17 | export HOME="${BATS_SUITE_TMPDIR}" 18 | [ -d "${HOME}" ] || mkdir -p "${HOME}" 19 | 20 | _uname="$(uname)" 21 | export _uname 22 | 23 | if [ -f "${REAL_HOME}/.gitconfig" ]; then 24 | cp "${REAL_HOME}/.gitconfig" "${HOME}/.gitconfig" 25 | fi 26 | 27 | # copy .kube from real home 28 | if [ -d "${REAL_HOME}/.kube" ]; then 29 | ln -sf "${REAL_HOME}/.kube" "${HOME}/.kube" 30 | fi 31 | 32 | CURRENT_TEST_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" 33 | export GIT_ROOT="${CURRENT_TEST_DIR}/../.." 34 | export TEST_ROOT="${GIT_ROOT}/tests" 35 | 36 | export CACHE_DIR="${TEST_ROOT}/.tmp/cache" 37 | export HELM_CACHE="${CACHE_DIR}/${_uname}/helm" 38 | 39 | mkdir -p "${HELM_CACHE}" 40 | 41 | HELM_DATA_HOME="$(_winpath "${HELM_CACHE}")" 42 | export HELM_DATA_HOME 43 | 44 | if [ ! -d "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}/" ]; then 45 | mkdir -p "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}/" 46 | "${HELM_BIN}" create "$(_winpath "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}")" 47 | 48 | if [ -d "${TEST_ROOT}/assets/values/${HELM_SECRETS_BACKEND}/templates/base/" ]; then 49 | cp -r "${TEST_ROOT}/assets/values/${HELM_SECRETS_BACKEND}/templates/base/." "${HELM_CACHE}/chart/${HELM_SECRETS_BACKEND}/templates/" 50 | fi 51 | fi 52 | 53 | helm_plugin_install "secrets" 54 | helm_plugin_install "git" 55 | 56 | if [[ "${CURRENT_TEST_DIR}" = *"/it" ]]; then 57 | helm_plugin_install "diff" --version 3.5.0 58 | fi 59 | 60 | mkdir -p "$HOME/.gnupg/" 61 | touch "$HOME/.gnupg/common.conf" 62 | 63 | GPG_PRIVATE_KEY="$(_winpath "${TEST_ROOT}/assets/gpg/private.gpg")" 64 | "${GPG_BIN}" --batch --import "${GPG_PRIVATE_KEY}" 65 | export _TEST_KEY="-----BEGIN PGP MESSAGE----- 66 | wcFMAxYpv4YXKfBAARAAVzE7/FMD7+UWwMls23zKKLoTs+5w9GMvugn0wi5KOJ8P 67 | PSrRY4r27VhwQH38gWDrzo3RCmO9414xZ0JW0HaN2Pgd3ml6mYCY/5RE7apgGZQI 68 | 3Im0fv8bhIwaP2UWPp74EXLzA3mh1dUtwxmuWOeoSq+Vm5NtbjkfUt/4MIcF5IAY 69 | c+U4ZOdQlzgExwu+VtOpeBrkwfglh5fFuKqM8Fg1IICi/Pp6YAlpAdGqlt1zS4Pj 70 | yjAS6eAvnpM0eA5hShuoO9JsAu4kVjaaBlipVpc1I2zdcT3H/1d7ASziwbKOm6jE 71 | PJxzaMDxn0UfMjkhTaTZ8v27lz6W7qdlHdCWGGI348QkSoDotm7OzMC7ZLfps3+9 72 | GrXo9Kwxkj6oy/thn92W2cRSeSD28g6kcUkHeG8L3mMv+gpTjIhM+Z8x3jJcVp2i 73 | yoA2dO/kO2/HTcUfnEjppKigqUlRuKfDn8ercjYiq+foqtimH192iXXyRmltYlH0 74 | GUSJ1FcNLAC9g0WLFPQnMFh5KxSweavpbdd6PILqEsyKvZpC5a+hzLKwGjWOveW1 75 | K34QZf6Ay3CPCegAyGVjxmsg1vPKD+9WAZinveCl37l3cCQW1VZzbGkHgtLQ30Qr 76 | DCRFZEstraLAQUf6VLAk9bPYX/fvkXmra970i/CfJjIg0SpOXbADBR4x+zRRZqrS 77 | 4AHkWTmhH/xXWyAgmh+sGs18OOFGfeC04AjhMmvg4uKzly6+4IDlNhPif2VpJYOi 78 | EmU8gQoUsAHKYro0hPfzBZyJlL+TqCPgHeRPANVgm4Ww6RlVrNFpTy9H4m4s5y/h 79 | EzAA 80 | =jf7D 81 | -----END PGP MESSAGE-----" 82 | export _TEST_global_secret=global_bar 83 | export _TEST_SERVICE_PORT=81 84 | export _TEST_SOME_SERVICE_PORT=83 85 | 86 | if on_windows; then 87 | # remove symlink, since its not supported on windows 88 | find "${TEST_ROOT}" -name secrets.symlink.yaml -delete 89 | fi 90 | } >&2 91 | } 92 | 93 | teardown_suite() { 94 | { 95 | "${GPGCONF_BIN}" --kill gpg-agent 96 | } >&3 97 | } 98 | 99 | helm_plugin_install() { 100 | { 101 | if "${HELM_BIN}" plugin list | grep -q "${1}"; then 102 | return 103 | fi 104 | 105 | case "${1}" in 106 | diff) 107 | URL="https://github.com/databus23/helm-diff" 108 | # renovate: github=databus23/helm-diff 109 | VERSION=v3.11.0 110 | ;; 111 | git) 112 | URL="https://github.com/aslafy-z/helm-git" 113 | # renovate: github=aslafy-z/helm-git 114 | VERSION=v1.3.0 115 | ;; 116 | secrets) 117 | URL="$(_winpath "${GIT_ROOT}")" 118 | if helm_version_greater_or_equal_than 4.0.0; then 119 | "${HELM_BIN}" plugin install "${URL}/plugins/helm-secrets-getter" 120 | "${HELM_BIN}" plugin install "${URL}/plugins/helm-secrets-post-renderer" 121 | URL="${URL}/plugins/helm-secrets-cli" 122 | fi 123 | ;; 124 | esac 125 | 126 | VERIFY="" 127 | 128 | if helm_version_greater_or_equal_than 4.0.0; then 129 | VERIFY="--verify=false" 130 | fi 131 | 132 | "${HELM_BIN}" plugin install "${URL}" "${@:2}" ${VERSION:+--version "${VERSION}"} ${VERIFY} 133 | } >&2 134 | } 135 | -------------------------------------------------------------------------------- /tests/unit/secret-backends.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load '../lib/helper' 4 | load '../bats/extensions/bats-support/load' 5 | load '../bats/extensions/bats-assert/load' 6 | load '../bats/extensions/bats-file/load' 7 | 8 | @test "secret-backend: helm secrets -b" { 9 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 10 | 11 | run "${HELM_BIN}" secrets -b nonexists decrypt "${FILE}" 12 | 13 | assert_output --partial "Can't find secret backend: nonexists" 14 | assert_failure 15 | } 16 | 17 | @test "secret-backend: helm secrets --backend" { 18 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 19 | 20 | run "${HELM_BIN}" secrets --backend nonexists decrypt "${FILE}" 21 | 22 | assert_output --partial "Can't find secret backend: nonexists" 23 | assert_failure 24 | } 25 | 26 | @test "secret-backend: helm secrets --backend=" { 27 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 28 | 29 | run "${HELM_BIN}" secrets --backend=nonexists decrypt "${FILE}" 30 | 31 | assert_output --partial "Can't find secret backend: nonexists" 32 | assert_failure 33 | } 34 | 35 | @test "secret-backend: helm secrets --backend=nonexists + HELM_SECRETS_ALLOWED_BACKENDS=noop" { 36 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 37 | 38 | run env HELM_SECRETS_ALLOWED_BACKENDS=noop WSLENV="HELM_SECRETS_ALLOWED_BACKENDS:${WSLENV}" \ 39 | "${HELM_BIN}" secrets --backend=nonexists decrypt "${FILE}" 40 | 41 | assert_output --partial "secret backend '${HELM_SECRETS_BACKEND}' not allowed" 42 | assert_failure 43 | } 44 | 45 | @test "secret-backend: helm secrets + env HELM_SECRETS_BACKEND" { 46 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 47 | 48 | run env HELM_SECRETS_BACKEND=nonexists WSLENV="HELM_SECRETS_BACKEND:${WSLENV}" \ 49 | "${HELM_BIN}" secrets decrypt "${FILE}" 50 | 51 | assert_output --partial "Can't find secret backend: nonexists" 52 | assert_failure 53 | } 54 | 55 | @test "secret-backend: helm secrets -b noop" { 56 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 57 | 58 | run "${HELM_BIN}" secrets -b noop decrypt "${FILE}" 59 | 60 | assert_output -e "\[helm-secrets\] File is not encrypted: .*" 61 | assert_failure 62 | } 63 | 64 | @test "secret-backend: helm secrets --backend noop" { 65 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 66 | 67 | run "${HELM_BIN}" secrets --backend noop decrypt "${FILE}" 68 | 69 | assert_output -e "\[helm-secrets\] File is not encrypted: .*" 70 | assert_failure 71 | } 72 | 73 | @test "secret-backend: helm secrets -b noop + q flag" { 74 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 75 | 76 | run "${HELM_BIN}" secrets -q -b noop decrypt "${FILE}" 77 | 78 | assert_output -e "\[helm-secrets\] File is not encrypted: .*" 79 | assert_failure 80 | } 81 | 82 | @test "secret-backend: helm secrets + env HELM_SECRETS_BACKEND=noop" { 83 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 84 | 85 | run env HELM_SECRETS_BACKEND=noop WSLENV="HELM_SECRETS_BACKEND:${WSLENV}" "${HELM_BIN}" secrets decrypt "${FILE}" 86 | 87 | assert_output -e "\[helm-secrets\] File is not encrypted: .*" 88 | assert_failure 89 | } 90 | 91 | @test "secret-backend: helm secrets + prefer cli arg -b noop over env" { 92 | FILE="${TEST_TEMP_DIR}/assets/values/sops/secrets.yaml" 93 | 94 | run env HELM_SECRETS_BACKEND=sops WSLENV="HELM_SECRETS_BACKEND:${WSLENV}" "${HELM_BIN}" secrets -b noop decrypt "${FILE}" 95 | 96 | assert_output -e "\[helm-secrets\] File is not encrypted: .*" 97 | assert_failure 98 | } 99 | 100 | @test "secret-backend: helm secrets --backend assets/custom-backend.sh" { 101 | FILE="${TEST_TEMP_DIR}/assets/values/custom-backend/secrets.yaml" 102 | 103 | run "${HELM_BIN}" secrets --backend "${TEST_TEMP_DIR}/assets/custom-backend.sh" decrypt "${FILE}" 104 | 105 | refute_output --partial '!vault' 106 | assert_output --partial 'production#global_secret' 107 | assert_success 108 | } 109 | 110 | @test "secret-backend: helm secrets + env HELM_SECRETS_BACKEND=assets/custom-backend.sh" { 111 | FILE="${TEST_TEMP_DIR}/assets/values/custom-backend/secrets.yaml" 112 | 113 | run env HELM_SECRETS_BACKEND="${TEST_TEMP_DIR}/assets/custom-backend.sh" WSLENV="HELM_SECRETS_BACKEND:${WSLENV}" "${HELM_BIN}" secrets decrypt "${FILE}" 114 | 115 | refute_output --partial '!vault' 116 | assert_output --partial 'production#global_secret' 117 | assert_success 118 | } 119 | 120 | @test "secret-backend: helm secrets --backend ${GIT_ROOT}/examples/backends/onepassword.sh" { 121 | if ! is_custom_backend "onepassword"; then 122 | skip 123 | fi 124 | 125 | FILE="${TEST_TEMP_DIR}/assets/values/custom-backend/onepassword-secrets.yaml" 126 | 127 | run "${HELM_BIN}" secrets --backend "${GIT_ROOT}/examples/backends/onepassword.sh" decrypt "${FILE}" 128 | 129 | refute_output --partial 'op://' 130 | assert_output --partial 'test-username' 131 | assert_output --partial 'mytestpassword123' 132 | assert_output --partial 'a-test-name' 133 | assert_output --partial 'my-test@example.com' 134 | assert_success 135 | } 136 | 137 | @test "secret-backend: helm secrets + env HELM_SECRETS_BACKEND=${GIT_ROOT}/examples/backends/onepassword.sh" { 138 | if ! is_custom_backend "onepassword"; then 139 | skip 140 | fi 141 | 142 | FILE="${TEST_TEMP_DIR}/assets/values/custom-backend/onepassword-secrets.yaml" 143 | 144 | run env HELM_SECRETS_BACKEND="${GIT_ROOT}/examples/backends/onepassword.sh" WSLENV="HELM_SECRETS_BACKEND:${WSLENV}" "${HELM_BIN}" secrets decrypt "${FILE}" 145 | 146 | refute_output --partial 'op://' 147 | assert_output --partial 'test-username' 148 | assert_output --partial 'mytestpassword123' 149 | assert_output --partial 'a-test-name' 150 | assert_output --partial 'my-test@example.com' 151 | assert_success 152 | } 153 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | mail@jkroepke.de. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /tests/assets/gpg/private2.gpg: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | 3 | 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 105 | =a5LM 106 | -----END PGP PRIVATE KEY BLOCK----- 107 | -------------------------------------------------------------------------------- /docs/Cloud Integration.md: -------------------------------------------------------------------------------- 1 | # Cloud Integration 2 | 3 | In cloud environments, secrets are stored inside a native secret manager. 4 | 5 | This documents describes the vals secrets to dynamically fetch secrets from cloud services directly. 6 | 7 | This integration is also supported inside [ArgoCD](https://github.com/jkroepke/helm-secrets/wiki/ArgoCD-Integration). 8 | 9 | # Prerequisites 10 | 11 | - helm-secrets [3.9.x](https://github.com/jkroepke/helm-secrets/releases/tag/v3.9.1) or higher. (literal values requires 4.1+) 12 | - [vals](https://github.com/variantdev/vals) backend usage 13 | 14 | ## Setup 15 | 16 | [vals](https://github.com/variantdev/vals) needs to be setup correctly first. 17 | Download `vals` from [GitHub](https://github.com/variantdev/vals/releases/latest) and put the binary into your `PATH`. e.g. `/usr/local/bin/`. 18 | Alternatively, use the environment variable `HELM_SECRETS_VALS_PATH` to define the path of the vals binary. 19 | 20 | # Authentication 21 | 22 | ## AWS 23 | 24 | AWS supports a multiple mechanism for authentication: 25 | 26 | 1. Define `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables 27 | 2. OIDC login flows / IAM Roles for services accounts 28 | 3. Default credential and config profiles in `~/.aws/credentials` and `~/.aws/config` 29 | 4. Instance profile credentials 30 | 31 | ## Azure 32 | 33 | Azure supports a multiple mechanism for authentication through environment variables 34 | 35 | 1. **Client Credentials**: Azure AD Application ID and Secret. 36 | 37 | - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. 38 | - `AZURE_CLIENT_ID`: Specifies the app client ID to use. 39 | - `AZURE_CLIENT_SECRET`: Specifies the app secret to use. 40 | 41 | 2. **Client Certificate**: Azure AD Application ID and X.509 Certificate. 42 | 43 | - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. 44 | - `AZURE_CLIENT_ID`: Specifies the app client ID to use. 45 | - `AZURE_CERTIFICATE_PATH`: Specifies the certificate Path to use. 46 | - `AZURE_CERTIFICATE_PASSWORD`: Specifies the certificate password to use. 47 | 48 | 3. **Resource Owner Password**: Azure AD User and Password. This grant type is *not 49 | recommended*, use device login instead if you need interactive login. 50 | 51 | - `AZURE_TENANT_ID`: Specifies the Tenant to which to authenticate. 52 | - `AZURE_CLIENT_ID`: Specifies the app client ID to use. 53 | - `AZURE_USERNAME`: Specifies the username to use. 54 | - `AZURE_PASSWORD`: Specifies the password to use. 55 | 56 | 4. **Azure Managed Service Identity**: Delegate credential management to the platform. 57 | Requires that code is running in Azure, e.g. on a VM. 58 | Azure SDK handles all configurations. 59 | See [Azure Managed Service Identity](https://docs.microsoft.com/azure/active-directory/msi-overview) 60 | for more details. 61 | 62 | # Usage 63 | 64 | Before running `helm`, the environment variable `HELM_SECRETS_BACKEND=vals` needs 65 | to be set or the command line option `--backend=vals` must be put in use. 66 | 67 | This enables the `vals` integration in helm-secrets. 68 | 69 | Vals needs cloud prover credentials to fetch secrets from the secret services. 70 | Be sure to have them in place before trying to use (for instance, use the cloud 71 | provider own CLI to fetch the same secrets). 72 | 73 | helm-secrets can not fill the cloud provider secrets store through the encryption command. 74 | 75 | > :warning: Vals reference strings must be declared in the "values" file (the 76 | > YAML file being used by the Helm template to provide values), not in the 77 | > resource itself! 78 | 79 | This is how you are suppose to do. First create a `Secret` (or anyother 80 | resource you want) file, that we will call `secret.yaml` in this example: 81 | 82 | ```yaml 83 | --- 84 | apiVersion: v1 85 | kind: Secret 86 | metadata: 87 | name: '{{ .Chart.Name }}-secrets' 88 | labels: 89 | app.kubernetes.io/name: '{{ .Chart.Name }}-secrets' 90 | app.kubernetes.io/version: '{{ .Chart.AppVersion | toString }}' 91 | namespace: '{{ .Values.namespace }}' 92 | name: '{{ .Chart.Name }}' 93 | repository: '{{ .Chart.Home }}' 94 | type: Opaque 95 | data: 96 | supersecret: '{{ .Values.aws | b64enc }}' 97 | ``` 98 | 99 | The `secret.yaml` should be saved into the `templates` folder of your helm chart. 100 | 101 | Next, create a values file named `values.yaml` and add the following snippet 102 | content: 103 | 104 | ```yaml 105 | aws: ref+awssecrets://mysecret/value 106 | ``` 107 | 108 | Finally, put everything together: 109 | 110 | ``` 111 | helm secrets template -f values.yaml . 112 | ``` 113 | 114 | That's it! You should see the resulting content from the template. 115 | 116 | ## Supported Backends 117 | 118 | `vals` support different backends. Click on the backend to gain more information. 119 | 120 | - [Vault](https://github.com/variantdev/vals/blob/main/README.md#vault) 121 | - [AWS SSM Parameter Store](https://github.com/variantdev/vals/blob/main/README.md#aws-ssm-parameter-store) 122 | - [AWS Secrets Manager](https://github.com/variantdev/vals/blob/main/README.md#aws-secrets-manager) 123 | - [AWS S3](https://github.com/variantdev/vals/blob/main/README.md#aws-s3) 124 | - [GCP Secrets Manager](https://github.com/variantdev/vals/blob/main/README.md#gcp-secrets-manager) 125 | - [Google GCS](https://github.com/variantdev/vals/blob/main/README.md#google-gcs) 126 | - [SOPS](https://github.com/variantdev/vals/blob/main/README.md#sops) powered by [sops](https://github.com/getsops/sops) 127 | - [Terraform (tfstate)](https://github.com/variantdev/vals/blob/main/README.md#terraform-tfstate) powered by [tfstate-lookup](https://github.com/fujiwara/tfstate-lookup) 128 | - [Echo](https://github.com/variantdev/vals/blob/main/README.md#echo) 129 | - [File](https://github.com/variantdev/vals/blob/main/README.md#file) 130 | - [Azure Key Vault](https://github.com/variantdev/vals/blob/main/README.md#azure-key-vault) 131 | - [EnvSubst](https://github.com/variantdev/vals/blob/main/README.md#envsubst) 132 | - [Gitlab CI Secrets](https://github.com/variantdev/vals/blob/main/README.md#gitlab-secrets) 133 | 134 | 135 | # Example secret.yaml 136 | 137 | ```yaml 138 | vault: ref+vault://mykv/foo#/bar 139 | aws: ref+awssecrets://mysecret/value 140 | aws-ssm: ref+awsssm://foo/bar?mode=singleparam#/BAR 141 | gcp: ref+gcpsecrets://PROJECT/SECRET[?version=VERSION] 142 | azure: ref+azurekeyvault://my-vault/secret-a 143 | sops: ref+sops://assets/values/vals/secrets.sops.yaml#/key 144 | file: ref+file:///absolute/path/to/file[#/path/to/the/value] 145 | service: 146 | port: ref+envsubst://$VAR1 147 | ``` 148 | 149 | # Example literal values 150 | 151 | ```bash 152 | export HELM_SECRETS_BACKEND=vals 153 | helm secrets template bitnami/mysql --name-template mysql \ 154 | --set auth.rootPassword=ref+awsssm://foo/bar?mode=singleparam#/BAR 155 | ``` 156 | 157 | wrapper-less environment like ArgoCD through downloader syntax (`--set-file` only): 158 | 159 | ```bash 160 | export HELM_SECRETS_BACKEND=vals 161 | helm template bitnami/mysql --name-template mysql \ 162 | --set-file auth.rootPassword=secrets+literal://ref+azurekeyvault://my-vault/secret-a 163 | ``` 164 | --------------------------------------------------------------------------------