-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
proxy protocol: new feature auto-detect proxy protocol #18951
Changes from 7 commits
278ce7e
121e87d
2d43ae3
de6b7f2
3a36695
409f94c
ae19e1f
ed4fb54
bcc7aef
8c87bcd
4aa520e
91c9a1b
c45ecc7
4259f1a
2b4a89a
f105c3d
6a0d49b
f47232f
fa9746a
a37ad43
6f19883
0927000
b5cbfc1
a7ef68f
9495853
ffb56d1
ac2982f
2a66a01
c9e086f
af6f88d
7f17660
59206da
b416242
4e4638a
354a365
86ad288
899f199
887d0bc
a23ac5f
6075b67
4d6f75f
2cb4526
1667461
a0c977f
85706e6
c86edef
6031151
a09bb1b
cfeda5a
5a660a0
e92a8a4
84cdc32
f3d65e9
b2d467e
45e9c24
056f74f
39e7b50
392124e
307df1c
5c50e23
36bbfa2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,4 +40,9 @@ message ProxyProtocol { | |
|
||
// The list of rules to apply to requests. | ||
repeated Rule rules = 1; | ||
|
||
// NOTICE: only enable if ALL traffic to the listener comes from a trusted source. | ||
// Defaults to false. If true, attempt to detect proxy protocol if present, and allow | ||
// requests through if proxy protocol is not used on the connection. | ||
bool detect_proxy_protocol = 2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel the major goal is allow the requests through if proxy protocol is not used. And actually the whole proxy protocol filter is about detect the proxy protocol, so this option name feels confuse. Should we call it something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 please clarify the name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that better as well, addressed in bcc7aef |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ Config::Config( | |
Stats::Scope& scope, | ||
const envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol& proto_config) | ||
: stats_{ALL_PROXY_PROTOCOL_STATS(POOL_COUNTER(scope))} { | ||
detect_proxy_protocol_ = proto_config.detect_proxy_protocol(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be set in the c'tor's member initialization list above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in c45ecc7 |
||
for (const auto& rule : proto_config.rules()) { | ||
tlv_types_[0xFF & rule.tlv_type()] = rule.on_tlv_present(); | ||
} | ||
|
@@ -63,6 +64,8 @@ const KeyValuePair* Config::isTlvTypeNeeded(uint8_t type) const { | |
|
||
size_t Config::numberOfNeededTlvTypes() const { return tlv_types_.size(); } | ||
|
||
bool Config::detectProxyProtocol() const { return detect_proxy_protocol_; } | ||
|
||
Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { | ||
ENVOY_LOG(debug, "proxy_protocol: new connection accepted"); | ||
Network::ConnectionSocket& socket = cb.socket(); | ||
|
@@ -77,11 +80,20 @@ Network::FilterStatus Filter::onAccept(Network::ListenerFilterCallbacks& cb) { | |
return Network::FilterStatus::StopIteration; | ||
} | ||
|
||
ReadOrParseState Filter::resetAndContinue(Network::IoHandle& io_handle) { | ||
// Release the file event so that we do not interfere with the connection read events. | ||
io_handle.resetFileEvents(); | ||
cb_->continueFilterChain(true); | ||
return ReadOrParseState::Done; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method doesn't need to return anything, and the returned status should probably be only at the end of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree, addressed in 2b4a89a |
||
} | ||
|
||
void Filter::onRead() { | ||
const ReadOrParseState read_state = onReadWorker(); | ||
if (read_state == ReadOrParseState::Error) { | ||
config_->stats_.downstream_cx_proxy_proto_error_.inc(); | ||
cb_->continueFilterChain(false); | ||
} else if (read_state == ReadOrParseState::SkipFilterError) { | ||
resetAndContinue(cb_->socket().ioHandle()); | ||
} | ||
} | ||
|
||
|
@@ -135,10 +147,7 @@ ReadOrParseState Filter::onReadWorker() { | |
proxy_protocol_header_.value().remote_address_); | ||
} | ||
|
||
// Release the file event so that we do not interfere with the connection read events. | ||
socket.ioHandle().resetFileEvents(); | ||
cb_->continueFilterChain(true); | ||
return ReadOrParseState::Done; | ||
return resetAndContinue(socket.ioHandle()); | ||
} | ||
|
||
absl::optional<size_t> Filter::lenV2Address(char* buf) { | ||
|
@@ -309,7 +318,7 @@ ReadOrParseState Filter::parseExtensions(Network::IoHandle& io_handle, uint8_t* | |
const auto recv_result = io_handle.recv(buf, to_read, 0); | ||
if (!recv_result.ok()) { | ||
if (recv_result.err_->getErrorCode() == Api::IoError::IoErrorCode::Again) { | ||
return ReadOrParseState::TryAgainLater; | ||
return ReadOrParseState::TryAgainLaterError; | ||
} | ||
ENVOY_LOG(debug, "failed to read proxy protocol (no bytes avail)"); | ||
return ReadOrParseState::Error; | ||
|
@@ -424,7 +433,7 @@ ReadOrParseState Filter::readProxyHeader(Network::IoHandle& io_handle) { | |
|
||
if (!result.ok()) { | ||
if (result.err_->getErrorCode() == Api::IoError::IoErrorCode::Again) { | ||
return ReadOrParseState::TryAgainLater; | ||
return ReadOrParseState::TryAgainLaterError; | ||
} | ||
ENVOY_LOG(debug, "failed to read proxy protocol (no bytes read)"); | ||
return ReadOrParseState::Error; | ||
|
@@ -434,6 +443,9 @@ ReadOrParseState Filter::readProxyHeader(Network::IoHandle& io_handle) { | |
if (nread < 1) { | ||
ENVOY_LOG(debug, "failed to read proxy protocol (no bytes read)"); | ||
return ReadOrParseState::Error; | ||
} else if (nread < PROXY_PROTO_V2_HEADER_LEN && config_.get()->detectProxyProtocol()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if the client send a partial v1 protocol header, I feel this will skip the process the v1 protocol header. it would be great to add an unitest for this case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for the feedback, addressed in ed4fb54 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. related #18951 (comment) |
||
ENVOY_LOG(debug, "need more bytes to detect proxy protocol header"); | ||
return ReadOrParseState::SkipFilterError; | ||
} | ||
|
||
if (buf_off_ + nread >= PROXY_PROTO_V2_HEADER_LEN) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,15 +60,22 @@ class Config : public Logger::Loggable<Logger::Id::filter> { | |
*/ | ||
size_t numberOfNeededTlvTypes() const; | ||
|
||
/** | ||
* Filter configuration that determines if we should pass-through failed proxy protocol | ||
* requests. Should only be configured to true for trusted downstreams. | ||
*/ | ||
bool detectProxyProtocol() const; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name here should also be clear (following the api field name change). |
||
|
||
private: | ||
absl::flat_hash_map<uint8_t, KeyValuePair> tlv_types_; | ||
bool detect_proxy_protocol_{}; | ||
}; | ||
|
||
using ConfigSharedPtr = std::shared_ptr<Config>; | ||
|
||
enum ProxyProtocolVersion { Unknown = -1, InProgress = -2, V1 = 1, V2 = 2 }; | ||
|
||
enum class ReadOrParseState { Done, TryAgainLater, Error }; | ||
enum class ReadOrParseState { Done, TryAgainLaterError, Error, SkipFilterError }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you rename I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. addressed in fa9746a |
||
|
||
/** | ||
* Implementation the PROXY Protocol listener filter | ||
|
@@ -98,7 +105,7 @@ class Filter : public Network::ListenerFilter, Logger::Loggable<Logger::Id::filt | |
/** | ||
* Helper function that attempts to read the proxy header | ||
* (delimited by \r\n if V1 format, or with length if V2) | ||
* @return bool true valid header, false if more data is needed or socket errors occurred. | ||
* @return ReadOrParseState Done if successfully parsed, or the error type. | ||
*/ | ||
ReadOrParseState readProxyHeader(Network::IoHandle& io_handle); | ||
|
||
|
@@ -118,6 +125,8 @@ class Filter : public Network::ListenerFilter, Logger::Loggable<Logger::Id::filt | |
bool parseV2Header(char* buf); | ||
absl::optional<size_t> lenV2Address(char* buf); | ||
|
||
ReadOrParseState resetAndContinue(Network::IoHandle& io_handle); | ||
|
||
Network::ListenerFilterCallbacks* cb_{}; | ||
|
||
// The offset in buf_ that has been fully read | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -222,6 +222,19 @@ TEST_P(ProxyProtocolTest, V1Basic) { | |
disconnect(); | ||
} | ||
|
||
TEST_P(ProxyProtocolTest, DetectNoProxyProtocol) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a comment on what the test is validating. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 4259f1a, please let me know if this doesn't clarify what might have been confusing |
||
|
||
envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config; | ||
proto_config.set_detect_proxy_protocol(true); | ||
connect(true, &proto_config); | ||
|
||
write("more data"); | ||
|
||
expectData("more data"); | ||
|
||
disconnect(); | ||
} | ||
|
||
TEST_P(ProxyProtocolTest, V1Minimal) { | ||
connect(); | ||
write("PROXY UNKNOWN\r\nmore data"); | ||
|
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.
Should probably in an
.. attention::
clause.See example.
I also suggest to rephrase the comment, starting with with it does, its default value, and then the notice part.
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.
Thanks for the example, addressed in 91c9a1b