Skip to content

Security authn via netty channel validator #95112

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
ed31653
WIP
albertzaharovits Apr 10, 2023
9aeb5ce
SecurityNetty4HttpServerTransportTests
albertzaharovits Apr 10, 2023
c91a3dd
test compilation fixes
albertzaharovits Apr 11, 2023
63c82a2
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 11, 2023
d9726ab
Renames
albertzaharovits Apr 11, 2023
fa45926
Authn new way
albertzaharovits Apr 11, 2023
f4ec4c6
Nits
albertzaharovits Apr 12, 2023
a6093e0
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 12, 2023
22eda88
Step 1
albertzaharovits Apr 12, 2023
751976f
Step 2
albertzaharovits Apr 12, 2023
1b33bce
Remove ActionType
albertzaharovits Apr 12, 2023
fec44e9
Fix SecurityRestFilterWarningHeadersTests
albertzaharovits Apr 12, 2023
a0e644a
Merge branch 'simplify-security-error-response' into security-early-a…
albertzaharovits Apr 12, 2023
322f4e9
Remove AuthenticationService from SecurityRestFilter
albertzaharovits Apr 12, 2023
6cee039
Fix error response code
albertzaharovits Apr 12, 2023
7ae2a7f
Fix LoggingAuditTrailTests
albertzaharovits Apr 12, 2023
e2dc713
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 12, 2023
0c8a5d8
Fix/remove tests from SecurityRestFilterTests
albertzaharovits Apr 12, 2023
3324f8d
introduce HttpHeadersValidationException
albertzaharovits Apr 13, 2023
b2092cd
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 13, 2023
657ccdb
Rename nit
albertzaharovits Apr 13, 2023
1ecda85
Register the exception
albertzaharovits Apr 13, 2023
d60e079
RestResponse from unwrapped request
albertzaharovits Apr 13, 2023
0875625
Nit, remove populatePerRequestThreadContext
albertzaharovits Apr 13, 2023
7f62690
authenticateMessage refactoring
albertzaharovits Apr 13, 2023
02d0e7c
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 16, 2023
7ab0586
Comments
albertzaharovits Apr 16, 2023
65665fb
RemoteHostHeader
albertzaharovits Apr 16, 2023
5e82354
NOOP validator rename
albertzaharovits Apr 16, 2023
887b9a1
Some javadocs
albertzaharovits Apr 16, 2023
7ce4182
Nits
albertzaharovits Apr 17, 2023
6dc4d9d
Spotless nit
albertzaharovits Apr 17, 2023
ffd158b
Some Netty4HttpServerTransportTests
albertzaharovits Apr 17, 2023
78e4dd8
RestControllerTests
albertzaharovits Apr 17, 2023
7e5e59f
HttpHeadersValidatorTests
albertzaharovits Apr 17, 2023
01a693c
nit
albertzaharovits Apr 17, 2023
cfc2351
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 17, 2023
78d87ed
Javadocs
albertzaharovits Apr 17, 2023
69621ef
Netty4HttpServerTransportTests with populatePerRequestThreadContext
albertzaharovits Apr 17, 2023
99adc9b
Update docs/changelog/95112.yaml
albertzaharovits Apr 17, 2023
5638376
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 18, 2023
5629965
Move VALIDATE_EVERYTHING_VALIDATOR
albertzaharovits Apr 18, 2023
fe7a775
Drop "Context" from "ValidationResultContext"
albertzaharovits Apr 18, 2023
367c24a
Spotless
albertzaharovits Apr 18, 2023
2586e17
More comments
albertzaharovits Apr 18, 2023
f67f4a6
testValidationErrors
albertzaharovits Apr 19, 2023
3e86621
Remove TriConsumer
albertzaharovits Apr 20, 2023
b0c8f17
static HttpHeadersUtils
albertzaharovits Apr 20, 2023
8564168
Remove ValidationResult
albertzaharovits Apr 20, 2023
ae451ed
Rename ValidatableHttpHeaders to HttpHeadersWithValidationContext
albertzaharovits Apr 20, 2023
be70664
Rename markAsSuccessfullyValidated to addValidationContext
albertzaharovits Apr 20, 2023
50ce1ed
Nit
albertzaharovits Apr 20, 2023
10b38b7
Nit, remove NOOP_VALIDATOR
albertzaharovits Apr 20, 2023
41cd269
move authentication==null logging
albertzaharovits Apr 21, 2023
b847e39
Remove stash before authentication
albertzaharovits Apr 21, 2023
e6d7428
Revert "Remove stash before authentication"
albertzaharovits Apr 21, 2023
32a5331
Remove stash with newStoredContext
albertzaharovits Apr 21, 2023
e7e15fd
Move "newStoredContext" and "contextPreservingListener" to
albertzaharovits Apr 21, 2023
22d90c3
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 21, 2023
a1eadee
Spotless
albertzaharovits Apr 21, 2023
509fc1b
Nit
albertzaharovits Apr 21, 2023
6e86d20
Leave out context from validation
albertzaharovits Apr 23, 2023
9c76679
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 23, 2023
32e97d1
Nit
albertzaharovits Apr 23, 2023
1a6f85d
Netty4HttpHeaderThreadContextTests
albertzaharovits Apr 24, 2023
bdd31cc
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 24, 2023
ae3e332
There can be response headers on the transport thread context
albertzaharovits Apr 24, 2023
1053dce
Remove if != default context in authentication
albertzaharovits Apr 24, 2023
57b6585
Spotless
albertzaharovits Apr 24, 2023
625e56c
Rename some validation to authentication
albertzaharovits Apr 24, 2023
3b6e734
validation -> authentication rename fallout in tests
albertzaharovits Apr 24, 2023
b6b6383
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 25, 2023
5f7dae8
Fix merge fallout
albertzaharovits Apr 25, 2023
14be805
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits Apr 28, 2023
aa6ae60
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits May 2, 2023
d2b363d
validator for Netty4HttpHeaderValidator in internal package
albertzaharovits May 2, 2023
c7eeba1
Authenticator in separate package
albertzaharovits May 2, 2023
b411dbe
Standalone HttpHeadersWithAuthenticationContext in http.netty4.authen…
albertzaharovits May 2, 2023
31be670
Nit
albertzaharovits May 2, 2023
7de6f79
Nit
albertzaharovits May 2, 2023
429def6
8.9
albertzaharovits May 2, 2023
4181d79
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits May 2, 2023
df8392d
Response stack trace tests
albertzaharovits May 2, 2023
2197964
testResponseHeadersFiltering
albertzaharovits May 2, 2023
d170c6b
Minor refactoring of Netty4HttpServerTransportTests
albertzaharovits May 3, 2023
ffa4f24
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits May 3, 2023
920f1fc
Nit
albertzaharovits May 3, 2023
815e523
testMultipleValidationsOnTheSameChannel
albertzaharovits May 3, 2023
b524b7c
IMPEX
albertzaharovits May 3, 2023
2b73b71
Replace Supplier<Netty4HttpHeaderValidator> with HttpValidator
albertzaharovits May 3, 2023
d143282
testAuthnContextWrapping
albertzaharovits May 4, 2023
e8da155
Merge branch 'main' into security-early-authn-with-header-validator
albertzaharovits May 4, 2023
0d03f6e
Nits
albertzaharovits May 4, 2023
36d1a88
only assert don't log null authentication
albertzaharovits May 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/95112.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 95112
summary: Header validator with Security
area: Authentication
type: enhancement
issues: []
1 change: 1 addition & 0 deletions modules/transport-netty4/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@

exports org.elasticsearch.http.netty4;
exports org.elasticsearch.transport.netty4;
exports org.elasticsearch.http.netty4.internal to org.elasticsearch.security;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
package org.elasticsearch.http.netty4;

import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.DecoderResult;
Expand All @@ -21,8 +20,8 @@

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ContextPreservingActionListener;
import org.elasticsearch.common.TriConsumer;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.http.netty4.internal.HttpValidator;
import org.elasticsearch.transport.Transports;

import java.util.ArrayDeque;
Expand All @@ -35,17 +34,12 @@

public class Netty4HttpHeaderValidator extends ChannelInboundHandlerAdapter {

public static final TriConsumer<HttpRequest, Channel, ActionListener<Void>> NOOP_VALIDATOR = ((
httpRequest,
channel,
listener) -> listener.onResponse(null));

private final TriConsumer<HttpRequest, Channel, ActionListener<Void>> validator;
private final HttpValidator validator;
private final ThreadContext threadContext;
private ArrayDeque<HttpObject> pending = new ArrayDeque<>(4);
private State state = WAITING_TO_START;

public Netty4HttpHeaderValidator(TriConsumer<HttpRequest, Channel, ActionListener<Void>> validator, ThreadContext threadContext) {
public Netty4HttpHeaderValidator(HttpValidator validator, ThreadContext threadContext) {
this.validator = validator;
this.threadContext = threadContext;
}
Expand Down Expand Up @@ -129,7 +123,7 @@ private void requestStart(ChannelHandlerContext ctx) {
);
// this prevents thread-context changes to propagate beyond the validation, as netty worker threads are reused
try (ThreadContext.StoredContext ignore = threadContext.newStoredContext()) {
validator.apply(httpRequest, ctx.channel(), contextPreservingActionListener);
validator.validate(httpRequest, ctx.channel(), contextPreservingActionListener);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
Expand Down Expand Up @@ -41,59 +40,35 @@ public class Netty4HttpRequest implements HttpRequest {

private final FullHttpRequest request;
private final BytesReference content;
private final HttpHeadersMap headers;
private final Map<String, List<String>> headers;
private final AtomicBoolean released;
private final Exception inboundException;
private final boolean pooled;

private final int sequence;

Netty4HttpRequest(int sequence, FullHttpRequest request) {
this(
sequence,
request,
new HttpHeadersMap(request.headers()),
new AtomicBoolean(false),
true,
Netty4Utils.toBytesReference(request.content())
);
this(sequence, request, new AtomicBoolean(false), true, Netty4Utils.toBytesReference(request.content()));
}

Netty4HttpRequest(int sequence, FullHttpRequest request, Exception inboundException) {
this(
sequence,
request,
new HttpHeadersMap(request.headers()),
new AtomicBoolean(false),
true,
Netty4Utils.toBytesReference(request.content()),
inboundException
);
this(sequence, request, new AtomicBoolean(false), true, Netty4Utils.toBytesReference(request.content()), inboundException);
}

private Netty4HttpRequest(
int sequence,
FullHttpRequest request,
HttpHeadersMap headers,
AtomicBoolean released,
boolean pooled,
BytesReference content
) {
this(sequence, request, headers, released, pooled, content, null);
private Netty4HttpRequest(int sequence, FullHttpRequest request, AtomicBoolean released, boolean pooled, BytesReference content) {
this(sequence, request, released, pooled, content, null);
}

private Netty4HttpRequest(
int sequence,
FullHttpRequest request,
HttpHeadersMap headers,
AtomicBoolean released,
boolean pooled,
BytesReference content,
Exception inboundException
) {
this.sequence = sequence;
this.request = request;
this.headers = headers;
this.headers = getHttpHeadersAsMap(request.headers());
this.content = content;
this.pooled = pooled;
this.released = released;
Expand All @@ -102,36 +77,7 @@ private Netty4HttpRequest(

@Override
public RestRequest.Method method() {
HttpMethod httpMethod = request.method();
if (httpMethod == HttpMethod.GET) return RestRequest.Method.GET;

if (httpMethod == HttpMethod.POST) return RestRequest.Method.POST;

if (httpMethod == HttpMethod.PUT) return RestRequest.Method.PUT;

if (httpMethod == HttpMethod.DELETE) return RestRequest.Method.DELETE;

if (httpMethod == HttpMethod.HEAD) {
return RestRequest.Method.HEAD;
}

if (httpMethod == HttpMethod.OPTIONS) {
return RestRequest.Method.OPTIONS;
}

if (httpMethod == HttpMethod.PATCH) {
return RestRequest.Method.PATCH;
}

if (httpMethod == HttpMethod.TRACE) {
return RestRequest.Method.TRACE;
}

if (httpMethod == HttpMethod.CONNECT) {
return RestRequest.Method.CONNECT;
}

throw new IllegalArgumentException("Unexpected http method: " + httpMethod);
return translateRequestMethod(request.method());
}

@Override
Expand Down Expand Up @@ -170,7 +116,6 @@ public HttpRequest releaseAndCopy() {
request.headers(),
request.trailingHeaders()
),
headers,
new AtomicBoolean(false),
false,
Netty4Utils.toBytesReference(copiedContent)
Expand Down Expand Up @@ -210,28 +155,19 @@ public HttpVersion protocolVersion() {

@Override
public HttpRequest removeHeader(String header) {
HttpHeaders headersWithoutContentTypeHeader = new DefaultHttpHeaders();
headersWithoutContentTypeHeader.add(request.headers());
headersWithoutContentTypeHeader.remove(header);
HttpHeaders trailingHeaders = new DefaultHttpHeaders();
trailingHeaders.add(request.trailingHeaders());
trailingHeaders.remove(header);
HttpHeaders copiedHeadersWithout = request.headers().copy();
copiedHeadersWithout.remove(header);
HttpHeaders copiedTrailingHeadersWithout = request.trailingHeaders().copy();
copiedTrailingHeadersWithout.remove(header);
FullHttpRequest requestWithoutHeader = new DefaultFullHttpRequest(
request.protocolVersion(),
request.method(),
request.uri(),
request.content(),
headersWithoutContentTypeHeader,
trailingHeaders
);
return new Netty4HttpRequest(
sequence,
requestWithoutHeader,
new HttpHeadersMap(requestWithoutHeader.headers()),
released,
pooled,
content
copiedHeadersWithout,
copiedTrailingHeadersWithout
);
return new Netty4HttpRequest(sequence, requestWithoutHeader, released, pooled, content);
}

@Override
Expand All @@ -249,6 +185,46 @@ public Exception getInboundException() {
return inboundException;
}

public io.netty.handler.codec.http.HttpRequest getNettyRequest() {
return request;
}

public static RestRequest.Method translateRequestMethod(HttpMethod httpMethod) {
if (httpMethod == HttpMethod.GET) return RestRequest.Method.GET;

if (httpMethod == HttpMethod.POST) return RestRequest.Method.POST;

if (httpMethod == HttpMethod.PUT) return RestRequest.Method.PUT;

if (httpMethod == HttpMethod.DELETE) return RestRequest.Method.DELETE;

if (httpMethod == HttpMethod.HEAD) {
return RestRequest.Method.HEAD;
}

if (httpMethod == HttpMethod.OPTIONS) {
return RestRequest.Method.OPTIONS;
}

if (httpMethod == HttpMethod.PATCH) {
return RestRequest.Method.PATCH;
}

if (httpMethod == HttpMethod.TRACE) {
return RestRequest.Method.TRACE;
}

if (httpMethod == HttpMethod.CONNECT) {
return RestRequest.Method.CONNECT;
}

throw new IllegalArgumentException("Unexpected http method: " + httpMethod);
}

public static Map<String, List<String>> getHttpHeadersAsMap(HttpHeaders httpHeaders) {
return new HttpHeadersMap(httpHeaders);
}

/**
* A wrapper of {@link HttpHeaders} that implements a map to prevent copying unnecessarily. This class does not support modifications
* and due to the underlying implementation, it performs case insensitive lookups of key to values.
Expand Down
Loading