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

Auto host rewrite option for strict_dns and logical_dns clusters #504

Merged
merged 19 commits into from
Feb 27, 2017

Conversation

rshriram
Copy link
Member

Enables envoy to rewrite Host headers for upstream requests, based on the hostname of the upstream instance (only for strict_dns and logical_dns clusters).

This is specifically useful when proxying to multiple cloud foundry instances (each of which has its own hostname, where the GoRouter routes requests to instances based on the hostname).

fixes #204

cc @mattklein123

While the changes are spread over a lot of files, they are pretty minor in 90% of the cases. The key changes are in the strict/logical dns implementations and hostdescription implementation.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

overall nice work, a few comments.

@@ -97,6 +98,15 @@ host_rewrite
*(optional, string)* Indicates that during forwarding, the host header will be swapped with this
value.

.. _config_http_conn_man_route_table_route_auto_host_rewrite:
Copy link
Member

Choose a reason for hiding this comment

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

nit: don't need the anchor if its not referenced anywhere (though you might want to reference from the router arch overview page).

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that is a todo on my end. Need to provide an example as well. Will add it to router arch

@@ -198,6 +198,11 @@ class RouteEntry {
* @return const VirtualHost& the virtual host that owns the route.
*/
virtual const VirtualHost& virtualHost() const PURE;

/**
* @return bool true if the Host header should be overwritten with the Upstream hostname.
Copy link
Member

Choose a reason for hiding this comment

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

"Host/:authority header"

Copy link
Member

Choose a reason for hiding this comment

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

s/Upstream/upstream

@@ -21,8 +21,7 @@ LogicalDnsCluster::LogicalDnsCluster(const Json::Object& config, Runtime::Loader
}

dns_url_ = hosts_json[0]->getString("url");
Network::Utility::hostFromTcpUrl(dns_url_);
Network::Utility::portFromTcpUrl(dns_url_);
hostname_ = Network::Utility::hostAndPortFromTcpUrl(dns_url_);
Copy link
Member

Choose a reason for hiding this comment

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

Though technically this should work (host:port), it's possible that some webservers might not handle this correctly. I could go either way, but my recommendation for now would be to remove hostAndPortFromTcpUrl() function that you added, and just populate the hostname() with the host, and without the port. Happy to discuss though.

Copy link
Member Author

Choose a reason for hiding this comment

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

According to the RFC,
A "host" without any trailing port information implies the default port for the service requested (e.g., "80" for an HTTP URL).

I assumed that if its not a default port, then the port must be part of the Host header.
If stuff works without a port, I would be more than happy to reuse the hostFromTcpUrl function.

@kyessenov thoughts? You hit some issues in k8s regarding this right?

Copy link
Member

Choose a reason for hiding this comment

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

yes, that's the RFC, but I'm just explaining that it might not actually work. In fact, Envoy is broken in this regard. We don't correctly parse out port when doing virtual host / domain matching from the host/auth header. For the use case you are adding this for, I strongly suspect that you probably only need host and not port, but you know your use case.

Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding is that envoy requires explicit port suffixes in hostname strings (e.g. domains in virtual hosts), just like RFC says.
I'd like to have the port number to be a part of the host rewrite string, and to be consistent with the server side config, it probably should be part of the string literal in the client cluster config. So basically, either strip port on both client and server side, or don't, but be consistent.

Copy link
Member

Choose a reason for hiding this comment

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

There are 2 things being conflated here, how Envoy parses "URLs" and what a web/proxy server expects in the Host/:authority header. This change is about auto-populating the Host/:authority header. Technically, all web/proxy servers should support host:port when doing vhost, etc. matching, but that doesn't always work, and as I said above, Envoy itself is broken in this regard currently. I think you will have better luck with this feature if you just populate the DNS host name and skip the port.

Copy link
Member

Choose a reason for hiding this comment

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

@kyessenov is this feature actually required for Istio? @rshriram I thought you were adding this to deal with cloud foundry.

Copy link
Contributor

Choose a reason for hiding this comment

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

no, we just use host-rewrite (in ingress) and SDS.

@@ -21,8 +21,7 @@ LogicalDnsCluster::LogicalDnsCluster(const Json::Object& config, Runtime::Loader
}

dns_url_ = hosts_json[0]->getString("url");
Network::Utility::hostFromTcpUrl(dns_url_);
Copy link
Member

Choose a reason for hiding this comment

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

I would leave those 2 lines, they make sure the URL is well formed, which hostAndPortFromTcpUrl does not. Otherwise we might get an exception later on. Since it's probably not clear why these lines were here, please add a comment to that effect.

HttpTestUtility::addDefaultHeaders(headers);
headers.Host()->value(req_host);
router_.decodeHeaders(headers, true);
EXPECT_EQ(req_host, headers.Host()->value().c_str());
Copy link
Member

Choose a reason for hiding this comment

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

The fact that this gets changed is an implementation detail that might not always be true. I would test that the headers you get in encodeHeaders() have the right host value. Same below.

@mattklein123
Copy link
Member

mattklein123 commented Feb 23, 2017

Also, you probably saw, but your tests are failing with asserts enabled so you also need to fix that.

@rshriram
Copy link
Member Author

rshriram commented Feb 23, 2017 via email

@mattklein123
Copy link
Member

In both cases, as you point out, it's probably safer to stick to just the
dns name and not the port.

OK, for now, let's just stick with host name for the rewrite. If we need to revisit this later to add port info we can do that.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

2 minor things otherwise looks good.

@@ -22,7 +22,9 @@ request. The router filter supports the following features:
level.
* :ref:`Path <config_http_conn_man_route_table_route_path_redirect>`/:ref:`host
<config_http_conn_man_route_table_route_host_redirect>` redirection at the route level.
* :ref:`Host rewriting <config_http_conn_man_route_table_route_host_rewrite>`.
* :ref:`Explicit host rewriting <config_http_conn_man_route_table_route_host_rewrite>`.
* :ref:`Automatic host rewriting <config_http_conn_man_route_table_route_auto_host_rewrite>` based on DNS name of
Copy link
Member

Choose a reason for hiding this comment

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

nit: "... the DNS name ..."

.WillOnce(Invoke([&](const Upstream::HostDescriptionPtr host)
-> void { EXPECT_EQ(host_address_, host->address()); }));
EXPECT_CALL(callbacks_.route_->route_entry_, autoHostRewrite()).WillOnce(Return(true));
EXPECT_CALL(*cm_.conn_pool_.host_, hostname()).WillRepeatedly(ReturnRef(dns_host));
Copy link
Member

Choose a reason for hiding this comment

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

It would be better if you made hostname_ a variable in the host mock which defaults to "" and then set up an ON_CALL for it. Then your tests just need to change the value of hostname_ inside the mock both here and below. It's a little less error prone and more realistic. Your test below should actually setup a DNS host like this test, but because autoHostRewrite() returns false, it won't be swapped.

MOCK_CONST_METHOD0(address, Network::Address::InstancePtr());
MOCK_CONST_METHOD0(stats, HostStats&());
MOCK_CONST_METHOD0(zone, const std::string&());

std::string hostname_{};
Copy link
Member

Choose a reason for hiding this comment

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

{} not needed

@@ -56,10 +56,12 @@ class MockHostDescription : public HostDescription {
MOCK_CONST_METHOD0(canary, bool());
MOCK_CONST_METHOD0(cluster, const ClusterInfo&());
MOCK_CONST_METHOD0(outlierDetector, Outlier::DetectorHostSink&());
MOCK_CONST_METHOD0(hostname, const std::string&());
Copy link
Member

Choose a reason for hiding this comment

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

add the ON_CALL to the constructor, and just ReturnRef hostname_

HttpTestUtility::addDefaultHeaders(incoming_headers);
incoming_headers.Host()->value(req_host);

ON_CALL(*cm_.conn_pool_.host_, hostname()).WillByDefault(ReturnRef(dns_host));
Copy link
Member

Choose a reason for hiding this comment

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

del this line, del dns_host, set host_.hostname_ = "scooby.doo"

HttpTestUtility::addDefaultHeaders(outgoing_headers);
outgoing_headers.Host()->value(dns_host);

ON_CALL(*cm_.conn_pool_.host_, hostname()).WillByDefault(ReturnRef(dns_host));
Copy link
Member

Choose a reason for hiding this comment

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

del this line, del dns_host, set host_.hostname_ = "scooby.doo"

@mattklein123 mattklein123 merged commit e909208 into envoyproxy:master Feb 27, 2017
@rshriram rshriram deleted the auto_host_rewrite branch March 23, 2017 15:29
lizan pushed a commit to lizan/envoy that referenced this pull request Jun 4, 2020
Signed-off-by: Pengyuan Bian <bianpengyuan@google.com>
wolfguoliang pushed a commit to wolfguoliang/envoy that referenced this pull request Jan 23, 2021
envoyproxy#504)

* zh-translation: docs/root/configuration/http/http_filters/original_src_filter.rst

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

* Update docs/root/configuration/http/http_filters/original_src_filter.rst

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>

Co-authored-by: majinghe <42570491+majinghe@users.noreply.github.com>
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.

auto host rewrite for DNS clusters
3 participants