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
Obtaining public key is enough to authenticate with SSO #12992
Comments
Yes that solves a number of issues. I'd like to implement that too, but need a solid chunk of time to understand it in-depth, implement, and test. Argo also might still need to issue its own tokens as a fallback in some cases (I'm not sure yet).
I need to take a closer look at the rest. If accurate, only needing the public key would certainly be problematic in a few ways. Although currently, as you stated, since Argo generates both in a k8s Secret there would be no new exploit route (would still be a vuln, but 0 impact vuln). Argo does validate the OAuth2 token before exchanging for its own token. Bypassing that and crafting your own tokens with the private key would be fair game for a token exchange (although now I'm wondering if capability/authZ-wise there's potentially a MITM possibility in and of itself there? 🤔). Btw, Argo does have a private security disclosure process. A bit late at this point though 😕 |
Sorry this is my first time reporting a security issue, so I hadn't thought about people misusing it. Feel free to take down the issue and keep it internally. |
For future reference, please use projects'
A little bit easier said than done, I believe this requires a GH support request 😕. With regard to the actual contents of the issue:
So your code uses the private key, not the public key. Could you elaborate why you think only the public key is necessary?
This token exchange does feel a little weak, but the only MITM is if someone has access to your Argo Server's private key, they can then potentially access everything that all users can access. That would normally require the same permissions as to modify the Argo Server's Deployment though, if not even more elevated (as it would require permissions to Secrets in Argo's namespace), so there's technically no privilege escalation route there. If you can modify the Server, you can turn off auth entirely |
So the key stored in the Kubernetes secret sso contains both a private and a public key. My point is, that if you extract the public key, that is sufficient to create a token. This means that if at some point the key is split into a private and a public key, and the public part is exposed there will be a security hole. My code example might be little misleading, due to my lack of Go skills. I use the "private key" in the example, which is the full key from Kubernetes which contains both the public and private key as mentioned above. The code then extracts the public part of the key: encrypter, err := jose.NewEncrypter(jose.A256GCM, jose.Recipient{Algorithm: jose.RSA_OAEP_256, Key: privateKey.Public()}, &jose.EncrypterOptions{Compression: jose.DEFLATE}) Note the part that extracts the public key: I did manage to manually extract the public part of the key, but as mentioned my Go skills are limited so I'm not sure how to pass that in directly. The above code is based on the code from the sso module in Argo Workflows. |
Oh I missed that detail (it requires scrolling to the right also). I've edited your initial sample to clarify that in the comments. I'll have to look into this more why it's accepting that 😕 |
Oh... the code straight up uses the public key to encrypt: argo-workflows/server/auth/sso/sso.go Line 182 in 5b7d147
That seems to date back to #4095 and seems to have been on purpose, as the tokens are opaque. I'm not sure if that's necessary, but that would explain usage of the public key; only the private key can decrypt it. Looking further, Argo having its own JWE apparently stems from #4035 / #3873 et al. Instead of implementing refresh tokens in that PR, it was decided to use a new JWE effectively as a token exchange. Although it's not entirely clear why that was decided. I might be missing something, but that would suggest that #12049 was the original approach and then was changed? |
Oh it doesn't generate both, it only generates the private key in the Secret. The public key is then derived from the private key if I'm understanding the code correctly. That's a bit better, technically the public key is not stored anywhere. This is effectively doing symmetric encryption but using asymmetric keys, which is confusing and odd. It would probably be more correct to just use symmetric encryption |
It seems that crypto/rsa generates a private key that also contains a public key: rsa source code. See line 113-127. As to your considerations about encryption options I'm not sure what the best option is. Just seems weird to me, that Argo WF issues its' own JWE tokens, instead of validating the SSO tokens from the provider. |
Just to be 100% explicit, as I wrote before, Argo does validate the provider tokens and then exchanges those for its own longer-lived encrypted/opaque tokens. (confusingly asymmetric as symmetric) Otherwise I agree that directly using the provider tokens would seem to make more sense (and have less indirection and therefore less room for error), and some SIG Security folks agreed (albeit only based on a short summary) during a meeting today as well. I'm not sure if you saw my comment from earlier today -- apparently Argo did that in the past and there was support & progress to improve that, but then it got replaced with the current JWE approach without much documented explanation. I'd like to make sure we have full context on that decision before changing that. Off-hand, I can imagine the JWE could be useful for some providers that don't support refresh tokens or have other limitations, but I would imagine that the vast majority are capable, and that Dex as an intermediary could mitigate various limitations too (if needed, similar to its current optional usage in Workflows). May check with some Intuit folks if they can find records of it etc |
Pre-requisites
:latest
image tag (i.e.quay.io/argoproj/workflow-controller:latest
) and can confirm the issue still exists on:latest
. If not, I have explained why, in detail, in my description below.What happened/what did you expect to happen?
I have SSO configured with Azure Entra ID. I have experimented a bit with the authentication, and it seems to me, that something is wrong in the SSO flow. Argo seems to discard all information in the token received from Azure before it issues its' own token. I have created some simple Go code to illustrate it. If I extract the
sso
secret created by Arog Workflows and run the code below, I can create a token which will successfully authenticate. The code only uses the public part of the sso key. Note that I put in an object ID from an Entra ID group which is allowed access through the Azure Enterprise App which Argo uses for authentication (putting 000-.. as an example). Such an ID is easily obtained by people inside an organization, as Entra object ID's are not secret .This means that obtaining the public key is enough to authenticate with Argo when SSO is enabled. I realize that the
sso
secret is not publicly accessible, however it seems to me that this should not be possible regardless. My knowledge of authentication is limited, but I would expect to see some of the properties from the Azure token contained in the Argo token. In that way Argo can verify that the client has actually authenticated with Azure beforehand.Found this issue which proposes a solution I would solve my issue: #12049
TLDR; Argo SSO doesn't encode anything from the Azure token into its' own token, thus use of public key is enough to authenticate.
Version
v3.4.11
Paste a small workflow that reproduces the issue. We must be able to run the workflow; don't enter a workflows that uses private images.
Logs from the workflow controller
Logs from in your workflow's wait container
The text was updated successfully, but these errors were encountered: