vault-csi-provider icon indicating copy to clipboard operation
vault-csi-provider copied to clipboard

Implicit mapping of secrets / objects - SecretProviderClass

Open adlnc opened this issue 2 years ago • 2 comments

Hi,

I've got a question regarding csi mode and also the way of creating kubernetes secrets using secretObjects from SecretProviderClass. Possibly a miss understanding from my side but I have a hard time thinking my way around this.

So as far as I understand, the current implementation provides an objects array of secrets to retrieve from vault. Each secret needs to be explicitly described using objectName, secretKey, secretPath. As specificied in documentation.

Now I'm curious why it doesn't seem possible to provide a default implementation where all keys from a secretPath would be used in order to create the secret. Using a simple 1:1 mapping instead of using objectName / secretKey mapping.

Example of current spec :

---
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
spec:
  provider: vault
  secretObjects:
    - secretName: vault-db-creds-secret
      type: Opaque
      data:
        - objectName: dbUsername # References dbUsername below
          key: username # Key within k8s secret for this value
        - objectName: dbPassword
          key: password
  parameters:
    roleName: 'app'
    vaultAddress: 'https://vault.vault:8200'
    vaultCACertPath: '/vault/tls/ca.crt'
    objects: |
      - objectName: "dbUsername"
        secretPath: "database/creds/db-app"
        secretKey: "username"
      - objectName: "dbPassword"
        secretPath: "database/creds/db-app"
        secretKey: "password"

Now if you have 30 secret keys, you would need to list these 30 items ?

---
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
[...]
      data:
        - objectName: object1
          key: username 
        - objectName: object2
          key: password
        [...]
        - objectName: object30
          key: something
  parameters:
    [...]
    objects: |
      - objectName: object1
        secretPath: "database/creds/db-app"
        secretKey: key1
      - objectName: object2
        secretPath: "database/creds/db-app"
        secretKey: key2
      [...]
      - objectName: object30
        secretPath: "database/creds/db-app"
        secretKey: key30

Can't we just just give a secretPath and expect it to retrieve all secretKeys from that. And creating the secret with secretKey: secretValue loop over secrets from that path ?

I feel like I'm missing the point here because I was not able to find any other questions/posts relevant for that matter. Could you help me get back on track here ?

Thanks

adlnc avatar Jan 23 '23 16:01 adlnc

Hello 👋 these are good questions. I think there are a few pre-existing things to mention here.

  • If you omit the secretKey, the whole JSON response will be written to the file/k8s secret (docs). So if you write many secrets to a single KV path (e.g. vault kv put secret/foo user=admin pass=hunter2 url=...), you can fetch all of those secret values in one SPC object.
  • There is a PR on the driver (with some renewed interest in the most recent community call) to support transforming JSON file contents using JSON path: https://github.com/kubernetes-sigs/secrets-store-csi-driver/pull/963
  • There is a feature request on the driver to support syncing all files from a mount request into the k8s secret: https://github.com/kubernetes-sigs/secrets-store-csi-driver/issues/529 - AFAIK this one is much less specced out than the transforms feature, so it's hard to say what the final functionality would look like, but I hope something like the below would be possible:
---
apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
[...]
  secretObjects:
  - secretName: db-creds
    type: Opaque
    syncAll: true
  parameters:
    [...]
    objects: |
      - objectName: obj
        secretPath: "database/creds/db-app"
  transforms:
    - inFilePath: "obj"
      jsonPath: "$.username"
      outFilePath: "username"
    - inFilePath: "obj"
      jsonPath: "$.password"
      outFilePath: "password"

I think that gets relatively close to what you want, except you can still end up with quite an arduous number of transform entries.

The vault-csi-provider owns all of the config inside parameters, and the driver owns the rest, so while the features for syncAll for k8s secrets and transforms would be best/only possible to do in the driver, there is a potential feature request here to automatically create one object per key inside the secret's data field. Perhaps we could default to doing that if both objectName and secretKey are omitted.

Do you think that would fit your use-case?

tomhjp avatar Jan 26 '23 18:01 tomhjp

Thank you for your reply,

Simply put, the answer is yes ! I think you totally got what I'm looking for. So you first remove my doubts that, at the moment, this doesn't seem to be supported. Now, do you think such feature could be implemented and would be relevant ?

Yes, I tried playing with the full json response while omitting the secretKey but handling a single object here doesn't seem to fit my need.

In the end, if I get this right in order for this to work, this would require the default behavior you mention when objectName and secretKey are omitted (vault-csi provider) + that PR for transforms and SyncAll option to be merged (secret-store-driver).

adlnc avatar Jan 27 '23 13:01 adlnc