-
Notifications
You must be signed in to change notification settings - Fork 298
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
Sync All Secrets to K8s #820
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. |
[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 |
29617be
to
e4ba1b6
Compare
I think what many people (including myself) who are using AWS Secrets Manager are looking for is the ability to automatically populate key/value pairs in a Kubernetes Secret with the key/value pairs of an AWS Secrets Manager secret. Currently, the AWS Secrets & Configuration Provider (ASCP) for the Secrets Store CSI Driver requires us to explicitly list all key/value pairs in a SecretProviderClass resource, and it needs to be done twice: On the input side, the AWS Secrets Manager secret is made available as a JSON object which includes all key/value pairs in the secret, and the desired keys need to be specified with a JSON path reference (jmesPath); On the output side, those selected values then need to be set as entries in the Kubernetes Secret. The desired solution here is to have it automatically create one entry in a Kubernetes Secret for each entry of the JSON object, using the same keys. Does this PR help with that? Would it require any changes on the ASCP side? |
It has been a while since I've had a look at this, so I'd have to read through it to refresh my memory of all of the changes that were made, but this was meant to be provider-agnostic. Regardless of which provider that the csi-driver gets its secrets from, the |
Having gone through the motions of running the driver with the AWS provider, it seems that the AWS provider does not send files back to the driver as the other providers do, see here. In order for the |
Appreciate the efforts @manedurphy. Aside from the AWS provider not adhering to the standard with respect to sending back files as you mentioned, a key point with AWS secrets is that their value is provided as a JSON object. If AWS were to adhere to the standard, would the changes in this PR enable converting an untyped JSON object into a single Kubernetes secret with all entries of the JSON object added as individual key/value pairs in the Kubernetes secret? |
I think I understand your question now, but please correct me if need be. When a user decides to sync all of the secrets listed in the provider's configuration, it is done so by referring to the name of the file that is mounted, and the value stored in that file will be the value of the secret that is synced to K8s. Having had another chance to play with the AWS provider, what I've noticed is that if we use
SecretProviderClassAWSapiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "db-creds"
objectType: "secretsmanager"
jmesPath:
- path: username
objectAlias: username
- path: password
objectAlias: password
syncOptions:
syncAll: true
type: Opaque Since we have 3 files mounted, we can expect 3 secrets to be synced to K8s named Should the AWS provider start adhering to the standard of sending files back to the driver, when |
e4ba1b6
to
ee3d036
Compare
Thanks for taking a closer look at the AWS provider and for providing an example. Your observations of the files that are mounted and their contents are correct. As for your last paragraph and to make sure I properly explained the desired outcome: The desired solution from my perspective is that apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "db-creds"
objectType: "secretsmanager"
syncOptions:
syncAll: true
type: Opaque This would create a single Kubernetes secret, containing In light of this added context, do you think this is possible? If so, do the required changes to make it work need to be made only in the AWS provider, or would this PR need additional work as well? |
Got it, thank you for clarifying @fubar. So, with the changes that are currently in place, that functionality is not available. However, it would be totally possible. My only concern would be implementing features in the driver that are provider-specific when the feature is meant to be provider-agnostic. However, I feel that I can implement this functionality in a way that can be used by any provider.
{"username": "my-db-user","password": "my-db-password"} SecretProviderClassAWSapiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "db-creds"
objectType: "secretsmanager"
syncOptions:
type: Opaque
syncAll: true
json: true SecretProviderClassVaultapiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-opaque
spec:
provider: vault
parameters:
roleName: "csi"
vaultAddress: "http://vault.vault:8200"
objects: |
- secretPath: "secret/data/db-creds"
objectName: "database/creds"
secretKey: "creds"
syncOptions:
type: Opaque
syncAll: true
json: true What are your thoughts on this approach? |
That sounds excellent! I am not familiar enough with the various secrets providers currently out there to know if support for JSON-encoded values should be the responsibility of a provider or the driver. However, it is not hard to imagine that other (and future) providers would use JSON under the hood - even aside from that, having the option of JSON support on the driver level would enable us to deliberately use JSON-encoded values with any provider if we find it beneficial for a given use case. Thus I think it would be a valuable addition to the driver. Having to create separate SPCs for JSON-encoded secrets and all others would be a bit cumbersome. What do you think of using a list to indicate which secrets are JSON, and only attempt to decode those? E.g.: apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: aws-secrets
spec:
provider: aws
parameters:
objects: |
- objectName: "db-creds"
objectType: "secretsmanager"
syncOptions:
type: Opaque
syncAll: true
decodeJsonSecrets:
- secretName: "db-creds" I recognize that adding a list is somewhat going against the original motivation for this PR, but I feel that this feature is different enough that a simple list of secrets would be the easiest to use. And it's optional, after all. Taking this one step further, one could imagine that other formats aside from JSON could be supported in the future. A specification like this would make it more extensible: syncOptions:
type: Opaque
syncAll: true
decodeSecrets:
- secretName: "db-creds"
format: json |
I like the idea of supporting multiple formats in the driver. I would assume that most would opt in for JSON, but leaving it open for other possibilities gives the driver more flexibility than the boolean value that I presented before. A couple of things comes to mind to bounce off of your idea for a
DB-Creds Secret{
"username": "my-db-user",
"password": "my-db-password"
} SecretProviderClassVaultapiVersion: 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: "username"
secretKey: "username"
- secretPath: "secret/data/database"
objectName: "password"
secretKey: "password"
- secretPath: "secret/data/database"
objectName: "db-creds"
syncOptions:
type: Opaque
syncAll: true
format: default
decodeSecrets:
- secretName: "db-creds"
format: json SecretProviderClassAWSapiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
name: aws-spc
spec:
provider: aws
parameters:
objects: |
- objectName: "db-creds"
objectType: "secretsmanager"
- objectName: "jwt-auth"
objectType: "secretsmanager"
syncOptions:
type: Opaque
syncAll: true
format: json
Mounted Secrets
{
"username": "db-user-json",
"password": "db-password-json"
}
username: db-user-yaml
password: db-password-yaml
POC Programpackage main
import (
"encoding/json"
"flag"
"fmt"
"os"
"github.com/hashicorp/go-hclog"
"sigs.k8s.io/yaml"
)
const (
jsonSecret = "secrets/db-creds-json"
yamlSecret = "secrets/db-creds-yaml"
normalSecret = "secrets/default"
)
var (
logger hclog.Logger
debug bool
)
func init() {
logger = hclog.Default()
flag.BoolVar(&debug, "debug", false, "set logger level to debug")
flag.Parse()
if debug {
logger.SetLevel(hclog.Debug)
}
}
func main() {
var (
secret interface{}
err error
)
if secret, err = readSecret(jsonSecret); err != nil {
panic(err)
}
logger.Info("secret received", "secret", secret.(map[string]interface{}))
if secret, err = readSecret(yamlSecret); err != nil {
panic(err)
}
logger.Info("secret received", "secret", secret.(map[string]interface{}))
if secret, err = readSecret(normalSecret); err != nil {
panic(err)
}
logger.Info("secret received", "secret", secret.(string))
}
func readSecret(path string) (interface{}, error) {
var (
fileContent []byte
secretsMap map[string]interface{}
err error
)
if fileContent, err = os.ReadFile(path); err != nil {
return nil, fmt.Errorf("could not read content: %v", err)
}
if err = json.Unmarshal(fileContent, &secretsMap); err == nil {
return secretsMap, nil
}
logger.Debug("could not read JSON data", "error", err)
if err = yaml.Unmarshal(fileContent, &secretsMap); err == nil {
return secretsMap, nil
}
logger.Debug("could not read YAML data", "error", err)
return string(fileContent), nil
} POC Logs2022-03-22T21:24:44.667-0700 [INFO] secret received: secret="map[password:db-password-json username:db-user-json]"
2022-03-22T21:24:44.667-0700 [DEBUG] could not read JSON data: error="invalid character 'u' looking for beginning of value"
2022-03-22T21:24:44.667-0700 [INFO] secret received: secret="map[password:db-password-yaml username:db-user-yaml]"
2022-03-22T21:24:44.667-0700 [DEBUG] could not read JSON data: error="invalid character 'd' looking for beginning of value"
2022-03-22T21:24:44.667-0700 [DEBUG] could not read YAML data: error="error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}"
2022-03-22T21:24:44.667-0700 [INFO] secret received: secret=default-secret
ExamplesyncOptions:
type: Opaque
syncAll: true
format: auto
removeOnPodDeletion: false Another thing that I am thinking about is how to inform each provider which format should be used. AWS mounts the secret as a JSON object by default, so that would be supported immediately. Vault, however, parses through the Vault Response{
"request_id": "1e9243e4-5d32-69c6-c3f0-7131eafd4c00",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"data": {
"password": "db-password",
"username": "db-username"
},
"metadata": {
"created_time": "2022-03-23T02:26:26.993678358Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
}
},
"warnings": null
}
This makes me think that for the Vault provider, in order to take advantage of the driver's supported formats, it would need the parameters in the SecretProviderClassVaultapiVersion: 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/db-creds"
format: json
syncOptions:
syncAll: true
type: Opaque
format: json Apologies for the verboseness of this response. I am enjoying the exchange of these ideas! |
First off, thanks for putting together those details, this is going in a great direction!
As for Vault and its nested JSON object, I would have expected providers to return only the value of a secret to the driver, and not a construct that includes metadata, but I can see why someone would want it all. If we consider this a Vault "format", and assuming it's unlikely to change, it could be made an option of the syncOptions:
type: Opaque
syncAll: true
format: vault Alternatively, a JSON path could work. Though a bit clunky, this would make it extensible for any other JSON object: syncOptions:
type: Opaque
syncAll: true
secrets:
- secretName: "db-creds"
format: json
jsonPath: "$.data.data" Thanks for being open to my suggestions! This has the potential to be a significant improvement to the whole integration of external secrets providers. |
So I really like the I think what I would like to do is spend some time this weekend to work on some POCs and cut a new branch where I can implement these ideas. I will try to break down the strategies the same as before, so that the changes that need to be made in the code are clear. I will return to this discussion with any insights, obstacles, and successes. Trying my best to balance time with work as it has recently been busy, but I'm excited to see these ideas through. Really appreciate your inputs, @fubar! |
Awesome, looking forward to the results! |
Alright, I was able to implement these changes and have several examples to demonstrate the new functionality. I will still need to write unit tests for the new functions I've added as well as update the documentation, but I want to make sure that this is going in the right direction before I address those areas. These examples were done with the Vault provider, but the functionality is provider-agnostic. These are the secrets that are stored in Vault. # Opaque
kubectl exec -n vault vault-0 -- vault kv put secret/database username="db-username" password="db-password"
kubectl exec -n vault vault-0 -- vault kv put secret/auth client_id="my-client-id" client_secret="my-client-secret"
# Basic
kubectl exec -n vault vault-0 -- vault kv put secret/basic-plaintext credentials="basic-username,basic-password"
kubectl exec -n vault vault-0 -- vault kv put secret/basic-json username="basic-username" password="basic-password"
# TLS
kubectl exec -n vault vault-0 -- vault kv put secret/tls-json tls.key="$TLS_KEY" tls.crt="$TLS_CERT"
kubectl exec -n vault vault-0 -- vault kv put secret/tls-plaintext data="$CERT" tls-plaintext content# CERT
-----BEGIN CERTIFICATE-----
MIIDOTCCAiGgAwIBAgIJAP0J5Z7N0Y5fMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
BAMMDmRlbW8uYXp1cmUuY29tMRgwFgYDVQQKDA9ha3MtaW5ncmVzcy10bHMwHhcN
MjAwNDE1MDQyMzQ2WhcNMjEwNDE1MDQyMzQ2WjAzMRcwFQYDVQQDDA5kZW1vLmF6
dXJlLmNvbTEYMBYGA1UECgwPYWtzLWluZ3Jlc3MtdGxzMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAyS3Zky3n8JlLBxPLzgUpKZYxvzRadeWLmWVbK9by
o08S0Ss8Jao7Ay1wHtnLbn52rzCX6IX1sAe1TAT755Gk7JtLMkshtj6F8BNeelEy
E1gsBE5ntY5vyLTm/jZUIKz2Z9TLnqvQTmp6gJ68BKJ1NobnsHiAcKc6hI7kmY9C
oshmAi5qiKYBgzv/thji0093vtVSa9iwHhQp+AEIMhkvM5ZZkiU5eE6MT9SBEcVW
KmWF28UsB04daYwS2MKJ5l6d4n0LUdAG0FBt1lCoT9rwUDj9l3Mqmi953gw26LUr
NrYnM/8N2jl7Cuyw5alIWaUDrt5i+pu8wdWfzVk+fO7x8QIDAQABo1AwTjAdBgNV
HQ4EFgQUwFBbR014McETdrGGklpEQcl71Q0wHwYDVR0jBBgwFoAUwFBbR014McET
drGGklpEQcl71Q0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATgTy
gg1Q6ISSekiBCe12dqUTMFQh9GKpfYWKRbMtjOjpc7Mdwkdmm3Fu6l3RfEFT28Ij
fy97LMYv8W7beemDFqdmneb2w2ww0ZAFJg+GqIJZ9s/JadiFBDNU7CmJMhA225Qz
XC8ovejiePslnL4QJWlhVG93ZlBJ6SDkRgfcoIW2x4IBE6wv7jmRF4lOvb3z1ddP
iPQqhbEEbwMpXmWv7/2RnjAHdjdGaWRMC5+CaI+lqHyj6ir1c+e6u1QUY54qjmgM
koN/frqYab5Ek3kauj1iqW7rPkrFCqT2evh0YRqb1bFsCLJrRNxnOZ5wKXV/OYQa
QX5t0wFGCZ0KlbXDiw==
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJLdmTLefwmUsH
E8vOBSkpljG/NFp15YuZZVsr1vKjTxLRKzwlqjsDLXAe2ctufnavMJfohfWwB7VM
BPvnkaTsm0sySyG2PoXwE156UTITWCwETme1jm/ItOb+NlQgrPZn1Mueq9BOanqA
nrwEonU2hueweIBwpzqEjuSZj0KiyGYCLmqIpgGDO/+2GOLTT3e+1VJr2LAeFCn4
AQgyGS8zllmSJTl4ToxP1IERxVYqZYXbxSwHTh1pjBLYwonmXp3ifQtR0AbQUG3W
UKhP2vBQOP2XcyqaL3neDDbotSs2ticz/w3aOXsK7LDlqUhZpQOu3mL6m7zB1Z/N
WT587vHxAgMBAAECggEAJb0qIYftCJ9ZCbzW8JDbRefc8SdbCN7Er0PqNHEgFy6Q
MxjPMambZF8ztzXYCaRDk12kQYRPsHPhuJ7+ulQCAjinhIm/izZzXbPkd0GgCSzz
JOOoZNCRe68j3fBHG9IWbyfmAp/sdalXzaT5VE09e7sW323bekaEnbVIgN30/CAS
gI77YdaIhG+PT/pSCOc11MTkBJp+VhT1tEtlRAR78b1RXbGi1oUHRee7C3Ia8IKQ
3L5dPxR9RsYsR2O66908kEi8ZcuIjcbIuRPDXYHY+5Nwm3mXuZlkyjyfxJXsIA8i
qBrQrSpHGgAn1TVlLDSCKPLbkRzBRRvAW0zL/cDTuQKBgQDq/9Yxx9QivAuUxxdE
u0VO5CzzZYFWhDxAXS3/wYyo1YnoPtUz/lGCvMWp0k2aaa0+KTXv2fRCUGSujHW7
Jfo4kuMPkauAhoXx9QJAcjoK0nNbYEaqoJyMoRID+Qb9XHkj+lmBTmMVgALCT9DI
HekHj/M3b7CknbfWv1sOZ/vpQwKBgQDbKEuP/DWQa9DC5nn5phHD/LWZLG/cMR4X
TmwM/cbfRxM/6W0+/KLAodz4amGRzVlW6ax4k26BSE8Zt/SiyA1DQRTeFloduoqW
iWF4dMeItxw2am+xLREwtoN3FgsJHu2z/O/0aaBAOMLUXIPIyiE4L6OnEPifE/pb
AM8EbM5auwKBgGhdABIRjbtzSa1kEYhbprcXjIL3lE4I4f0vpIsNuNsOInW62dKC
Yk6uaRY3KHGn9uFBSgvf/qMost310R8xCYPwb9htN/4XQAspZTubvv0pY0O0aQ3D
0GJ/8dFD2f/Q/pekyfUsC8Lzm8YRzkXhSqkqG7iF6Kviw08iolyuf2ijAoGBANaA
pRzDvWWisUziKsa3zbGnGdNXVBEPniUvo8A/b7RAK84lWcEJov6qLs6RyPfdJrFT
u3S00LcHICzLCU1+QsTt4U/STtfEKjtXMailnFrq5lk4aiPfOXEVYq1fTOPbesrt
Katu6uOQ6tjRyEbx1/vXXPV7Peztr9/8daMeIAdbAoGBAOYRJ1CzMYQKjWF32Uas
7hhQxyH1QI4nV56Dryq7l/UWun2pfwNLZFqOHD3qm05aznzNKvk9aHAsOPFfUUXO
7sp0Ge5FLMSw1uMNnutcVcMz37KAY2fOoE2xoLM4DU/H2NqDjeGCsOsU1ReRS1vB
J+42JGwBdLV99ruYKVKOWPh4
-----END PRIVATE KEY----- tls-json content# TLS_KEY
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJLdmTLefwmUsH
E8vOBSkpljG/NFp15YuZZVsr1vKjTxLRKzwlqjsDLXAe2ctufnavMJfohfWwB7VM
BPvnkaTsm0sySyG2PoXwE156UTITWCwETme1jm/ItOb+NlQgrPZn1Mueq9BOanqA
nrwEonU2hueweIBwpzqEjuSZj0KiyGYCLmqIpgGDO/+2GOLTT3e+1VJr2LAeFCn4
AQgyGS8zllmSJTl4ToxP1IERxVYqZYXbxSwHTh1pjBLYwonmXp3ifQtR0AbQUG3W
UKhP2vBQOP2XcyqaL3neDDbotSs2ticz/w3aOXsK7LDlqUhZpQOu3mL6m7zB1Z/N
WT587vHxAgMBAAECggEAJb0qIYftCJ9ZCbzW8JDbRefc8SdbCN7Er0PqNHEgFy6Q
MxjPMambZF8ztzXYCaRDk12kQYRPsHPhuJ7+ulQCAjinhIm/izZzXbPkd0GgCSzz
JOOoZNCRe68j3fBHG9IWbyfmAp/sdalXzaT5VE09e7sW323bekaEnbVIgN30/CAS
gI77YdaIhG+PT/pSCOc11MTkBJp+VhT1tEtlRAR78b1RXbGi1oUHRee7C3Ia8IKQ
3L5dPxR9RsYsR2O66908kEi8ZcuIjcbIuRPDXYHY+5Nwm3mXuZlkyjyfxJXsIA8i
qBrQrSpHGgAn1TVlLDSCKPLbkRzBRRvAW0zL/cDTuQKBgQDq/9Yxx9QivAuUxxdE
u0VO5CzzZYFWhDxAXS3/wYyo1YnoPtUz/lGCvMWp0k2aaa0+KTXv2fRCUGSujHW7
Jfo4kuMPkauAhoXx9QJAcjoK0nNbYEaqoJyMoRID+Qb9XHkj+lmBTmMVgALCT9DI
HekHj/M3b7CknbfWv1sOZ/vpQwKBgQDbKEuP/DWQa9DC5nn5phHD/LWZLG/cMR4X
TmwM/cbfRxM/6W0+/KLAodz4amGRzVlW6ax4k26BSE8Zt/SiyA1DQRTeFloduoqW
iWF4dMeItxw2am+xLREwtoN3FgsJHu2z/O/0aaBAOMLUXIPIyiE4L6OnEPifE/pb
AM8EbM5auwKBgGhdABIRjbtzSa1kEYhbprcXjIL3lE4I4f0vpIsNuNsOInW62dKC
Yk6uaRY3KHGn9uFBSgvf/qMost310R8xCYPwb9htN/4XQAspZTubvv0pY0O0aQ3D
0GJ/8dFD2f/Q/pekyfUsC8Lzm8YRzkXhSqkqG7iF6Kviw08iolyuf2ijAoGBANaA
pRzDvWWisUziKsa3zbGnGdNXVBEPniUvo8A/b7RAK84lWcEJov6qLs6RyPfdJrFT
u3S00LcHICzLCU1+QsTt4U/STtfEKjtXMailnFrq5lk4aiPfOXEVYq1fTOPbesrt
Katu6uOQ6tjRyEbx1/vXXPV7Peztr9/8daMeIAdbAoGBAOYRJ1CzMYQKjWF32Uas
7hhQxyH1QI4nV56Dryq7l/UWun2pfwNLZFqOHD3qm05aznzNKvk9aHAsOPFfUUXO
7sp0Ge5FLMSw1uMNnutcVcMz37KAY2fOoE2xoLM4DU/H2NqDjeGCsOsU1ReRS1vB
J+42JGwBdLV99ruYKVKOWPh4
-----END PRIVATE KEY-----
# TLS_CERT
-----BEGIN CERTIFICATE-----
MIIDOTCCAiGgAwIBAgIJAP0J5Z7N0Y5fMA0GCSqGSIb3DQEBCwUAMDMxFzAVBgNV
BAMMDmRlbW8uYXp1cmUuY29tMRgwFgYDVQQKDA9ha3MtaW5ncmVzcy10bHMwHhcN
MjAwNDE1MDQyMzQ2WhcNMjEwNDE1MDQyMzQ2WjAzMRcwFQYDVQQDDA5kZW1vLmF6
dXJlLmNvbTEYMBYGA1UECgwPYWtzLWluZ3Jlc3MtdGxzMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAyS3Zky3n8JlLBxPLzgUpKZYxvzRadeWLmWVbK9by
o08S0Ss8Jao7Ay1wHtnLbn52rzCX6IX1sAe1TAT755Gk7JtLMkshtj6F8BNeelEy
E1gsBE5ntY5vyLTm/jZUIKz2Z9TLnqvQTmp6gJ68BKJ1NobnsHiAcKc6hI7kmY9C
oshmAi5qiKYBgzv/thji0093vtVSa9iwHhQp+AEIMhkvM5ZZkiU5eE6MT9SBEcVW
KmWF28UsB04daYwS2MKJ5l6d4n0LUdAG0FBt1lCoT9rwUDj9l3Mqmi953gw26LUr
NrYnM/8N2jl7Cuyw5alIWaUDrt5i+pu8wdWfzVk+fO7x8QIDAQABo1AwTjAdBgNV
HQ4EFgQUwFBbR014McETdrGGklpEQcl71Q0wHwYDVR0jBBgwFoAUwFBbR014McET
drGGklpEQcl71Q0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATgTy
gg1Q6ISSekiBCe12dqUTMFQh9GKpfYWKRbMtjOjpc7Mdwkdmm3Fu6l3RfEFT28Ij
fy97LMYv8W7beemDFqdmneb2w2ww0ZAFJg+GqIJZ9s/JadiFBDNU7CmJMhA225Qz
XC8ovejiePslnL4QJWlhVG93ZlBJ6SDkRgfcoIW2x4IBE6wv7jmRF4lOvb3z1ddP
iPQqhbEEbwMpXmWv7/2RnjAHdjdGaWRMC5+CaI+lqHyj6ir1c+e6u1QUY54qjmgM
koN/frqYab5Ek3kauj1iqW7rPkrFCqT2evh0YRqb1bFsCLJrRNxnOZ5wKXV/OYQa
QX5t0wFGCZ0KlbXDiw==
-----END CERTIFICATE----- OpaqueExample 1
DetailsapiVersion: 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"
- secretPath: "secret/data/auth"
objectName: "jwt-auth"
syncOptions:
syncAll: true
type: Opaque
format: plaintext
jsonPath: "$.bad.path"
secrets:
- secretName: db-creds
format: json
jsonPath: "$.data.data"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox-deployment-opaque
labels:
app: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
terminationGracePeriodSeconds: 0
containers:
- image: k8s.gcr.io/e2e-test-images/busybox:1.29
name: busybox
imagePullPolicy: IfNotPresent
command:
- "/bin/sleep"
- "10000"
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: database-username
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-password
key: password
volumeMounts:
- name: secrets-store-inline
mountPath: "/mnt/secrets-store"
readOnly: true
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
volumes:
- name: secrets-store-inline
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "vault-spc" kubectl get secrets
# Output
database-password Opaque 1 2s
database-username Opaque 1 2s
db-creds Opaque 2 2s
default-token-nv2v8 kubernetes.io/service-account-token 3 7h24m
jwt-auth Opaque 1 2s
kubectl get secret database-username -o yaml
# Output
apiVersion: v1
data:
username: ZGItdXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T00:57:55Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: database-username
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-opaque-c4bf48669
uid: 22ee0f18-6494-430c-9f36-084f8b43a51f
resourceVersion: "48583"
uid: 63eca3cf-4cdd-45fb-8f90-9aed2962d699
type: Opaque
kubectl get secret database-password -o yaml
# Output
apiVersion: v1
data:
password: ZGItcGFzc3dvcmQ=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T00:57:55Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: database-password
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-opaque-c4bf48669
uid: 22ee0f18-6494-430c-9f36-084f8b43a51f
resourceVersion: "48582"
uid: 8d078727-f611-48bd-a9d4-821577de9372
type: Opaque
kubectl get secrets db-creds -o yaml
# Output
apiVersion: v1
data:
password: ZGItcGFzc3dvcmQ=
username: ZGItdXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T00:57:55Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: db-creds
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-opaque-c4bf48669
uid: 22ee0f18-6494-430c-9f36-084f8b43a51f
resourceVersion: "48580"
uid: a8941489-54bc-4ee7-b2e6-655131b422c1
type: Opaque
kubectl get secrets jwt-auth -o yaml
# Output
apiVersion: v1
data:
jwt-auth: eyJyZXF1ZXN0X2lkIjoiNmM0NDI3MjEtMzM2Yy1lOTA2LTY2ZTQtMGNlYTliNTU1N2U0IiwibGVhc2VfaWQiOiIiLCJsZWFzZV9kdXJhdGlvbiI6MCwicmVuZXdhYmxlIjpmYWxzZSwiZGF0YSI6eyJkYXRhIjp7ImNsaWVudF9pZCI6Im15LWNsaWVudC1pZCIsImNsaWVudF9zZWNyZXQiOiJteS1jbGllbnQtc2VjcmV0In0sIm1ldGFkYXRhIjp7ImNyZWF0ZWRfdGltZSI6IjIwMjItMDMtMjdUMjA6MzM6MzQuODI0NjYyNzk5WiIsImN1c3RvbV9tZXRhZGF0YSI6bnVsbCwiZGVsZXRpb25fdGltZSI6IiIsImRlc3Ryb3llZCI6ZmFsc2UsInZlcnNpb24iOjF9fSwid2FybmluZ3MiOm51bGx9
kind: Secret
metadata:
creationTimestamp: "2022-03-28T00:57:55Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: jwt-auth
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-opaque-c4bf48669
uid: 22ee0f18-6494-430c-9f36-084f8b43a51f
resourceVersion: "50439"
uid: de8fba16-4625-4550-9b11-c1119fca8892
type: Opaque Example 2
DetailsapiVersion: 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"
- secretPath: "secret/data/auth"
objectName: "jwt-auth"
syncOptions:
syncAll: true
type: Opaque
format: plaintext
jsonPath: "$.bad.path"
secrets:
- secretName: db-creds
format: json
jsonPath: "$.data.data"
- secretName: jwt-auth
format: json kubectl get secrets
# Output
NAME TYPE DATA AGE
database-password Opaque 1 3s
database-username Opaque 1 3s
db-creds Opaque 2 3s
default-token-nv2v8 kubernetes.io/service-account-token 3 7h50m
# Driver logs
E0328 01:24:10.663014 1 reconciler.go:493] "failed to get data in spc for secret" err="invalid json path $.bad.path" spc="default/vault-spc" secret="default/jwt-auth" controller="rotation" Example 3
DetailsapiVersion: 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"
- secretPath: "secret/data/auth"
objectName: "jwt-auth"
syncOptions:
syncAll: true
type: Opaque
format: plaintext
secrets:
- secretName: db-creds
format: json
jsonPath: "$.data.data"
- secretName: jwt-auth
format: json {
"request_id": "d912c8b6-7f44-cee3-847c-9276f0239399",
"lease_id": "",
"lease_duration": 0,
"renewable": false,
"data": {
"data": {
"client_id": "my-client-id",
"client_secret": "my-client-secret"
},
"metadata": {
"created_time": "2022-03-27T20:33:34.824662799Z",
"custom_metadata": null,
"deletion_time": "",
"destroyed": false,
"version": 1
}
},
"warnings": null
} kubectl get secrets jwt-auth -o yaml
# Output
apiVersion: v1
data:
data: eyJkYXRhIjp7ImNsaWVudF9pZCI6Im15LWNsaWVudC1pZCIsImNsaWVudF9zZWNyZXQiOiJteS1jbGllbnQtc2VjcmV0In0sIm1ldGFkYXRhIjp7ImNyZWF0ZWRfdGltZSI6IjIwMjItMDMtMjdUMjA6MzM6MzQuODI0NjYyNzk5WiIsImN1c3RvbV9tZXRhZGF0YSI6bnVsbCwiZGVsZXRpb25fdGltZSI6IiIsImRlc3Ryb3llZCI6ZmFsc2UsInZlcnNpb24iOjF9fQ==
lease_duration: MA==
lease_id: ""
renewable: ZmFsc2U=
request_id: ZmUwYWQ3ZWYtNjc0OS1iMWIxLTQ0NjItNDdiNDlmYWZhMWRl
warnings: bnVsbA==
kind: Secret
metadata:
creationTimestamp: "2022-03-28T01:30:04Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: jwt-auth
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-opaque-c4bf48669
uid: c85f1469-590d-461d-835b-4fdeb5802c36
resourceVersion: "52040"
uid: 0b9bc95c-cced-4b96-8cec-13f5e9c682ee
type: Opaque BasicExample
DetailsapiVersion: 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/basic-json"
objectName: "basic-json"
- secretPath: "secret/data/basic-plaintext"
objectName: "basic-plaintext"
secretKey: "credentials"
syncOptions:
syncAll: true
type: kubernetes.io/basic-auth
format: json
jsonPath: "$.data.data"
secrets:
- secretName: basic-plaintext
format: plaintext kubectl get secrets
# Output
basic-json kubernetes.io/basic-auth 2 11s
basic-plaintext kubernetes.io/basic-auth 2 11s
default-token-nv2v8 kubernetes.io/service-account-token 3 8h
kubectl get secrets basic-json -o yaml
# Output
apiVersion: v1
data:
password: YmFzaWMtcGFzc3dvcmQ=
username: YmFzaWMtdXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T01:59:11Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: basic-json
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-basic-54b4b754f4
uid: 5f297eee-541d-43a7-9ecc-5d26ff4e406e
resourceVersion: "55103"
uid: d5feb59a-61cf-4dbe-a092-73fb97200d6f
type: kubernetes.io/basic-auth
kubectl get secrets basic-plaintext -o yaml
# Output
apiVersion: v1
data:
password: YmFzaWMtcGFzc3dvcmQ=
username: YmFzaWMtdXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T01:59:11Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: basic-plaintext
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-basic-54b4b754f4
uid: 5f297eee-541d-43a7-9ecc-5d26ff4e406e
resourceVersion: "55104"
uid: db403427-ab1d-4b82-b001-d4c7d8d9ca9b
type: kubernetes.io/basic-auth TLSExample
DetailsapiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: vault-tls
spec:
provider: vault
parameters:
roleName: "csi"
vaultAddress: "http://vault.vault:8200"
objects: |
- secretPath: "secret/data/tls-json"
objectName: "tls-json"
- secretPath: "secret/data/tls-plaintext"
objectName: "tls-plaintext"
secretKey: "data"
syncOptions:
syncAll: true
type: kubernetes.io/tls
format: plaintext
jsonPath: "$.data.data"
secrets:
- secretName: tls-json
format: json kubectl get secrets
# Output
NAME TYPE DATA AGE
default-token-nv2v8 kubernetes.io/service-account-token 3 8h
tls-json kubernetes.io/tls 2 3s
tls-plaintext kubernetes.io/tls 2 3s
kubectl get secrets tls-plaintext -o yaml
# Output
apiVersion: v1
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPVENDQWlHZ0F3SUJBZ0lKQVAwSjVaN04wWTVmTUEwR0NTcUdTSWIzRFFFQkN3VUFNRE14RnpBVkJnTlYKQkFNTURtUmxiVzh1WVhwMWNtVXVZMjl0TVJnd0ZnWURWUVFLREE5aGEzTXRhVzVuY21WemN5MTBiSE13SGhjTgpNakF3TkRFMU1EUXlNelEyV2hjTk1qRXdOREUxTURReU16UTJXakF6TVJjd0ZRWURWUVFEREE1a1pXMXZMbUY2CmRYSmxMbU52YlRFWU1CWUdBMVVFQ2d3UFlXdHpMV2x1WjNKbGMzTXRkR3h6TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBeVMzWmt5M244SmxMQnhQTHpnVXBLWll4dnpSYWRlV0xtV1ZiSzlieQpvMDhTMFNzOEphbzdBeTF3SHRuTGJuNTJyekNYNklYMXNBZTFUQVQ3NTVHazdKdExNa3NodGo2RjhCTmVlbEV5CkUxZ3NCRTVudFk1dnlMVG0valpVSUt6Mlo5VExucXZRVG1wNmdKNjhCS0oxTm9ibnNIaUFjS2M2aEk3a21ZOUMKb3NobUFpNXFpS1lCZ3p2L3RoamkwMDkzdnRWU2E5aXdIaFFwK0FFSU1oa3ZNNVpaa2lVNWVFNk1UOVNCRWNWVwpLbVdGMjhVc0IwNGRhWXdTMk1LSjVsNmQ0bjBMVWRBRzBGQnQxbENvVDlyd1VEajlsM01xbWk5NTNndzI2TFVyCk5yWW5NLzhOMmpsN0N1eXc1YWxJV2FVRHJ0NWkrcHU4d2RXZnpWaytmTzd4OFFJREFRQUJvMUF3VGpBZEJnTlYKSFE0RUZnUVV3RkJiUjAxNE1jRVRkckdHa2xwRVFjbDcxUTB3SHdZRFZSMGpCQmd3Rm9BVXdGQmJSMDE0TWNFVApkckdHa2xwRVFjbDcxUTB3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFUZ1R5CmdnMVE2SVNTZWtpQkNlMTJkcVVUTUZRaDlHS3BmWVdLUmJNdGpPanBjN01kd2tkbW0zRnU2bDNSZkVGVDI4SWoKZnk5N0xNWXY4VzdiZWVtREZxZG1uZWIydzJ3dzBaQUZKZytHcUlKWjlzL0phZGlGQkROVTdDbUpNaEEyMjVRegpYQzhvdmVqaWVQc2xuTDRRSldsaFZHOTNabEJKNlNEa1JnZmNvSVcyeDRJQkU2d3Y3am1SRjRsT3ZiM3oxZGRQCmlQUXFoYkVFYndNcFhtV3Y3LzJSbmpBSGRqZEdhV1JNQzUrQ2FJK2xxSHlqNmlyMWMrZTZ1MVFVWTU0cWptZ00Ka29OL2ZycVlhYjVFazNrYXVqMWlxVzdyUGtyRkNxVDJldmgwWVJxYjFiRnNDTEpyUk54bk9aNXdLWFYvT1lRYQpRWDV0MHdGR0NaMEtsYlhEaXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBeVMzWmt5M244SmxMQnhQTHpnVXBLWll4dnpSYWRlV0xtV1ZiSzlieW8wOFMwU3M4CkphbzdBeTF3SHRuTGJuNTJyekNYNklYMXNBZTFUQVQ3NTVHazdKdExNa3NodGo2RjhCTmVlbEV5RTFnc0JFNW4KdFk1dnlMVG0valpVSUt6Mlo5VExucXZRVG1wNmdKNjhCS0oxTm9ibnNIaUFjS2M2aEk3a21ZOUNvc2htQWk1cQppS1lCZ3p2L3RoamkwMDkzdnRWU2E5aXdIaFFwK0FFSU1oa3ZNNVpaa2lVNWVFNk1UOVNCRWNWV0ttV0YyOFVzCkIwNGRhWXdTMk1LSjVsNmQ0bjBMVWRBRzBGQnQxbENvVDlyd1VEajlsM01xbWk5NTNndzI2TFVyTnJZbk0vOE4KMmpsN0N1eXc1YWxJV2FVRHJ0NWkrcHU4d2RXZnpWaytmTzd4OFFJREFRQUJBb0lCQUNXOUtpR0g3UWlmV1FtOAoxdkNRMjBYbjNQRW5Xd2pleEs5RDZqUnhJQmN1a0RNWXp6R3BtMlJmTTdjMTJBbWtRNU5kcEVHRVQ3Qno0YmllCi9ycFVBZ0k0cDRTSnY0czJjMTJ6NUhkQm9Ba3M4eVRqcUdUUWtYdXZJOTN3Unh2U0ZtOG41Z0tmN0hXcFY4MmsKK1ZSTlBYdTdGdDl0MjNwR2hKMjFTSURkOVB3Z0VvQ08rMkhXaUlSdmowLzZVZ2puTmRURTVBU2FmbFlVOWJSTApaVVFFZS9HOVVWMnhvdGFGQjBYbnV3dHlHdkNDa055K1hUOFVmVWJHTEVkanV1dmRQSkJJdkdYTGlJM0d5TGtUCncxMkIyUHVUY0p0NWw3bVpaTW84bjhTVjdDQVBJcWdhMEswcVJ4b0FKOVUxWlN3MGdpankyNUVjd1VVYndGdE0KeS8zQTA3a0NnWUVBNnYvV01jZlVJcndMbE1jWFJMdEZUdVFzODJXQlZvUThRRjB0LzhHTXFOV0o2RDdWTS81UgpncnpGcWRKTm1tbXRQaWsxNzluMFFsQmtyb3gxdXlYNk9KTGpENUdyZ0lhRjhmVUNRSEk2Q3RKelcyQkdxcUNjCmpLRVNBL2tHL1Z4NUkvcFpnVTVqRllBQ3drL1F5QjNwQjQvek4yK3dwSjIzMXI5YkRtZjc2VU1DZ1lFQTJ5aEwKai93MWtHdlF3dVo1K2FZUncveTFtU3h2M0RFZUYwNXNEUDNHMzBjVFArbHRQdnlpd0tIYytHcGhrYzFaVnVtcwplSk51Z1VoUEdiZjBvc2dOUTBFVTNoWmFIYnFLbG9saGVIVEhpTGNjTm1wdnNTMFJNTGFEZHhZTENSN3RzL3p2CjlHbWdRRGpDMUZ5RHlNb2hPQytqcHhENG54UDZXd0RQQkd6T1dyc0NnWUJvWFFBU0VZMjdjMG10WkJHSVc2YTMKRjR5Qzk1Uk9DT0g5TDZTTERiamJEaUoxdXRuU2dtSk9ybWtXTnloeHAvYmhRVW9MMy82aktMTGQ5ZEVmTVFtRAo4Ry9ZYlRmK0YwQUxLV1U3bTc3OUtXTkR0R2tOdzlCaWYvSFJROW4vMFA2WHBNbjFMQXZDODV2R0VjNUY0VXFwCktodTRoZWlyNHNOUElxSmNybjlvb3dLQmdRRFdnS1VjdzcxbG9yRk00aXJHdDgyeHB4blRWMVFSRDU0bEw2UEEKUDIrMFFDdk9KVm5CQ2FMK3FpN09rY2ozM1NheFU3dDB0TkMzQnlBc3l3bE5ma0xFN2VGUDBrN1h4Q283VnpHbwpwWnhhNnVaWk9Hb2ozemx4RldLdFgwemoyM3JLN1NtcmJ1cmprT3JZMGNoRzhkZjcxMXoxZXozczdhL2YvSFdqCkhpQUhXd0tCZ1FEbUVTZFFzekdFQ28xaGQ5bEdyTzRZVU1jaDlVQ09KMWVlZzY4cXU1ZjFGcnA5cVg4RFMyUmEKamh3OTZwdE9XczU4elNyNVBXaHdMRGp4WDFGRnp1N0tkQm51UlN6RXNOYmpEWjdyWEZYRE05K3lnR05uenFCTgpzYUN6T0ExUHg5amFnNDNoZ3JEckZOVVhrVXRid1NmdU5pUnNBWFMxZmZhN21DbFNqbGo0ZUE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T02:15:37Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: tls-plaintext
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-tls-656dfb6d85
uid: d1a66939-cd8e-40b4-8487-ad5aed859880
resourceVersion: "56816"
uid: 17b05390-cf84-4d09-b54a-00781f06064f
type: kubernetes.io/tls
kubectl get secrets tls-json -o yaml
apiVersion: v1
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURPVENDQWlHZ0F3SUJBZ0lKQVAwSjVaN04wWTVmTUEwR0NTcUdTSWIzRFFFQkN3VUFNRE14RnpBVkJnTlYKQkFNTURtUmxiVzh1WVhwMWNtVXVZMjl0TVJnd0ZnWURWUVFLREE5aGEzTXRhVzVuY21WemN5MTBiSE13SGhjTgpNakF3TkRFMU1EUXlNelEyV2hjTk1qRXdOREUxTURReU16UTJXakF6TVJjd0ZRWURWUVFEREE1a1pXMXZMbUY2CmRYSmxMbU52YlRFWU1CWUdBMVVFQ2d3UFlXdHpMV2x1WjNKbGMzTXRkR3h6TUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBeVMzWmt5M244SmxMQnhQTHpnVXBLWll4dnpSYWRlV0xtV1ZiSzlieQpvMDhTMFNzOEphbzdBeTF3SHRuTGJuNTJyekNYNklYMXNBZTFUQVQ3NTVHazdKdExNa3NodGo2RjhCTmVlbEV5CkUxZ3NCRTVudFk1dnlMVG0valpVSUt6Mlo5VExucXZRVG1wNmdKNjhCS0oxTm9ibnNIaUFjS2M2aEk3a21ZOUMKb3NobUFpNXFpS1lCZ3p2L3RoamkwMDkzdnRWU2E5aXdIaFFwK0FFSU1oa3ZNNVpaa2lVNWVFNk1UOVNCRWNWVwpLbVdGMjhVc0IwNGRhWXdTMk1LSjVsNmQ0bjBMVWRBRzBGQnQxbENvVDlyd1VEajlsM01xbWk5NTNndzI2TFVyCk5yWW5NLzhOMmpsN0N1eXc1YWxJV2FVRHJ0NWkrcHU4d2RXZnpWaytmTzd4OFFJREFRQUJvMUF3VGpBZEJnTlYKSFE0RUZnUVV3RkJiUjAxNE1jRVRkckdHa2xwRVFjbDcxUTB3SHdZRFZSMGpCQmd3Rm9BVXdGQmJSMDE0TWNFVApkckdHa2xwRVFjbDcxUTB3REFZRFZSMFRCQVV3QXdFQi96QU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFUZ1R5CmdnMVE2SVNTZWtpQkNlMTJkcVVUTUZRaDlHS3BmWVdLUmJNdGpPanBjN01kd2tkbW0zRnU2bDNSZkVGVDI4SWoKZnk5N0xNWXY4VzdiZWVtREZxZG1uZWIydzJ3dzBaQUZKZytHcUlKWjlzL0phZGlGQkROVTdDbUpNaEEyMjVRegpYQzhvdmVqaWVQc2xuTDRRSldsaFZHOTNabEJKNlNEa1JnZmNvSVcyeDRJQkU2d3Y3am1SRjRsT3ZiM3oxZGRQCmlQUXFoYkVFYndNcFhtV3Y3LzJSbmpBSGRqZEdhV1JNQzUrQ2FJK2xxSHlqNmlyMWMrZTZ1MVFVWTU0cWptZ00Ka29OL2ZycVlhYjVFazNrYXVqMWlxVzdyUGtyRkNxVDJldmgwWVJxYjFiRnNDTEpyUk54bk9aNXdLWFYvT1lRYQpRWDV0MHdGR0NaMEtsYlhEaXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRREpMZG1UTGVmd21Vc0gKRTh2T0JTa3BsakcvTkZwMTVZdVpaVnNyMXZLalR4TFJLendscWpzRExYQWUyY3R1Zm5hdk1KZm9oZld3QjdWTQpCUHZua2FUc20wc3lTeUcyUG9Yd0UxNTZVVElUV0N3RVRtZTFqbS9JdE9iK05sUWdyUFpuMU11ZXE5Qk9hbnFBCm5yd0VvblUyaHVld2VJQndwenFFanVTWmowS2l5R1lDTG1xSXBnR0RPLysyR09MVFQzZSsxVkpyMkxBZUZDbjQKQVFneUdTOHpsbG1TSlRsNFRveFAxSUVSeFZZcVpZWGJ4U3dIVGgxcGpCTFl3b25tWHAzaWZRdFIwQWJRVUczVwpVS2hQMnZCUU9QMlhjeXFhTDNuZUREYm90U3MydGljei93M2FPWHNLN0xEbHFVaFpwUU91M21MNm03ekIxWi9OCldUNTg3dkh4QWdNQkFBRUNnZ0VBSmIwcUlZZnRDSjlaQ2J6VzhKRGJSZWZjOFNkYkNON0VyMFBxTkhFZ0Z5NlEKTXhqUE1hbWJaRjh6dHpYWUNhUkRrMTJrUVlSUHNIUGh1SjcrdWxRQ0FqaW5oSW0vaXpaelhiUGtkMEdnQ1N6egpKT09vWk5DUmU2OGozZkJIRzlJV2J5Zm1BcC9zZGFsWHphVDVWRTA5ZTdzVzMyM2Jla2FFbmJWSWdOMzAvQ0FTCmdJNzdZZGFJaEcrUFQvcFNDT2MxMU1Ua0JKcCtWaFQxdEV0bFJBUjc4YjFSWGJHaTFvVUhSZWU3QzNJYThJS1EKM0w1ZFB4UjlSc1lzUjJPNjY5MDhrRWk4WmN1SWpjYkl1UlBEWFlIWSs1TndtM21YdVpsa3lqeWZ4SlhzSUE4aQpxQnJRclNwSEdnQW4xVFZsTERTQ0tQTGJrUnpCUlJ2QVcwekwvY0RUdVFLQmdRRHEvOVl4eDlRaXZBdVV4eGRFCnUwVk81Q3p6WllGV2hEeEFYUzMvd1l5bzFZbm9QdFV6L2xHQ3ZNV3AwazJhYWEwK0tUWHYyZlJDVUdTdWpIVzcKSmZvNGt1TVBrYXVBaG9YeDlRSkFjam9LMG5OYllFYXFvSnlNb1JJRCtRYjlYSGtqK2xtQlRtTVZnQUxDVDlESQpIZWtIai9NM2I3Q2tuYmZXdjFzT1ovdnBRd0tCZ1FEYktFdVAvRFdRYTlEQzVubjVwaEhEL0xXWkxHL2NNUjRYClRtd00vY2JmUnhNLzZXMCsvS0xBb2R6NGFtR1J6VmxXNmF4NGsyNkJTRThadC9TaXlBMURRUlRlRmxvZHVvcVcKaVdGNGRNZUl0eHcyYW0reExSRXd0b04zRmdzSkh1MnovTy8wYWFCQU9NTFVYSVBJeWlFNEw2T25FUGlmRS9wYgpBTThFYk01YXV3S0JnR2hkQUJJUmpidHpTYTFrRVloYnByY1hqSUwzbEU0STRmMHZwSXNOdU5zT0luVzYyZEtDCllrNnVhUlkzS0hHbjl1RkJTZ3ZmL3FNb3N0MzEwUjh4Q1lQd2I5aHROLzRYUUFzcFpUdWJ2djBwWTBPMGFRM0QKMEdKLzhkRkQyZi9RL3Bla3lmVXNDOEx6bThZUnprWGhTcWtxRzdpRjZLdml3MDhpb2x5dWYyaWpBb0dCQU5hQQpwUnpEdldXaXNVemlLc2EzemJHbkdkTlhWQkVQbmlVdm84QS9iN1JBSzg0bFdjRUpvdjZxTHM2UnlQZmRKckZUCnUzUzAwTGNISUN6TENVMStRc1R0NFUvU1R0ZkVLanRYTWFpbG5GcnE1bGs0YWlQZk9YRVZZcTFmVE9QYmVzcnQKS2F0dTZ1T1E2dGpSeUVieDEvdlhYUFY3UGV6dHI5LzhkYU1lSUFkYkFvR0JBT1lSSjFDek1ZUUtqV0YzMlVhcwo3aGhReHlIMVFJNG5WNTZEcnlxN2wvVVd1bjJwZndOTFpGcU9IRDNxbTA1YXpuek5Ldms5YUhBc09QRmZVVVhPCjdzcDBHZTVGTE1TdzF1TU5udXRjVmNNejM3S0FZMmZPb0UyeG9MTTREVS9IMk5xRGplR0NzT3NVMVJlUlMxdkIKSis0MkpHd0JkTFY5OXJ1WUtWS09XUGg0Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
kind: Secret
metadata:
creationTimestamp: "2022-03-28T02:15:37Z"
labels:
secrets-store.csi.k8s.io/managed: "true"
name: tls-json
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: busybox-deployment-tls-656dfb6d85
uid: d1a66939-cd8e-40b4-8487-ad5aed859880
resourceVersion: "56815"
uid: 76b63d13-f1fd-41fd-9fc5-ca83a524c53d
type: kubernetes.io/tls Code
Snippetfunc GetSecretData(secretObjData []*secretsstorev1.SecretObjectData, secretType corev1.SecretType, files map[string]string, format, jsonPath string) (map[string][]byte, error) {
datamap := make(map[string][]byte)
for _, data := range secretObjData {
objectName := strings.TrimSpace(data.ObjectName)
dataKey := strings.TrimSpace(data.Key)
if len(objectName) == 0 {
return datamap, fmt.Errorf("object name in secretObjects.data is empty")
}
if len(dataKey) == 0 {
return datamap, fmt.Errorf("key in secretObjects.data is empty")
}
file, ok := files[objectName]
if !ok {
return datamap, fmt.Errorf("file matching objectName %s not found in the pod", objectName)
}
content, err := os.ReadFile(file)
if err != nil {
return datamap, fmt.Errorf("failed to read file %s, err: %w", objectName, err)
}
// TODO (manedurphy) Take auto-detection into consideration
switch format {
case formatJSON:
var (
jsonContent map[string]interface{}
valBytes []byte
err error
)
if err = json.Unmarshal(content, &jsonContent); err == nil {
if jsonPath != "" {
var (
jsonPathSplit []string
valid bool
)
jsonPathSplit = strings.Split(jsonPath, ".")[1:]
for _, path := range jsonPathSplit {
if jsonContent, valid = jsonContent[path].(map[string]interface{}); !valid {
return datamap, fmt.Errorf("invalid json path %s", jsonPath)
}
}
}
for key, val := range jsonContent {
switch val := val.(type) {
case string:
valBytes = []byte(val)
default:
if valBytes, err = json.Marshal(val); err != nil {
return datamap, fmt.Errorf("failed to marshal value %v, err: %w", val, err)
}
}
datamap[key] = valBytes
}
continue
}
return datamap, fmt.Errorf("failed to unmarshal JSON file contents %s, err: %w", file, err)
default:
datamap[dataKey] = content
if secretType == corev1.SecretTypeTLS {
c, err := GetCertPart(content, dataKey)
if err != nil {
return datamap, fmt.Errorf("failed to get cert data from file %s, err: %w", file, err)
}
datamap[dataKey] = c
}
if secretType == corev1.SecretTypeBasicAuth {
username, password := getBasicAuthCredentials(content)
delete(datamap, dataKey)
datamap[basicAuthUsername] = []byte(username)
datamap[basicAuthPassword] = []byte(password)
}
}
}
return datamap, nil
} Additional Notes
|
That all looks excellent to me, nice work! To confirm, specifying I'd be curious to hear the feedback from the meeting if you're able to share it. |
That is correct, omitting the top-level |
commenting to keep this alive. the last several weeks have been busy but i will be returning to this soon. |
What feedback did you get on this in the meeting? |
@fubar that moving forward with the |
@manedurphy nice, one step at a time. |
Sync All Secrets to K8s
Addresses #529
Purpose
SecretProviderClass
to K8s.SecretObject
for each secret that they want to sync. Instead, they just have to declare that all secrets that are mounted to the filesystem of the pod should be synced to K8s, as well as the K8s secret type that should be used for each secret listed in the parameters.Implementation
Type Definitions
SyncOptions
type has been defined and added as a field for theSecretProviderClass
.Type Definitions
Patcher
Patcher
loops through theSecretObjects
that are defined in theSecretProviderClass
, we need to build them up whensyncOptions.syncAll
is set to true since the user is not expected to manually create secrets objects in the configuration.Patcher
Reconcile
Reconcile
also iterates through secrets objects, so we must build them whensyncOptions.syncAll
is set to true.pkg/rotation/reconciler.go
.Reconcile
BuildSecretObjects
BuildSecretObjects
function inpkg/util/spcutil/secret_object_builder.go
takes the files and K8s secret type as parameters and builds the secret objects on theSecretProviderClass
.BuildSecretObjects
Usage
docs/book/src/topics/sync-all-secrets-to-k8s.md
.Additional Notes
e2e-provider.bats
as well.