-
Notifications
You must be signed in to change notification settings - Fork 9.8k
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
Fouled Authentication when using Concurrency/Election by multiple clients #17502
Comments
It isn't clear what exactly issue you were running into.
What's the CN (Common Name) field in these certificates? FYI. https://etcd.io/docs/v3.5/op-guide/authentication/rbac/#using-tls-common-name |
Possibly the same issue as #12385 |
Not exactly, but the cause is somewhat similar This issue #14850 is probably manifestation of the same problem I'm talking about. I even found it fails with basic auth. I prepared docker image which demonstrates the problem so you may have ready "stand" for trying and researching it. Files will work outside of docker too I believe (or you can instead run your custom-build What the image does, briefly (direct instructions to run it are somewhere below):
How the bug happens: If we call elections function
The step 4 fails with "Permission denied" if step 2 is still waiting, which we achieve in launching it into background and adding small sleep before step 3. If sleep is incremented so that step 2 finishes before step 4 - there is no error. As I understand it happens because steps 2 and 4 call for Watch inside Campaign and it uses Watch-stream inside for which credentials are shared internally since it is still single connection (internally) so on the step-4 Watch tries to use credentials of User-1. Instructions to run Download the attached file: etcd-issue-17502.zip - it contains dockerfile to build the image, 3 scripts to build and run everything, proto-files for grpcurl. Unpack the file and launch Launch Launch P.S. Thanks in advance for researching this. Files are intentionally kept small so you hopefully can tweak them in any way you like to better understand the issue. |
@ahrtr Benjamin, please have a look at it when you have time - I marked it is not "security-related" because it is about auth misused in pretty narrow case - but on the second thought I'm unsure, whether this behavior could be harmfully exploited. |
Thanks @RodionGork for the detailed steps to reproduce the issue. ReasonWhen two sub/logic streams/watchers share the same physical gRPC stream, ServerStream.Context may return unexpected context, accordingly etcd may get the unexpected token. Specifically, etcd gets the gRPC stream context using gRPC's interface ServerStream.Context, see below, etcd/server/etcdserver/api/v3rpc/watch.go Line 236 in 9f8756b
Afterwards, etcd gets token from metadata included in the context, see below. Based on my test, when two sub/logic streams/watchers (the two wathcers are using two different tokens to communicate with the grpc/etcd server) share the same physical gRPC stream, etcd may get unexpected token. For example, etcdserver may get wather1's token when it's processing wather2's message. @dfawley is this expected behahaviour from gRPC perspective? Lines 1059 to 1073 in 9f8756b
Suggestion (best practice): it can resolve the issueUse etcd client sdk to communicate with etcdserver, and create a separate client for each user, so that the watchers for different users will not share the same gRPC stream. Please refer to #17532. Actually I don't recommend to use the interface defined in v3election.proto, it just added a layer to make the election easier to use. But if you read #17532, it's still super easy to implement it without using the interface in |
The v3election.proto was added in #7634. Although I don't recommend to use it as I mentioned above, it's useful for applications written using a language other than Golang. |
gRPC has no concept of stream sharing whatsoever. It's entirely contained within the application. The context used on the client to initiate the stream will have the metadata (set via https://pkg.go.dev/google.golang.org/grpc/metadata#NewOutgoingContext
|
Thanks @dfawley for the feedback.
@RodionGork The reason is that once 2 finishes before step 4, then etcd will create a new gRPC stream instead of reusing the existing gPRC stream when executing step 4. So it won't reuse the token for user1, accordingly no error. On top of my previous comment,
Line 322 in 9f8756b
Lines 1038 to 1043 in 9f8756b
Lines 335 to 339 in 9f8756b
|
No, it can't be exploited. I don't see any security issue. If you provide a wrong token, the request will be rejected for sure. The issue is the request may be still be rejected even you provide a valid token just as we discussed above. Note the issue can only be reproduced when (1) auth is enabled and (2) multiple users call Election.Campaign concurrently. It's a design issue to me. We have two directions to resolve this issue,
|
Proposed action for this issue:
References: |
Commenting here to ensure it is not forgotten that is likely v3lock also has the same issue. |
Hey @ahrtr - Discussed this issue during sig-etcd triage meeting. Can you please confirm where in the docs you would like to see this updated so we can assign this issue out to a contributor? |
I think api_concurrency_reference_v3/ might be the best place? |
If etcd is using the wrong token, it could as well be authenticated when it shouldn't, as it can be not authenticated when it should, no? |
Bug report criteria
What happened?
Briefly
When using Concurrency / Election functionality by several clients - it may happen that internal GRPC connection uses wrong authentication (wrong user) for request due to auth preserved for the connection (in the "context" of the serverWatchStream).
In our case, with TLS authentication, with different certificates.
Experiencing this on versions from 3.4.19 to 3.5.12 (recent).
UPD - please read my next comment which contains docker image to demonstrate the issue and explains steps. It happens both with authorization by certificates and with basic auth.
Details
Concurrency / Election functionality accepts GRPC requests (like Campaign or Observe) and then does several internal GRPC-requests to KV-storage. With GRPC v3 approach of "auth per connection" (rather than "per request) these internal requests may reuse existing authorization. Particularly this happens with serverWatchStream, e.g. here:
https://github.com/etcd-io/etcd/blob/main/server/etcdserver/api/v3rpc/watch.go#L236
the call boils down to
AuthInfoFromCtx
(inv3_server.go
, notstore.go
) from where it delegates toAuthInfoFromTLS
(instore.go
that time). And herepeer
is fetched from the context, but context is the field of the "watch stream" and may remain from some preceding call by very different client.https://github.com/etcd-io/etcd/blob/main/server/auth/store.go#L1012
This leads to sudden
Permission Denied
when app is experiencing load by several clients simultaneously, despite working fine if the interfering clients are shut down.What did you expect to happen?
Behavior should not differ if some unrelated clients are making unrelated requests simultaneously. There should be no sudden "permission denied" (as no permission changes happened).
How can we reproduce it (as minimally and precisely as possible)?
As it requires running etcd leader election with TLS authentication and several client, reproducing is hardly "minimal", but I hope you recognize the problem from the description owing to your better knowledge of code. If necessary though, please write back, I'll try to create docker file / image with everything necessary.
Anything else we need to know?
No response
Etcd version (please run commands below)
Etcd configuration (command line flags or environment variables)
(this seems irrelevant)
Etcd debug information (please run commands below, feel free to obfuscate the IP address or FQDN in the output)
(this seems irrelevant)
Relevant log output
(no specific logs besides manually added for debugging)
The text was updated successfully, but these errors were encountered: