Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/book/src/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ The driver uses gRPC to communicate with the provider. To implement a secrets-st

See [design doc](https://docs.google.com/document/d/10-RHUJGM0oMN88AZNxjOmGz0NsWAvOYrWUEV-FbLWyw/edit?usp=sharing) for more details.

The `MountRequest` message structure includes several additional keys in the `Attributes` field that a provider can use when retrieving a secret. Keys beginning with `csi.storage.k8s.io` are [passed through from the Kubelet](https://kubernetes-csi.github.io/docs/pod-info.html?highlight=pod.name#pod-info-on-mount-with-csi-driver-object) if `podInfoOnMount` is `true` on the CSI driver.

| Attribute Key | Description |
| --- | ---- |
| `csi.storage.k8s.io/pod-name` | Pod name |
| `csi.storage.k8s.io/pod.namespace` | Pod namespace |
| `csi.storage.k8s.io/pod.uid` | Pod UID |
| `csi.storage.k8s.io/serviceAccount.name` | The Pod's ServiceAccount name |
| `csi.storage.k8s.io/serviceAccount.tokens` | A JSON structure serialized to a string containing service account tokens belonging to a pod [when a CSI driver has `tokenRequests` configured](https://kubernetes-csi.github.io/docs/token-requests.html). |
| `secrets-store-csi-driver.sigs.k8s.io/volume.id` | The CSI Volume's ID from the `NodePublishVolumeRequest` call. This may be useful as a cache key if using Service Account tokens to fetch secrets. |


## Features supported by current providers

| Features \ Providers | Azure | GCP | AWS | Vault | Akeyless | Conjur |
Expand Down
4 changes: 4 additions & 0 deletions pkg/secrets-store/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const (
// CSIPodServiceAccountTokens is the service account tokens of the pod that the mount is created for
CSIPodServiceAccountTokens = "csi.storage.k8s.io/serviceAccount.tokens" //nolint

SecretStoreVolumeID = "secrets-store-csi-driver.sigs.k8s.io/volume.id"

secretProviderClassField = "secretProviderClass"
)

Expand Down Expand Up @@ -181,6 +183,8 @@ func (ns *nodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
for k, v := range attrib {
parameters[k] = v
}
// Add the volume ID to the parameters
parameters[SecretStoreVolumeID] = volumeID
// csi.storage.k8s.io/serviceAccount.tokens is empty for Kubernetes version < 1.20.
// For 1.20+, if tokenRequests is set in the CSI driver spec, kubelet will generate
// a token for the pod and send it to the CSI driver.
Expand Down
13 changes: 13 additions & 0 deletions test/bats/e2e-provider.bats
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ export VALIDATE_TOKENS_AUDIENCE=$(get_token_requests_audience)
log_token_requests_audience
}

@test "ensure volume ID is set" {
if [[ -n "${VALIDATE_VOLUME_ID}" ]]; then
kubectl create ns validate-volume-id
local curl_pod_name=curl-$(openssl rand -hex 5)
kubectl run ${curl_pod_name} -n validate-volume-id --image=curlimages/curl:7.75.0 --labels="util=validate-volume-id" -- tail -f /dev/null
kubectl wait -n validate-volume-id --for=condition=Ready --timeout=60s pod ${curl_pod_name}
local pod_ip=$(kubectl get pod -n kube-system -l app=csi-secrets-store-e2e-provider -o jsonpath="{.items[0].status.podIP}")
run kubectl exec ${curl_pod_name} -n validate-volume-id -- curl http://${pod_ip}:8080/validate-volume-id
kubectl delete pod -l util=validate-volume-id -n validate-volume-id --force --grace-period 0
kubectl delete ns validate-volume-id
fi
}


@test "secretproviderclasses crd is established" {
kubectl wait --for condition=established --timeout=60s crd/secretproviderclasses.secrets-store.csi.x-k8s.io
Expand Down
1 change: 1 addition & 0 deletions test/e2eprovider/e2e_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func mainErr() error {

http.HandleFunc("/rotation", server.RotationHandler)
http.HandleFunc("/validate-token-requests", server.ValidateTokenAudienceHandler)
http.HandleFunc("/validate-volume-id", server.VolumeIDHandler)

server := &http.Server{
Addr: ":8080",
Expand Down
25 changes: 24 additions & 1 deletion test/e2eprovider/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ This is mock key

podUIDAttribute = "csi.storage.k8s.io/pod.uid"
serviceAccountTokensAttribute = "csi.storage.k8s.io/serviceAccount.tokens" //nolint

secretStoreVolumeID = "secrets-store-csi-driver.sigs.k8s.io/volume.id"
// RWMutex is to safely access podCache
m sync.RWMutex
)
Expand Down Expand Up @@ -186,6 +186,12 @@ func (s *Server) Mount(ctx context.Context, req *v1alpha1.MountRequest) (*v1alph
}
}

if _, ok := os.LookupEnv("VALIDATE_VOLUME_ID"); ok {
if err := validateVolumeIDAttr(attrib); err != nil {
return nil, fmt.Errorf("failed to validate volume ID, error: %w", err)
}
}

m.Lock()
podCache[attrib[podUIDAttribute]] = true
m.Unlock()
Expand Down Expand Up @@ -247,6 +253,12 @@ func ValidateTokenAudienceHandler(w http.ResponseWriter, r *http.Request) {
klog.InfoS("Validation for token requests audience", "audience", os.Getenv("VALIDATE_TOKENS_AUDIENCE"))
}

// VolumeIDHandler enables volume ID for the mock provider
func VolumeIDHandler(w http.ResponseWriter, r *http.Request) {
os.Setenv("VALIDATE_VOLUME_ID", "true")
klog.InfoS("Volume ID validation enabled")
}

// validateTokens checks there are tokens for distinct audiences in the
// service account token attribute.
func validateTokens(tokenAudiences, saTokens string) error {
Expand All @@ -266,3 +278,14 @@ func validateTokens(tokenAudiences, saTokens string) error {
}
return nil
}

func validateVolumeIDAttr(attributes map[string]string) error {
volumeID, ok := attributes[secretStoreVolumeID]
if !ok {
return fmt.Errorf("volume ID is not set")
}
if volumeID == "" {
return fmt.Errorf("volume ID is empty")
}
return nil
}
Loading