int128 / kubelogin

kubectl plugin for Kubernetes OpenID Connect authentication (kubectl oidc-login)
Apache License 2.0
1.64k stars 191 forks source link

Is distributing client-secret insecure in any way? #414

Open bjethwan opened 3 years ago

bjethwan commented 3 years ago

I enjoyed using this as krew plugin to access k8s. But I had a question if it's safe to share the client-secret as part of kubeconfig to all the dev in an org? Any known risk using this with OpenAM? How about we script the steps to bundle either kubeconfig or just the client-secret in the custom-built binary? I want opinions on this topic before I take up such a task

bjethwan commented 3 years ago

In fact that's one of the reasons why authorization grant is exchanged for access token of out user's browser using more secure back channel in case of oauth and oidc

int128 commented 3 years ago

My understanding is that the authorization server checks if the client (i.e. kubelogin) is valid using the Client ID and Secret. It prevents access from unauthorized clients such as outsite the organization. Therefore, I think it's no problem to share the Client ID and Secret with members in your organization.

The only concern is that kubelogin stores the Client Secret and token cache in clear text to disk. It would be nice to provide an option to store them to the keychain.

KlavsKlavsen commented 3 years ago

But what does this client secret grant access too ? you still need a google account (if using it against google oidc) within the org that the client-id secret belongs to right?

pavels commented 3 years ago

You need to look at OIDC/oauth from broader perspective (i will simplify things, in reality is is a bit more complicated) - remember this window on google for example (or facebook) "App XY wants your permission to access your personal information ..."? If you say "yes" you are granting the application - in term of OIDC this would be the "client" - right to get token issued with your credentials so the "client" can act on your behalf in any third party service that is configured to accept such credentials. It is completely up to the service what it will or will not allow you to do with the user token.

Now there are usually setting in the identity provider that says, if the client is required to ask approval from the user first (something resembling the permission dialog mentioned earlier), or is approved by default (no approval screen). Your kubernetes client will be typically set to skip the approval screen. Because of this, the client must authenticate with the identity provider so the identity provider knows if the user must approve the client first or if the client is of approved type - also other client specific settings is protected this way.

What is somebody gets the Client Secret? He can craft URL that will ask you for the credentials in your identity provider and than redirects you to attackers web server so he can get the token. In this case somebody possessing your Client Secret can try some phishing techniques and they will look very trustworthy as it is your own identity provider asking you your credentials. There is second security measure - allowed redirect URLs. If the attacker can't redirect to URL he controls, he can't get the token after AUTH. So if allowed redirect URLs for the client are set properly, leak of Client Secret is not that big of a problem. But if you set your redirect URL to be * (allow any) and your Client Secret leaks then you can fall victim for very nasty phishing attacks (even if your identity provider is not publicly accessible).

KlavsKlavsen commented 3 years ago

So it actually IS a risk storing client secret in .kube/config file (due to phishing).. Could client secret (as used for identity provider) be stored in kubernetes secrets - so ONLY kubelogin "app" in cluster - can access clientsecret (and use it when talking to identity provider) ? (but then it would need to talk with idp directly first - to exchange the client secret with some short-lived token - which could then be used, when redirecting user - to avoid client secret getting stolen there). It sounds as if OIDC protocol isn't well protected against phising?

KlavsKlavsen commented 3 years ago

According to https://developer.okta.com/blog/2019/10/21/illustrated-guide-to-oauth-and-oidc <- it seems you can use client secret to get an authorization token - which is exactly that shortlived key - which (hopefully?) is whats then used in the browser request the user sees ?

KlavsKlavsen commented 3 years ago

This diagram seems to indicate kubelogin already does this https://github.com/int128/kubelogin/pull/144/files - ONLY issue is that kubelogin MUST then run inside kubernetes - for it to have a secure way to access the client secret - without users being able to get access to it. I believe it IS possible to do this today? (with a caveat about not being able to redirect to browser).. If one then had kubelogin locally ALSO - kubelogin could get the token (gotten from idp with client secret) and do redirect - and things would be much better protected ?

pavels commented 3 years ago

OIDC is actually very well protected against phishing with multuple measures

client secret is one of them, but not the only one, the list of allowed redirect targets is another one

if your allowed redirect urls are set properly than unless attacker can get access to some system on on of these urls he can create the link but has no means to retrive the token afterwards - so set your allowed redirect urls correctly and you are good.

storing client secret in kubernetes cluster is chicken and egg problem - how do authenticate to cluster to access the secret if you need that secret to authenticate to cluster?

i think that if somebody is concerned with the client secrte really, he can probably encrypt his kubeconfig file and decrypt each time he wants to use it? in any case, trying to use kubernetes to secure thus secret is futile - the security needs to be local if somebody is that concerned

KlavsKlavsen commented 3 years ago

I would much prefer having the secret in 1 place only - inside the kubernetes cluster (as a secret in a namespace) - instead of having it available to everyone in the company (so they can bootstrap a new machine) - and having a copy of it on EVERY computer that needs to access the cluster. So I think you're misunderstanding me. It should be perfectly fine - if kubectl/kubelogin just talked a public service in the cluster (filtered if one wants by LB in front - that could do f.ex. client ip filtering) - and then only got a token (which is the short-lived token, you get from client secret).. Instead of everyone having the client secret, which as you said:

What is somebody gets the Client Secret? He can craft URL that will ask you for the credentials in your identity provider and than redirects you to attackers web server so he can get the token. In this case somebody possessing your Client Secret can try some phishing techniques and they will look very trustworthy as it is your own identity provider asking you your credentials.
There is second security measure - allowed redirect URLs. If the attacker can't redirect to URL he controls, he can't get the token after AUTH. So if allowed redirect URLs for the client are set properly, leak of Client Secret is not that big of a problem. But if you set your redirect URL to be * (allow any) and your Client Secret leaks then you can fall victim for very nasty phishing attacks (even if your identity provider is not publicly accessible).
KlavsKlavsen commented 3 years ago

or is the client secret being revealed not a problem - if the redirect url is just set to a specific url ? and what should that be for Google ? (setup guide shows examples for keycloak and the others)

marcbachmann commented 3 years ago

I just recently implemented an openid connect filter for envoy that takes care of the handshake. Such a service could allow having the client secret central in the kubernetes cluster. So in kubelogin, we would have to open a window, initiate the handshake and then maybe use postMessage or use an url to report back the refresh token. In the openid connect filter we just save the id_token in an https-only cookie.

shawnz commented 3 years ago

Would the new OAuth "device authorization grant" be useful here? It seems like it is exactly designed for this scenario and in fact doesn't use a client secret or redirect URL at all, only back channel communication.

OOTS commented 2 years ago

Hey, I was facing the same questions and did some research. From my understanding, there are two different types of OIDC clients: "confidential" clients (which use a client secret) and "public" clients (which do not have the ability to securely store the client secret).

In my mind, kubectl/kubelogin is an example of the latter one. The usual recommendation is to use PKCE for public clients to compensate for the lack of a client token.

gkgeorgiev commented 1 year ago

Hey, I was facing the same questions and did some research. From my understanding, there are two different types of OIDC clients: "confidential" clients (which use a client secret) and "public" clients (which do not have the ability to securely store the client secret).

In my mind, kubectl/kubelogin is an example of the latter one. The usual recommendation is to use PKCE for public clients to compensate for the lack of a client token.

I fully share the same opinion. And here's the proof https://oauth.net/2/client-types/ The kubectl users are the end-users. The one who needs a secret is the one doing the back-channel communication and this typically is the protected resource to get the access token renewed or validate an access token, etc.

Thx

Larswa commented 1 year ago

This is a really good discussion, and while sharing the secret may not be a perfect solution, compare it to passing kube config files around, or having to create certificates for each user involved. While its not perfect, I believe that this is infintely better than the immediate alternatives that I guess that mosts orgs are stuck at, at least at the beginning of their Kubernetes journey?

isihu commented 11 months ago

The documentation makes it seem that the client secret is absolutely necessary in the kubeconfig. But if your OIDC provider supports PKCE then it's not. I was able to just remove the client secret from the kubeconfig and it still worked.