Dapr 1.17.18
This update contains the following security fix:
- Security: Operator disclosed resources and secrets scoped to other apps
Security: Operator disclosed resources and secrets scoped to other apps
Problem
In Kubernetes mode, the operator's ListHTTPEndpoints API returned every HTTPEndpoint in the caller's namespace, including those scoped to other apps, with their Kubernetes secret references resolved to plaintext values.
GetConfiguration returned any Configuration in the namespace by name, with secret references in its OpenTelemetry headers resolved, whichever configuration the calling app was assigned.
ListResiliency, GetResiliency and ListSubscriptions also ignored scopes. These APIs do not resolve or return secrets, so only the resource definitions were exposed.
Impact
Any workload able to get a Dapr workload identity for an app ID in a namespace, which includes every Dapr-enabled pod, could call the operator directly and read the credentials of HTTPEndpoint resources scoped to other apps in that namespace.
This covers header values such as Authorization tokens, and client TLS certificates, private keys and root CAs.
It could also read secret OpenTelemetry headers of Configurations assigned to other apps.
Only values from the Kubernetes secret store (the default) were disclosed, because the operator resolves only those. Other namespaces were not exposed.
Sidecars already ignored resources not scoped to their app ID, so applications behaved correctly; the problem was what the operator sent.
Reported by Sathwik Yadlapati.
Root Cause
These operator APIs checked that the caller's identity was in the requested namespace, but did not compare the caller's app ID with each resource's scopes, as ListComponents and the resource update streams already did.
GetConfiguration trusted the configuration name sent by the sidecar.
Solution
The operator now skips resources whose scopes do not include the calling app ID before it resolves any secret, and GetResiliency reports them as not found.
GetConfiguration only returns the configuration named in the dapr.io/config annotation of one of the calling app's pods, and returns PermissionDenied otherwise. The operator reads the app's pods from the Kubernetes API, selected by the dapr.io/app-id label.
A daprd you run in Kubernetes mode without the injector, with --config, must be in a pod with the dapr.io/enabled: "true", dapr.io/app-id and dapr.io/config annotations and the dapr.io/app-id label, and its container must be named daprd so the injector does not add a second one.
Otherwise the operator refuses the configuration and daprd fails to start.