-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Netty4 HTTP authn enhancements (#92220) #96703
Netty4 HTTP authn enhancements (#92220) #96703
Conversation
This introduces a way to validate HTTP headers prior to reading the request body. Co-authored-by: Albert Zaharovits <albert.zaharovits@elastic.co>
e20b604
to
1009ac5
Compare
Authentication for the HTTP channel is changing to not have access to the contents of the HTTP request body. But auditing of authentication success still requires access to the request body. Consequently, auditing of authentication success is now separated from the authentication logic: auditing is invoked in the SecurityRestFilter#handleRequest, after the AuthenticationService has done its job earlier in the handling flow for the HTTP request.
…lastic#94959) In preparation for when the audit trail will not be able to use the request interface to extract the remote endpoint address of rest requests, this PR makes the LoggingAuditTrail to look into the thread context for the remote address, and the SecurityRestFilter to populate as such the thread context before invoking the authentication.
@elasticmachine run elasticsearch-ci/part-2 |
8e41efc
to
0aa3df6
Compare
…uests (elastic#94990) AuthN and Audit are NOT going to have access to the Rest interface anymore, because the AuthenticationService is going to be invoked before the RestRequest is built. This refactoring makes the AuthenticationService and the AuditTrail take as parameters HTTP-related request interfaces.
…xt supplier (elastic#95040) When `Security` authenticates a request (AuthenticationService#authenticate) it mostly looks in the thread context for all the request details that it needs. Consequently, there's code that populates the thread context before invoking authentication. This PR brings all the code that's responsible for populating the said thread context, as a parameter to the `Security` plugin. This is required in the follow-up that is going to involve setting up the thread context (given the aforementioned parameter), and running the authentication under that context, before the request is dispatched (rather than "while" it's being dispatched, currently).
0aa3df6
to
7c956e9
Compare
…95189) This ensures that Warning and X-elastic-product response headers are only removed in case of 401 or 403 HTTP error response codes. Previously, the response headers were also removed in non 401 and 403 conditions, in case of esoteric errors during authN, but this behavior displaced the response-filtering logic to an uncomfortable code site from a reusability pov.
…ic#95498) This ensures that the validator (of http requests over the netty channel) is unable to alter the ES context of netty worker threads. The netty worker threads are oblivious to the ES context, but they eventually call into the ES code, at which point any tampering of the context during the validation is visible and potentially dangerous.
b1a1499
to
f61af24
Compare
Hooks "REST" authN, as a "validator", into the new netty channel interceptor for http headers.
7dc2d2a
to
454af7e
Compare
This address HTTP OPTIONS requests following the authentication refactoring in elastic#95112. Relates elastic#95112
Instead of not authN and letting them through, this PR rejects OPTIONS requests with a body (400). Relates elastic#95112
454af7e
to
f76d8a0
Compare
This PR tests that malformed HTTP requests that fail at the decoding stage don't go through validation and are further dispatched as bad requests. Related: elastic#95112
Skips SecurityNetty4HttpServerTransportTests testMalformedRequestDispatchedNoAuthn under turkish-like locales because the asserted messages are internally processed with String#toUpper(). This results in different error messages under such locales, and it's easier to skip the test entirely than to assert the different messages. Closes: elastic#95983
Fixes SecurityNetty4HttpServerTransportTests testAuthnContextWrapping The mocking removed code that called release on request objects. Closes elastic#95846
In patological cases it might be possible that validation in invoked on requests that are not primed correctly (wrapped with HttpHeadersAuthenticatorUtils#wrapAsMessageWithAuthenticationContext) during HTTP decoding. This test asserts that such occurences are a case of 500 error.
Large requests, that exceed the http.max_content_length, are never dispatched, even if the HTTP header validation fails and the request should otherwise be dispatched as a "bad" one.
org.elasticsearch.http.HttpHeadersValidationException.class, | ||
org.elasticsearch.http.HttpHeadersValidationException::new, | ||
162, | ||
Version.V_7_17_11 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder to fix this with the version that this PR actually lands in.
Also, I need to figure out what this (transport) version needs to be since the HttpHeadersValidationException
was also merged into 8.9 (but with a different id
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed 140f323 to make the HttpHeadersValidationException
not extend ElasticsearchException
such that it doesn't have to be registered for serialization. This is possible because HttpHeadersValidationException
s are never serialized across nodes (they are exclusively used to "communicate" between the validating channel handler and the RestController
).
(I turns out backporting new ElasticsearchException
types to 7.17 is not easily done).
I've pushed 74bced2 to reinstate the tests for authn failure in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@albertzaharovits - nice job on this back port ! I have spent time reviewing commit by commit (thanks for keeping that as an option) paying special attention to anything specific to 7.x. While reviewing the tests, i mostly looked at changes to existing tests that could indicate behavior changes (didn't find any) and didn't pay much attention to net-new tests (I assume those generally copy pasted). I then diffed many of the key files to their 8.x counterpart looking for any differences not explained by unrelated commits. I only found a couple minor questions... please let me know if there are specific areas that may not be covered by my approach (or anything you just a second set of eyes on).
filter = new SecurityRestFilter(licenseState, threadContext, authcService, secondaryAuthenticator, restHandler, false); | ||
}).when(auditTrail).authenticationSuccess(any(RestRequest.class)); | ||
TestUtils.UpdatableLicenseState licenseState = new TestUtils.UpdatableLicenseState(); | ||
licenseState.update(License.OperationMode.GOLD, true, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to update the licenseState ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, TestUtils.UpdatableLicenseState licenseState = new TestUtils.UpdatableLicenseState()
gives you a null
license.
ActionListener.wrap(ignored -> listener.onResponse(null), listener::onFailure) | ||
); | ||
} else { | ||
listener.onResponse(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you assert the expected request type and comment that authentication for non-nio requests is handled elsewhere. (i totally read this wrong the first time)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to implement some convoluted get-arounds to address this, because besides Netty4HttpRequest
s, in tests, there are also FakeHttpRequests
inbound.
} | ||
|
||
private static HttpHeadersWithAuthenticationContext unwrapAuthenticatedHeaders(org.elasticsearch.http.HttpRequest request) { | ||
if (request instanceof HttpPipelinedRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you help me understand why we don't need to do this in 8.x ? https://github.com/elastic/elasticsearch/blob/main/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/internal/HttpHeadersAuthenticatorUtils.java#L110
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In 7.17 there is an extra netty pipeline handler
Line 336 in fe18a67
ch.pipeline().addLast("pipelining", new Netty4HttpPipeliningHandler(logger, transport.pipeliningMaxEvents)); |
HttpPipelinedRequest
type Line 47 in fe18a67
HttpPipelinedRequest pipelinedRequest = aggregator.read(((Netty4HttpRequest) msg)); |
In main, there's no wrapping. The pipelining (HTTP proto) and request dispatch happen in the same channel handler
Line 114 in 991b06e
netty4HttpRequest = new Netty4HttpRequest(readSequence++, fullHttpRequest, nonError); |
Hence the code in the HttpHeadersAuthenticatorUtils
has to peel away one extra layer in 7.17 to get to the type that contains the authentication.
There's nothing that I can think of. |
Thanks for the follow ups... LGTM |
This is a backport of multiple work items related to authentication enhancements for HTTP,
which were originally merged in the 8.8 - 8.9 releases.
Hence, the HTTP (only the netty4-based implementation (default), not the NIO one) authentication
implementation gets a throughput boost (especially for requests failing authn).
Relates to: ES-6188 #92220 #95112