Helmfile: Feat: Allow simple Vault integration

Created on 2 Nov 2018  路  18Comments  路  Source: roboll/helmfile

Currently there are many users that want to integrate Vault with Kubernetes, but there are no high level tools for this. The current Kubernetes AuthMethod for Vault is too complex and coupled to the definition of the charts. This proposal could allow for a Vault integration with Kubernetes that is simple and decoupled.

This could be the structure of a helmfile integrated with Vault:

releases:
  - chart: .
    name: release-test
    version: 0.1.0
    values:
      - application:
           mysql_url: jdbc.....
           mysql_user: localtest
           mysql_password: localtest
    vault:
      active: true
      secrets:
        - vaultPath: secret-engine/my-application
          key: mysql.username
          targetVar: application.mysql_user
        - vaultPath: secret-engine/my-application
          key: mysql.password
          targetVar: application.mysql_password

Basically the project could use the VAULT_ADDR and VAULT_TOKEN environment variables to query all the secrets from Vault using the vaultPath and the key to inject them to the helm release using --set.

So, for that example the --set flags of the helm upgrade -i ... command would look like:

--set application.mysql_user=<queried-secret> --set application.mysql_password=<queried-secret>

If the vault.active field is set to false, or the vault field is not used, the release will just use the "localtest" values.

The main advantage I see for this proposal is that it allows centralization of secrets. Usually storing encrypted secrets in repositories result in secret-management-madness as there is no unique source of truth in case multiple repositories require the same secret. Vault solves this. Of course, this approach only addresses static Vault secrets (secrets in git repos are static too). For dynamic Vault secrets, I think there's no other alternative than the Kubernetes Auth Method.

There is one security issue with this proposal: Tiller has to use the --storage (Secret) backend instead of the ConfigMap backend to avoid fetching injected secrets with helm get values <release-name>. Although, I think this is also a problem for the helm-secrets pluign.

feature request

Most helpful comment

So I was wondering if it is possible to integrate a tool like gomplate to helmfile.

Relying on gomplate datasources, we can reliably add support for many backends including vault.

An example helmfile.yaml featuring the feature would look like:

releases:
  - chart: .
    name: release-test
    version: 0.1.0
    datasources:
      myvault: vault://vault.example.com:8200///secret-engine/my-application
    values:
      - application:
           mysql_url: jdbc.....
           # Note that {{` and `}} must be required to defer template execution. Otherwise it is executed on loading helmfile.yaml, which doesnt work due to that no datasources are loaded yet
           mysql_url: {{` {{ datasource "myvault" "mysql.username" }} `}}
           mysql_password: {{` {{ datasource "myvault" "mysql.password" }} `}}

All 18 comments

I would really like to PR this, but I don't know Golang. I just started learning it to be able to contribute, but I don't know how long it can take for me to learn and do a decent PR.

@AndresPineros Hey!

there is no unique source of truth in case multiple repositories require the same secret

Yeah, I believe this is definitely the thing we should consider.

A possible work-around as of today would be to use templating. That is, include something like {{ exec "./your/cmd/to/fetch/vault/secret/to/dump/yaml/or/json" (list "path.to.key") }} within a values.yaml.gotmpl under secrets: field of your helmfile.yaml.

Would it work?

I think this would be a great feature to directly support and essentially change the secrets handling into a plug-able system.

And please let me plug-in that my own use-case requires an integration with AWS Secrets Manager :)

So I was wondering if it is possible to integrate a tool like gomplate to helmfile.

Relying on gomplate datasources, we can reliably add support for many backends including vault.

An example helmfile.yaml featuring the feature would look like:

releases:
  - chart: .
    name: release-test
    version: 0.1.0
    datasources:
      myvault: vault://vault.example.com:8200///secret-engine/my-application
    values:
      - application:
           mysql_url: jdbc.....
           # Note that {{` and `}} must be required to defer template execution. Otherwise it is executed on loading helmfile.yaml, which doesnt work due to that no datasources are loaded yet
           mysql_url: {{` {{ datasource "myvault" "mysql.username" }} `}}
           mysql_password: {{` {{ datasource "myvault" "mysql.password" }} `}}

Data sources like gomplate would be great. Before I saw this issue, today I was thinking how it would be nice to access SSM Parameter store directly.

An alternative idea would be to focus on making secrets consumption from any backend declarative, while making the changes required to helmfile minimum.

That is, allow defining any secrets backend within a helmfile.yaml:

secretsProvider:
- name: vault
  command: "bin/helmfile-secrets-provider-vault"
  protocols:
  - "vault"

releases:
-  chart: .
    name: release-test
    version: 0.1.0
    secrets:
      - vault://vault.example.com:8200////secret-engine/my-application/?application.mysql_user=mysql.user,application.mysql_password=mysql.password

In the above example, bin/helmfile-vault-provider is executed like bin/helmfile-vault-provider 'vault://vault.example.com:8200////secret-engine/my-application/?...' > $temp_file_created_by_helmfile to produce a values.yaml-like file containing the secret .

The produced secrets file should be subject to caching proposed in #444

I would think you would want to move more of the configuration for the backend out of the secrets block so you don't have to repeat it.

Thanks!

That sounds good. Then my best idea would be https://github.com/roboll/helmfile/issues/392#issuecomment-455061273.

Reimplementing gomplate datasources or integrating gomplate, along with my proposed extension to helmfile's template engine and helmfile.yaml would make the repetition minimum, while allowing flexibility to either source a single secret value or the whole YAML data.

For my point of writing any secrets provider within a helmfile.yaml, adding new gomteplate datasource(or its reimplementation)s like exec would work.

I now think we can start with something simple like the below:

templates:
  vault: &vault
    exec: "vault read secrets/foo/bar/{{.key}}"
  vault: &awssecret
    exec: "aws secretmanager get ...."

releases:
- name: myapp
  chart: ./mychart
  secrets:
  - secrets.yaml #helm-secrets
  - <<: *vault
     key: myvaultsecret
  - <<: *awssecret
     key: myawssecret.key

And the above should work fine with #444

That works, but it is going to get very ugly very fast.

@mumoshu godaddy has something pretty cool for hydrating k8s secrets from AWS Secrets Manager: https://github.com/godaddy/kubernetes-external-secrets

BUT you gotta get AWS creds to access Secrets Manager in there to begin with somehow. It would be great if helmfile could retrieve those AWS creds from secrets manager and create k8s secrets for the external secrets manager service to use

@red8888 Hey! Yeah I've even considered to add integration with my aws-secret-operator but I'm still guesting what the real benefit It provides.

For your case, are you talking about making Helmfile able to import SecretsManager secrets as Helm chart values, or something more advanced?

AWS creds from secrets manager and create k8s secrets for the external secrets manager service to use

This has nothing to do with kubernetes-external-secrets. Probably you meant to reimplement part of kubernetes-external-secrets into Helmfile so that it creates K8s secrets from SecretsManager secrets?

@mumoshu yeah thats what i was thinking. I was not aware of https://github.com/mumoshu/aws-secret-operator

kubernetes-external-secrets does mostly the same thing looks like.

I just meant a service used for hydrating k8s secrets from aws secrets manager needs AWS creds to access it. using kubernetes-external-secrets I have been manually scripting that part of the deployment (pulling the AWS creds from Secrets Manger and creating a k8s secret for kubernetes-external-secrets to use). After that point kubernetes-external-secrets dynamically creates k8s secrets for me from secrets manager (through the custom resources like your service does)

its not a big deal to script it myself, but would be neat if I could have helm pull the initial creds. that might be asking to much though

@red8888 Thanks for clarifying! I think I understood your problem now.

Your use-case should be addressed with #745 which has AWS Secrets Manager as one of supported backends.

You can combine it with the incubator/raw Helm chart so that you can create whatever K8s resources from helmfile values.

I now think we can start with something simple like the below:

templates:
  vault: &vault
    exec: "vault read secrets/foo/bar/{{.key}}"
  vault: &awssecret
    exec: "aws secretmanager get ...."

releases:
- name: myapp
  chart: ./mychart
  secrets:
  - secrets.yaml #helm-secrets
  - <<: *vault
     key: myvaultsecret
  - <<: *awssecret
     key: myawssecret.key

I haven't been able to understand or use this example, could you clarify ?
in ./helmfile.yaml: failed to read helmfile.yaml: reading document at index 1: yaml: unmarshal errors: line 12: field exec not found in type state.TemplateSpec line 24: cannot unmarshal !!map into string

EDIT ==> went further but still can't get it to work :

templates:                                                                      
  vault: &vault                                                                 
    exec: "vault kv get -field={{.key}} secrets/test"                       

releases:                                                                       
- name: chart                                              
  chart: chart                             
  namespace: chart                                                                                                     
  secrets:                                                                      
  - <<: *vault                                                                  
    key: root_password

Goes :

executing "stringTemplate" at <.key>: can't evaluate field key in type state.EnvironmentTemplateData

@etiennejournet Hey! The feature that supports the example isn't implemented yet.
Fortunately, we now have a better alternative #906 that you can use.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cilerler picture cilerler  路  3Comments

willejs picture willejs  路  4Comments

pavdmyt picture pavdmyt  路  3Comments

madAndroid picture madAndroid  路  3Comments

ppawiggers picture ppawiggers  路  3Comments