on
Using Sops (Secrets OPerationS) with Helm: Plugin-less solution
Background:
If you are a Helm (Kubernetes package manager) user, you know that Helm provides no default way of protecting secrets, especially if all the senstive values are stored along with the Helm Chart in the same repository. helm-secrets plugin can help with that, but one can also roll up his custom solution. helm-secrets
forces the user to use a specific directory structure (helm_vars
directory that holds all env and secret variables). This may not seem like a bummer to some, but it can be an enough reason for others to ditch the plugin.
helm-secrets
can be seen as a Helm wrapper for Sops, a tool that allows protecting your sensitive files through cryptography. Using AES, Sops encrypts files (several formats are supported like Yaml and Json), and also appends metadata that will help in the decryption later. The use cases for Sops can vary, but one of the most common usage is securing files stored in versioning systems like git.
Encrypting / Decrypting secrets
Lets’s suppose we have the following secrets file that we would like to use in our Helm Chart
secret_example.yaml
SECRET_VALUE1: secret_value1
SECRET_VALUE2: secret_value2
The result of encrypting the file using a gpg2 key (running: sops -e -i --pgp 30FE2103DB8D388B0BDBFC2222C46635D8ED0E9 secret-example.yaml
):
SECRET_VALUE1: ENC[AES256_GCM,data:UzqJmLcBZWSBS119ZQ==,iv:x6P1RIMnNAXc0FXQthAAUG23rdM5pZPizAYYh5eqfQg=,tag:Ack/ltD57S69QBlHt4ehqw==,type:str]
SECRET_VALUE2: ENC[AES256_GCM,data:Ah+U8rMw7jqfHwL32g==,iv:AMbSUtzj1XFjR1agKGpZyms3Dxhqm+wdnSWc5FWiqMI=,tag:LWdhBypGitnRU/xLtM0Q+w==,type:str]
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
lastmodified: '2020-11-21T20:00:27Z'
mac: ENC[AES256_GCM,data:Ii2i5V9uX+hxF7bE/esYwGqoUwojtm+vrzWSIFdQe3dZ0d353qp13PRKOcsiQUTJVzXwEszwA6SWJTl/lvA0vmkN+sX0HISsfBvcgBlE1QPxLoG46ST71/rJhUY3z1RfDlOQFx4QMYMRSStzFSSOrkqR1krFpEZAA0tK2jV0h9E=,iv:jduQPej9/DPkDnwGBJc7Wrnf/XB/NiqkPUiuMcYyrn8=,tag:LP41QIlD6H0F05WFgHgrYg==,type:str]
pgp:
- created_at: '2020-11-21T20:00:27Z'
enc: |-
-----BEGIN PGP MESSAGE-----
wcDMA7p1B6ApVZDfAQwAVyuMdmxnKMwyC/lf7QyKM0HxWBRszhZOb5njCDtasemy
4646VfwYRBECGMKn0kU0zwZqmHV8o9DJd4NPHTAtB6HKuMurt8mt1YUVQU2+kaL2
K7EWrWbuVwKR55Bv55ORGI5HNIy49jtSS/qdfIjgTO18dBgap/FOkK5fBNgxbNXI
nqstcsqFT5Qsqb60q5rR7vaC0Riy6nqQM+slHW2ippSc1LgfCmxvNuDJw89oNLbq
nrEBmkuYoyxAROuIDLkNiNujFvZ8ei/5vDdw7q41v9vkt4oEt2hjO9sy7EUho6ry
m8PH1u3/w6Cz8Tg2We0rh6w94TTw/8Ukkxw7lBoZ2VCoc/sf+1Z1YDfC0kTlBqj9
SUxBCp0jViDKShijDlFvMhjcFNJZuCe2yecR6d2qh700o9SckTdKWPMEvWUp07ND
ZCy8Ko4yOc6UrN6kqKN7rHk7nLjVrlf7t3aJ9VGalFDXO/CZyqKWvN6oTXOQB4Pw
hafSv63nWFGBTYl5lOin0uYBPgZEn8IcaWO1+jF4Kg2aCp1xln5Pcc4+lzhxfkiJ
OQ2qF1WNCMAici5S2htNt5XEAP+I6iB6l1ukijqNqspq5AhcUOWWTPyAwzO9fOD4
J/ri1X4sHwA=
=7E7x
-----END PGP MESSAGE-----
fp: 30FE2103DB8D388B0BDBFC2222C46635D8ED0E9
unencrypted_suffix: _unencrypted
version: 3.6.1
Explanation: Sops encrypted the values in the original file and added a sops
metadata section at the bottom with references to the type and the id of the key used to encrypt the values
Now the secret can be safely stored (in git) along with the Helm Chart.
The secret file values can be decrypted before deploying/updating the Helm Chart, and then used to create the Kubernetes secrets.
Secret Helm Template
Let’s suppose we are storing all of our secret files in a folder named secrets
in the root path of the Helm Chart, then using a simple bash script, we can loop through the files and decrypt them.
ls -1 secrets/ | while read file; do sops -d --pgp $GPG_KEY_ID secrets/$file > secrets/${file:0:(-5)}.dec.yaml; done
Explanation: We have given the files the .dec.yaml
extension so that we can easily filter them from our Helm template, e.g secret-example.yaml
will be decrypted as secret-example.dec.yaml
We are now ready to generate our Kubernetes secrets using the Helm template.
{{- $files := .Files }}
{{ range $path, $bytes := .Files.Glob "secrets/**.dec.yaml" }}
{{- $secretData := $files.Get $path | fromYaml }}
apiVersion: v1
kind: Secret
metadata:
name: {{ trimPrefix "." (base $path | replace ".dec.yaml" "") }}-secret
namespace: default
type: Opaque
data:
{{- range $name, $secret := ($secretData) }}
{{ $name }}: {{ $secret | b64enc }}
{{- end }}
---
{{- end }}
Explanation: We loop through all the files with .dec.yaml
extention in the secret
directory and we create a secret with the name filename-without-extention-secret, e.g secret-example-secret
For convenience, we can put our script into a file e.g decrypt-secrets.sh
and call it whenever we want to call helm install
or helm upgrade
it’s also recommended to remove the .dec.yaml
files after the release is done.
Take away: if one knows how both Helm and Sops work, he can simply do without the plugin that can add unecessary complexity.