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
[PROPOSAL] Identity Management Overhaul #5756
Comments
Thanks, this all looks good to me and most solutions (a, c, d) are straightforward to implement. Re: b, as you correctly mentioned, dropping the ability to select an app-id is disruptive and thus wouldn't be possible as it's very core to Dapr and we wouldn't want to drop custom application identities only to mitigate a Kubernetes API operation (referencing the alternative of the injector getting pods). Another important thing to remember is that workload identities should work outside of Kubernetes, as Dapr runs in self-hosted mode, so any change needs to be compatible with this requirement. For this (main) reason I think relying on Service Accounts for identities is not the best way to go. |
Thanks @yaron2, makes sense. I agree that we should stay clear of tying Dapr APP IDs with Service Accounts in favor of getting the ID from the kube API. A new spiffe ID could look something like: I'll update the proposal with these changes. |
Thanks, as discussed - the current SPIFFE ID already contains the app id. |
Lately we've seen that services that request data about pods have a much increased memory requirement. Users have reported that the Dapr operator can use up to 800MB of memory even without a single Dapr-enabled pod. Since the K8s SDK keep a cache of data in memory, this can mean that sentry would have to cache a lot of data too. Can I propose a slightly different approach to this?
|
@ItalyPaleAle I don't think the memory consumption is a problem here as we will not be using an informer in sentry, instead we will be using a single GET request to the API server on incoming requests. If using an informer is a requirement, then we could look at using a metadata only watch since we only need the pod metadata- my fear with that though is that sentry will often have an out of date state of the pod or not observe a new pod before the dapr container requests it's identity, forcing a retries and increasing error rates. I'm also quite against having secrets be readable through the API (env var / ConfigMap). |
Ok, if this doesn't cache data locally in the app/SDK, it would probably be OK. A self-contained token would still be faster however at verification time. It wouldn't be a "secret" but just a bearer token. Issued by the injector (which has the private key), it attests that the app has the app-id it claims to have. The app presents that to sentry and sentry validates it. The token isn't secret because it contains only public claims (it shouldn't be a concern if the app knows its app-id) and using signatures we can guarantee it won't be tampered with. (In my last message I wrote this could be a JWT; I was wrong there on second thought and it could be a JWS but not JWT, which would require certain claims to be present) |
I'd definitely consider the app ID token to be a secret since it is basically a password- even though it def contains public information, having access to the is token allows actors to request for dapr identities for an App ID which is not itself (impersonation). |
That makes sense |
@JoshVanL I've been thinking about this more and while you're right, I actually don't think it matters in the sense that it doesn't make a difference. Your proposal is to have certificates issued if:
In this case, the relevant thing is still the bound token which is stored in If we use a separate JWS issued by the Dapr Injector, for Sentry to issue a token you will need:
We could store the JWS in a path like The benefits of using a separate, Dapr-issued JWS are:
|
The problem with this is that sentry or injector needs to create that volume. In vanilla Kubernetes, the only real way to do this is by creating ConfigMaps or Secrets. This is problematic because they are accessible through the API and would also mean granting sentry/injector the permissions to access those resources in all namespaces. Not to mention the overhead of ensuring the contents of those volumes is kept up to date and correct- forcing the writer to watch pods.. The creation of these resources is comparable to the GET Pods operation. The other option is to create a dapr CSI driver which is OTT, even more resource intensive, and comes with its own security model to manage/maintain. As an aside, it is quite problematic that we can set the App ID as a Pod annotation since annotations are mutable on Pods, meaning that a dapr sidecar can be using an identity document which does not match the k8s API assumption. |
Another thing of note implementing the above- sentry is also going to have to GET Configurations in order to determine the trust domain of the app. |
This seems another point in the case of using pre-signed token :)
True, then maybe setting an env var may be better. The env var secret store could be changed to disable access to that variable.
Is the trust domain of the app a new value that's being added to the Dapr Configuration CRD (specifically If you think this should not change frequently, I'd recommend passing it to Sentry as a CLI flag that is set via variables in the Helm chart. |
The trust domain already exists in the configuration API. |
This issue has been automatically marked as stale because it has not had activity in the last 60 days. It will be closed in the next 7 days unless it is tagged (pinned, good first issue, help wanted or triaged/resolved) or other activity occurs. Thank you for your contributions. |
In This unfortunately means that in To summarize the security posture: v1.11
v1.12
v1.13
|
This issue has been automatically marked as stale because it has not had activity in the last 60 days. It will be closed in the next 7 days unless it is tagged (pinned, good first issue, help wanted or triaged/resolved) or other activity occurs. Thank you for your contributions. |
moving tracking issue to 1.14 for there are still open issues/PRs linked to it. |
Done! |
PRs
mtls.mountSentryVolume
,extraVolumes
,extraVolumeMounts
Helm Chart options #6895/area runtime
/area operator
/area placement
This proposal describes a strategy for overhauling the identity story in Dapr. It involves several distinct points of interest, but have been put into the same proposal as they all serve the same goal and together will have a greater sum than their parts.
Motivation
In Kubernetes mode, Dapr is made up from a set of privileged control plane components (sentry, operator, injector, dashboard, placement (also self hosted)), and lower privileged workload daprd sidecars. In order for Dapr to maintain a strong security posture to ensure malicious, or otherwise, actors cannot read/write/get/post data it should not have access to, Dapr must have a strong authentication and authorization story. Today, Dapr uses the sentry control plane component as an identity provider and certificate authority to the rest of the Dapr components and workloads.
I've identified a number of things Dapr can improve which should give us:
Goals
Non-Goals
Current Shortfalls
Dapr currently has a number of shortcomings which prevent it from having a strong identity story, and undermines the mTLS of Dapr components.
1. Issuer Key Sharing
The dapr sidecar injector currently adds the Sentry issuing certificate and private key to Dapr workloads as environment variables. This means that all Dapr sidecars have access to the issuer private key and so can mint any certificate identity they want, allowing workloads to impersonate anyone (including the control plane). This is perhaps the most critical problem to fix.
2. Broken CA Auth
Sentry is the component that acts as the Certificate Authority, and is responsible for authenticating dapr workloads and minting them appropriate X.509 identities. This flow follows a common pattern whereby workloads send an audience scoped Kubernetes ServiceAccount token which Sentry verifies against the Kubernetes API server.
Reviewing the sentry flow:
The current implementation does not currently validate that the ServiceAccount associated with the reviewed token matches the identity requested by the client, and will instead validate the App ID which the client can set any value to.
dapr/pkg/sentry/server/server.go
Line 150 in 9f143d8
dapr/pkg/sentry/ca/certificate_authority.go
Line 125 in 9f143d8
dapr/pkg/sentry/identity/kubernetes/validator.go
Line 40 in 9f143d8
dapr/pkg/sentry/server/server.go
Line 158 in 1c95ad1
This means that clients have unbounded impersonation (i.e. Alice and request a cert containing Bob).
3. Control Plane components uses same ServiceAccount
Currently (bar the dashboard), all control plane components use the same ServiceAccount. This means that not only does each component share the same Kubernetes identity, they all share the same RBAC, violating POLP.
4. Injector uses separate trust anchor for MutatingWebhook
A lesser problem in Dapr is that the sidecar injector currently uses a separate root of trust for serving the MutatingWebhook server, with the certificate being signed and managed through Helm. This is somewhat disjointed as all other certificates are managed and observed through sentry. We basically have 2 systems of identity management.
Solutions
a) Removing Issuer certificate key pair from workloads
Change the injector to no longer set the
DAPR_CERT_CHAIN
andDAPR_CERT_KEY
environment variables.Deprecate the flags
--issuer-key-secret-key
and--issuer-certificate-secret-key
on daprd and all control plane components except the sentry component. These flags can still be set for backwards compatibility however functionally do nothing. If set in future versions, a warning log output should describe that these flags no longer do anything.Add the
--sentry-trust-domain
flag which will be used by sentry clients to validate sentry connections. This flag gets set by the injector for daprd workloads.Refactor the runtime so that all components request their identity from sentry on boot.
b) App ID
App ID is a string identifier that is used for service invocation/communicating with other dapr workloads, and scoping access.
In Kubernetes, workloads can set their own App ID (via the
dapr.io/app-id
annotation), or will it will be defaulted to the Pods name. This App ID has no relation to the Pod's ServiceAccount which results in sentry having no way of validating the authenticity of incoming App identity requests. Some possible solutions to this:spiffe://<trust domain>/ns/<namespace>/appid/<app ID name>
. We should consider sentry GETting Pods as an expensive operation and would slow down init time of dapr workloads.Drop the ability for workloads to self select their APP ID name, in favor of enforcing they be identified by their ServiceAccount. This would eliminate the overhead of GETting pods, however would result in a breaking change to the Dapr API and highly likely disruptive to existing code paths/tooling.Going forward with the first option.Both solutions here are not great.
c) Separate Service Acounts for control plane components.
Each dapr component should be given their own distinct Service Account. All but the Sentry component should request their identity certificate from Sentry. This is also a good opportunity to review the existing RBAC and ensure POLP.
d) Injector uses identity certificate for Mutating Webhook
The injector identity will be special cased so Sentry will add the injector DNS server name to its identity document. This will allow the injector to serve its Dapr identity document to the Kubernetes API server. Change injector (or Sentry) to update its own Mutating Webhook with the CA trust bundle of the Dapr trust domain. Remove webhook cert management from helm.
misc:
The text was updated successfully, but these errors were encountered: