Skip to content
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

ip-tagging filter: add support for an optional ip-tag-header field #36434

Merged

Conversation

Radha13
Copy link
Contributor

@Radha13 Radha13 commented Oct 3, 2024

Description

This change adds support for specifying an optional header to ip-tagging filter instead of the default header that it uses (x-envoy-ip-tags).

example

   http_filters
     - name: ip.tagging
       typed_config:
         "@type": type.googleapis.com/envoy.extensions.filters.http.ip_tagging.v3.IPTagging
         request_type: BOTH
         ip_tag_header: "x-slack-foo-bar"
         ip_tags: []

Why

Currently, the ip-tagging filter always writes its output into the x-envoy-ip-tags header. When this filter is used for more than one purpose in the same filter chain which we do at Slack in our production environment, we do need to take care of cleaning up the header in between which has been proven a bit tricky sometimes. Leaking the values in between the filter chain is bad so we try to avoid that.

We would like this to be configurable. This way we can use the optional header instead of trying the use the same header however many times the same filter is used on the same filter chain in a listener.

Risk Level

this is a new feature, doesn't affect the existing functionality so guessing low but open to changing if I'm wrong.

Testing

added test

Docs Changes: Added, alongside release notes

Co-authored-by: Ariane van der Steldt [email protected]
Signed-off-by: Radha Kumari [email protected]

Copy link

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to (api/envoy/|docs/root/api-docs/).
envoyproxy/api-shepherds assignee is @adisuissa
CC @envoyproxy/api-watchers: FYI only for changes made to (api/envoy/|docs/root/api-docs/).

🐱

Caused by: #36434 was opened by Radha13.

see: more, trace.

@Radha13 Radha13 changed the title ip-tagging filter: add support for an optional ip-tagging-header field ip-tagging filter: add support for an optional ip-tag-header field Oct 3, 2024
Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
Overall LGTM, left a few minor comments.
Please also add a release note as this is a user-facing change.

@@ -71,6 +73,7 @@ class IpTaggingFilterConfig {
const Stats::StatName total_;
const Stats::StatName unknown_tag_;
std::unique_ptr<Network::LcTrie::LcTrie<std::string>> trie_;
const Http::LowerCaseString header_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename to alt_header_ or something ip_tag_header_ to make it more readable.

@@ -39,6 +39,8 @@ class IpTaggingFilterConfig {
FilterRequestType requestType() const { return request_type_; }
const Network::LcTrie::LcTrie<std::string>& trie() const { return *trie_; }

const Http::LowerCaseString& ip_tag_header() const { return header_; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment here, stating that empty string implies that no ip_tag_header is set.

I do wonder if it just makes more sense to return the correct ip_tag_header here (either the default or the alternative). On the one hand I think it may make the code a bit more readable, but then the calls to the headers.append will need to be a bit different.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I added the conditional in decodeHeaders as first, it was easier and second the decision on which header to use is made when we are appending the tags but I'm open to returning the correct header from ip_tag_header(). I will have to see how involved that change is but let me know if that's what you prefer.

Radha13 and others added 2 commits October 8, 2024 11:32
@Radha13
Copy link
Contributor Author

Radha13 commented Oct 8, 2024

Almost forgot, don't really know how to add release notes but will check the docs 🙇🏽‍♀️

Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
A release note should be added here: https://github.com/envoyproxy/envoy/blob/main/changelogs/current.yaml

Assigning @alyssawilk for code-owner review
/assign @alyssawilk

@alyssawilk
Copy link
Contributor

weird, that assign didn't work.
@JuniorHsu can you do first pass?

Signed-off-by: Radha Kumari <[email protected]>
@JuniorHsu
Copy link
Contributor

ci fails

CodeChecker ERROR [yamllint] changelogs/current.yaml (line-length 484:141): line too long (152 > 140 characters)
CodeChecker ERROR [yamllint] Check failed

Signed-off-by: Radha Kumari <[email protected]>
@JuniorHsu
Copy link
Contributor

i'll review after ci is happy but this looks like a replacement of #35628
and ideally we want to modify docs/root/configuration/http/http_filters/ip_tagging_filter.rst since the behavior might be changed by your pr

headers.appendEnvoyIpTags(tags_join, ",");

// Use the optional header instead of x-envoy-ip-tags if it's not empty.
if (!config_->ip_tag_header().get().empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to sanitize this header so others can not spoof it. When you configure ip_tag_header as x-foo-tags, and the requests come in with x-foo-tags header, if the requests does not get any tag from envoy ip tagging filter, they still have x-foo-tags header and the value can be anything.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least we want to default to sanitize otherwise we need to set security level to untrusted downstream. I didn't check metadata tho.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuniorHsu @cqi1217 I think @nahratzah meant to post #36434 (comment) here. let me know what you folks think about this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alyssawilk we hit a dilemma here.
currently ip_tagging is robust to untrusted downstream

security_posture: robust_to_untrusted_downstream

however my understanding is non x- prefixed header won't be sanitized by envoy, which is vulnerable, so appending header could downgrade the security_posture.

However, @nahratzah mention the asymmetric behavior in #36434 (comment)

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it'd help if I had clarity around why the user wants a custom header. was it to bypass the x-envoy checks? If it's just "we want a custom header" we could require an x-envoy prefix. If they want a non-x-envoy header I agree this would be changing the security model and either that needs to be fixed, or (if intentioanl) needs a lot more documentation

@Radha13
Copy link
Contributor Author

Radha13 commented Oct 9, 2024

@JuniorHsu Apologies but the CI failures looks to be Java exceptions and looks unrelated to the change unless I'm mistaken.

Error: ERROR: /source/WORKSPACE:17:19: fetching http_archive rule //external:com_googlesource_googleurl: Traceback (most recent call last):
Error: ERROR: @com_googlesource_googleurl//build_config:system_icu :: Error loading option @com_googlesource_googleurl//build_config:system_icu: java.io.IOException: Error downloading [https://storage.googleapis.com/quiche-envoy-integration/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz] to /build/bazel_root/base/external/com_googlesource_googleurl/temp14106256169062325536/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz: GET returned 403 Forbidden
Error: ERROR: An error occurred during the fetch of repository 'com_googlesource_googleurl':
Warning: WARNING: Download from https://storage.googleapis.com/quiche-envoy-integration/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz failed: class java.io.IOException GET returned 403 Forbidden

Also, wasn't really aware of #35628 before you mentioned. Looks like it was closed due to inactivity. Let me make the change to docs.

@adisuissa
Copy link
Contributor

@JuniorHsu Apologies but the CI failures looks to be Java exceptions and looks unrelated to the change unless I'm mistaken.

Error: ERROR: /source/WORKSPACE:17:19: fetching http_archive rule //external:com_googlesource_googleurl: Traceback (most recent call last):
Error: ERROR: @com_googlesource_googleurl//build_config:system_icu :: Error loading option @com_googlesource_googleurl//build_config:system_icu: java.io.IOException: Error downloading [https://storage.googleapis.com/quiche-envoy-integration/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz] to /build/bazel_root/base/external/com_googlesource_googleurl/temp14106256169062325536/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz: GET returned 403 Forbidden
Error: ERROR: An error occurred during the fetch of repository 'com_googlesource_googleurl':
Warning: WARNING: Download from https://storage.googleapis.com/quiche-envoy-integration/dd4080fec0b443296c0ed0036e1e776df8813aa7.tar.gz failed: class java.io.IOException GET returned 403 Forbidden

Also, wasn't really aware of #35628 before you mentioned. Looks like it was closed due to inactivity. Let me make the change to docs.

Please try to merge latest main. There's ongoing work migrating the CI infra in the last couple of weeks.

@nahratzah
Copy link
Contributor

Test failure appears unrelated, and to be due to a connection-timed-out while downloading `kafka_server_binary`.
ERROR: An error occurred during the fetch of repository 'kafka_server_binary'
Error in download_and_extract: java.io.IOException: Error downloading [https://downloads.apache.org/kafka/3.8.0/kafka_2.13-3.8.0.tgz] to /build/bazel_root/base/external/kafka_server_binary/temp1195429097495657859/kafka_2.13-3.8.0.tgz: connect timed out

OptRef<const Http::LowerCaseString> header_name = config_->ip_tag_header();

if (tags.empty()) {
bool mustSanitize =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't really mustSanitize given we'll only do it if there's a header name configured. Also snake case for Envoy variables
maybe_sanitize?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I wish there was a linter for this.) <3

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good - just a few more nits and you're good to go
/wait

@@ -39,6 +42,14 @@ class IpTaggingFilterConfig {
FilterRequestType requestType() const { return request_type_; }
const Network::LcTrie::LcTrie<std::string>& trie() const { return *trie_; }

OptRef<const Http::LowerCaseString> ip_tag_header() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and for functions ipTagHeader and ipTagHeaderAction

// (DEFAULT) The header specified in :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`
// will be dropped, before the tags are applied. Sanitize will be run regardless of if the source is a trusted IP or not.
//
// Note that the header will be visible unsanitized to any filters downstream of the ip-tag-header filter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, mind adding
unless it has an x-envoy prefix

initializeFilter(internal_request_yaml);

Http::TestRequestHeaderMapImpl request_headers{
{"x-envoy-internal", "true"}, {"x-envoy-optional-header", "foo"}}; // foo will be removed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind adding multiple just so we regression test that rmove does remove all?

Signed-off-by: Ariane van der Steldt <[email protected]>
Ariane van der Steldt added 3 commits November 4, 2024 16:09
Signed-off-by: Ariane van der Steldt <[email protected]>
Signed-off-by: Ariane van der Steldt <[email protected]>
@nahratzah
Copy link
Contributor

Updated the PR. <3

alyssawilk
alyssawilk previously approved these changes Nov 4, 2024
Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wohoo!

@alyssawilk
Copy link
Contributor

@adisuissa any final shepherd comments?

// Control if the header in :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`
// will be sanitized, or be appended to.
//
// This is ignored if :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>` is empty.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this field is closely related to the ip_tag_header field, it will be better to have them both under the same message type, and make the ip_tag_header field in that message type required (and probably move the HeaderAction to that inner message as well).

// Describes how to apply the tags to the headers.
enum HeaderAction {
// (DEFAULT) The header specified in :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`
// will be dropped, before the tags are applied. Sanitize will be run regardless of if the source is a trusted IP or not.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// will be dropped, before the tags are applied. Sanitize will be run regardless of if the source is a trusted IP or not.
// will be dropped, before the tags are applied. The incoming header will be "sanitized" regardless of whether the request is an internal or external.

I'm trying to stick to the same terminology introduced in https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/header_sanitizing. I'm assuming that is the goal, and if so, I suggest trying to stick with the same terms.

// (DEFAULT) The header specified in :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`
// will be dropped, before the tags are applied. Sanitize will be run regardless of if the source is a trusted IP or not.
//
// Note that the header will be visible unsanitized to any filters downstream of the ip-tag-header filter, unless it has an *x-envoy* prefix.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The word "downstream" may be ambiguous here.
I'm assuming it implies the filters that are invoked after this filter, but I'm not sure. Can you clarify it (probably by not using the word "downstream")?

Comment on lines 52 to 53
// :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`,
// leaving original values in place.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`,
// leaving original values in place.
// :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`.

// :ref:`ip_tag_header <envoy_v3_api_field_extensions.filters.http.ip_tagging.v3.IPTagging.ip_tag_header>`,
// leaving original values in place.
//
// Please note that this could cause the header to retain values set by the http client even if the client is from an untrusted IP.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RE "untrusted IP", see comment above regarding the terms.

// leaving original values in place.
//
// Please note that this could cause the header to retain values set by the http client even if the client is from an untrusted IP.
APPEND_FORWARD = 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "FORWARD" imply?

I'm assuming the goal is to have something similar to APPEND_IF_EXISTS_OR_ADD. Will it make sense to either use the same enum (probably not as the one suggested adds sanitization), or use the same enum name?

Ariane van der Steldt added 2 commits November 5, 2024 13:28
Signed-off-by: Ariane van der Steldt <[email protected]>
Ariane van der Steldt added 4 commits November 5, 2024 14:15
Signed-off-by: Ariane van der Steldt <[email protected]>
Signed-off-by: Ariane van der Steldt <[email protected]>
Signed-off-by: Ariane van der Steldt <[email protected]>
@nahratzah
Copy link
Contributor

I applied the changes. <3

Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm api

@repokitteh-read-only repokitteh-read-only bot removed the api label Nov 6, 2024
@alyssawilk alyssawilk merged commit 414ad34 into envoyproxy:main Nov 6, 2024
21 checks passed
@Radha13 Radha13 deleted the add-optional-header-ip-tagging-filter branch November 25, 2024 11:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants