-
Notifications
You must be signed in to change notification settings - Fork 296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TransformOptions Configuration Block to SecretProviderClass #963
Conversation
Hi @manedurphy. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
apis/v1/secretproviderclass_types.go
Outdated
// SecretProviderClassSpec defines the desired state of SecretProviderClass | ||
type SecretProviderClassSpec struct { | ||
// Configuration for provider name | ||
Provider Provider `json:"provider,omitempty"` | ||
// Configuration for specific provider | ||
Parameters map[string]string `json:"parameters,omitempty"` | ||
SecretObjects []*SecretObject `json:"secretObjects,omitempty"` | ||
SyncOptions SyncOptions `json:"syncOptions,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My major concern here is that this PR adds this great templating/translation feature but only in the context of secret syncing.
I would like to see this totally separate from secret syncing as this provides useful transforms for our main usecase of writing secrets to files.
Re-writing this as something like TransformOptions
then performing the transform between the RPC response from the provider but before the data is written to with the AtomicWriter
will make this feature useful in many more usecases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Between client.Mount()
RPC to the provider and the fileutil.WritePayloads
Is I think the idea spot to inject the concept of a "transform" where 1 file can map to multiple files based on json path or other future format extractors.
If we are able to have this concept of a 'transform' directly in the SPC then it could be useful for other issues such as: #948
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I am understanding the changes you are looking for @tam7t. My understanding of the issue that you've linked (#948) is that the user wants the ability to hardcode secret k/v pairs directly in secretObjects
. The functionality I am adding with this feature is meant for targeting a set of secrets when the mounted content is a json
object with multiple k/v pairs. Can you provide an example to demonstrate the idea behind the concept of a "transform" where 1 file can map to multiple files based on json path or other future format extractors
?
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: manedurphy The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@aramase @tam7t The example below shows the SecretProviderClass
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-spc
spec:
provider: vault
parameters:
roleName: "csi"
vaultAddress: "http://vault.vault:8200"
objects: |
- secretPath: "secret/data/database"
objectName: "database/username"
secretKey: "username"
- secretPath: "secret/data/database"
objectName: "database/password"
secretKey: "password"
- secretPath: "secret/data/database"
objectName: "db-creds"
secretObjects:
- secretName: db-creds
type: Opaque
data:
- objectName: db-creds/username
key: username
- objectName: db-creds/password
key: password
transformOptions:
format: json
jsonPath: $.data.data
secrets:
- objectName: database/username
format: plaintext
- objectName: database/password
format: plaintext Mount
# /mnt/secrets-store
ls -l
# output
total 0
lrwxrwxrwx 1 root root 15 Jul 31 20:05 database -> ..data/database
lrwxrwxrwx 1 root root 15 Jul 31 20:05 db-creds -> ..data/db-creds
ls -l database/
# output
total 8
-rw-r--r-- 1 root root 10 Jul 31 20:08 password
-rw-r--r-- 1 root root 7 Jul 31 20:08 username
ls -l db-creds/
# output
total 8
-rw-r--r-- 1 root root 10 Jul 31 20:08 password
-rw-r--r-- 1 root root 7 Jul 31 20:08 username |
// expected format of the secret received from the provider (e.g. plaintext, json) | ||
Format string `json:"format,omitempty"` | ||
// JSON path to target for a secret received in JSON format | ||
JsonPath string `json:"jsonPath,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSONPath instead of JsonPath per https://github.com/golang/go/wiki/CodeReviewComments#initialisms
// expected format of the secret received from the provider (e.g. plaintext, json) | ||
Format string `json:"format,omitempty"` | ||
// JSON path to target for a secret received in JSON format | ||
JsonPath string `json:"jsonPath,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSONPath
@@ -46,13 +46,32 @@ type SecretObject struct { | |||
Data []*SecretObjectData `json:"data,omitempty"` | |||
} | |||
|
|||
type Secret struct { | |||
// name of the object to sync |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't follow this very well in a lot of the code but per https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences comments should be full sentences. Don't try to fix the other stuff in this PR but would be good to try to apply to the comments added here.
Format string `json:"format,omitempty"` | ||
// JSON path to target for a secret received in JSON format | ||
JsonPath string `json:"jsonPath,omitempty"` | ||
Secrets []Secret `json:"secrets,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand what this represents and it doesn't seem like the test cases cover where TransformOptions.Secrets
is populated?
I see the example in #963 (comment) but I'm not clear why the secret:
part there is needed.
JsonPath string `json:"jsonPath,omitempty"` | ||
} | ||
|
||
type TransformOptions struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we actually want the ProviderFilePath
or InFilePath
here to note the input data for the translation. I could see a single SecretProviderClass
that has a number of different secrets that you want to split out in different ways.
// SecretProviderClassSpec defines the desired state of SecretProviderClass | ||
type SecretProviderClassSpec struct { | ||
// Configuration for provider name | ||
Provider Provider `json:"provider,omitempty"` | ||
// Configuration for specific provider | ||
Parameters map[string]string `json:"parameters,omitempty"` | ||
SecretObjects []*SecretObject `json:"secretObjects,omitempty"` | ||
// Configuration for secret transformation | ||
TransformOptions TransformOptions `json:"transformOptions,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we want this to be a list like []*TransformOptions
so that multiple transforms can be done on the secrets before presenting in the pod filesystem.
|
||
type TransformOptions struct { | ||
// expected format of the secret received from the provider (e.g. plaintext, json) | ||
Format string `json:"format,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can actually omit this just determine behavior based on whether JsonPath
is populated.
type TransformOptions struct { | ||
// expected format of the secret received from the provider (e.g. plaintext, json) | ||
Format string `json:"format,omitempty"` | ||
// JSON path to target for a secret received in JSON format |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice from the implementation that it expects the JsonPath
to resolve to string key's that become filenames. I think this comment should be explicit about that behavior.
Additionally I think it might be nice to support extracting a single value if the path resolves to a string instead of a json object. It may be a bit more verbose but I think more explicit to support resolving to a json value and adding an OutFilePath
parameter. Consider a response from the provider that says file my_secret
has value {"username":"admin", "password": "hunter2"}
You could then write an SPC like:
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: app-secrets
spec:
provider: vault
parameters:
objects: |
- objectName: "my_secret"
secretPath: "secret/data/my-kv"
secretKey: "blob"
transforms:
- inFilePath: "my_secret"
jsonPath: "$.username"
outFilePath: "username"
- inFilePath: "my_secret"
jsonPath: "$.password"
outFilePath: "password"
Later we could add a transform like the this to resolve #948
transforms:
- staticValue: "admin"
outFilePath: "username"
Where we could generate a file based just on SPC data for non-secret static value admin
to file username
.
commenting to acknowledge feedback. haven't had much time in the last few weeks. will try to revisit this as soon as i can. |
Curious if we have any desire to support CEL here (maybe instead of JSON path). CEL is used by Kubernetes in a few places now, and the list is growing. |
Hello guys! Thanks for your collaboration with this! I'm really excited about this feature, it will save a lot of work for my team with a more reliable process :) |
hey team, that feature will be perfect for our integrations. example rabbitmq and prometheus. to have sync with our vault |
PR needs rebase. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
When would this PR accepted? |
This PR needs rebase and comments addressed. @tam7t is working on a POC that might help accomplish this., I'm going to close this PR due to inactivity. /close |
@aramase: Closed this PR. In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
Hello @tam7t, did you have the time to poc this ? |
PR Type
/kind feature
Related Issues & Pull Requests
Issues
Pull Requests
Purpose
JSON
response into individual files based on thekey/value
pairs found within thatJSON
response.SyncAll
. I tried to be thorough enough in this document so referencing my previous work will not be necessary, but it is there if it helps to read it as well.Implementation
API Type Definitions
transformOptions
field is a new configuration block that is set in the SPC's definition.SecretProviderClass
dropdown below, we can see that communication is established with a Vault provider.secretKey
will retrieve a JSON object with all secrets on the secret path and all metadata included with Vault's JSON response.transformOptions
configuration block allows the user to target ajsonPath
on the JSON response body and create a file for eachkey/value
pair in that nested object. In this example, a user will likely want to mount bothkey/value
pairs stored within the.data.data
JSON path in the Vault JSON response as individual files, rather than syncing the full JSON response with all of its metadata.V1 Definitions
SecretProviderClass
Vault JSON Response
Mounting Secret Content
transformOptions.Format
value.plaintext
format, which is what the driver does already. The format can be configured in theSecretProviderClass
as shown in the example previously.MountContent
Example
SecretProviderClass
handle secrets with multiple formats..spec.transformOptions.secrets
list to specify which secrets should be handled differently than what is configured at a higher level in the hierarchy.plaintext
, and everything else should be handled asjson
.SecretProviderClass
username
andpassword
from Vault with 2 strategies.secretKey
, which gets a specfic key on a path in Vault.secretKey
. The absence of thesecretKey
field on an item in.spec.parameters.objects
tells Vault to return all secrets on thesecretPath
as a JSON object.container
kubectl get secrets # Output NAME TYPE DATA AGE db-creds Opaque 2 111s
Addtional Notes
Todos