Mutual TLS: Update port to address as listener identity.#570
Mutual TLS: Update port to address as listener identity.#570mattklein123 merged 13 commits intoenvoyproxy:masterfrom
Conversation
|
@mattklein123 , please take a look, this PR fixes #507, please take a look. thanks. |
| } | ||
| } | ||
|
|
||
| TcpListenSocket::TcpListenSocket(std::string address, bool bind_to_port) { |
| local_address_.reset(new Address::Ipv4Instance(port)); | ||
| } | ||
|
|
||
| TcpListenSocket::TcpListenSocket(int fd, std::string address) { |
source/exe/hot_restart.cc
Outdated
|
|
||
| RpcGetListenSocketRequest rpc; | ||
| rpc.port_ = port; | ||
| memcpy(rpc.address_, address.c_str(), address.length() + 1); |
There was a problem hiding this comment.
make sure address.length() < 256?
test/common/network/dns_impl_test.cc
Outdated
| ReadFilter(TestDnsServerQuery& parent) : parent_(parent) {} | ||
|
|
||
| // Network::ReadFilter | ||
| // Network::ReadFilter' |
|
@myidpt , FYI |
|
@htuch can you possibly take the first pass on this? |
|
wattli@ I can take a look at this. Before beginning, it would be helpful if you can you rebase from master, squash all the commits down to a single commit and provide a descriptive commit message? Thanks. |
|
Also, Travis CI tests are failing as this isn't building. I think rebasing from master might fix it if it's working locally for you. |
|
@htuch please take a look. Thanks. |
|
@wattli Travis CI is still failing. Also, you haven't rebased from master, squashed down the commits and put together a descriptive comment. You should squash locally with "git rebase -i" until you have a single commit, and then "git push -f" to this branch. This will kill the working history and leave us with a single commit to start the review with. It's fine to later push smaller fixup commits, but I'd like to begin with a base that is most of the way there. If you're new to git and need some help, I'll be available most of tomorrow EDT on https://gitter.im/lyft/envoy and can assist with the git flow. |
ab673ba to
5daa5c4
Compare
|
@htuch , it should be ready for review now. Thanks. |
| * the address returned by getOriginalDst() match the listener address. In this case the | ||
| * listener handles the connection directly and does not hand it off. | ||
| */ | ||
| if (listener->socket_.localAddress()->asString() != final_local_address->asString()) { |
There was a problem hiding this comment.
I wonder if we want to have address wildcards. For ingress on Kubernetes pods, what's the IP address in the listener? Is it pod IP, service IP, or 127.0.0.1? If it can be either pod IP or service IP, then we need to have a list of addresses in the listener spec.
There was a problem hiding this comment.
Is this instead of 0.0.0.0 which is too broad? E.g. you're after just {pod IP, 127.0.0.1} but not service IP? Then yeah, a list might make sense. This seems like it's beyond the scope of this PR though - you can use 0.0.0.0 for backwards compatibility with Envoy today with the current PR. It would make sense to file a PR for extending to a list once this PR is in though.
There was a problem hiding this comment.
Agree with htuch's point. We can start from one address with 0.0.0.0 as the wild card. And then extend to a list if necessary.
There was a problem hiding this comment.
In general, looks great. In addition to the comments below, I agree with @lizan that we should have some tests for 0.0.0.0 wildcard matching, in particular given the new logic in ConnectionHandlerImpl::findListenerByPort().
Can you also add some tests for use_original_dst that deal with different addresses?
source/exe/hot_restart.cc
Outdated
There was a problem hiding this comment.
Either sizeof(rpc.address_) (which should work, as this is an explicitly sized array) or use a named constant both here and in hot_restart.h.
There was a problem hiding this comment.
Addresses have overloaded == operator, so should be comparable directly without having to convert to strings.
There was a problem hiding this comment.
Ditto, shouldn't need to be performing direct string comparisons here.
There was a problem hiding this comment.
This was a problem in the original code - we shouldn't use single letter variables like l, too easy to confuse with 1. can we rename to "auto listener" ?
There was a problem hiding this comment.
Updated here and everywhere else in this file.
There was a problem hiding this comment.
Maybe we should have an attribute on Address that lets us know if it's 0.0.0.0 without having to do string compares?
There was a problem hiding this comment.
Good point, i added a member function isWildcard().
There was a problem hiding this comment.
Do you want to EXPECT on the return value for listen? Same above, I know this was an issue in the original test.
There was a problem hiding this comment.
Can you comment on why we have this test as well as the socket2? In general, all these socket cases could do with a one line comment explaining the intent.
|
@htuch , wildcard test case is covered as well as use_original_dst. Thanks. |
|
@wattli I'll take a look, but tests are failing, so please look into them as well. |
include/envoy/network/address.h
Outdated
include/envoy/network/address.h
Outdated
There was a problem hiding this comment.
I was thinking of adding helpers for creating the ANY address instances, equivalent to (but with shared instances):
namespace Network {
namespace Address {
InstancePtr anyIpv4() { return InstancePtr(new Ipv4Instance(0)); }
InstancePtr anyIpv6() { return InstancePtr(new Ipv6Instance(0)); }
} // Address
} // Network
That should make the impl a bit easier.
There was a problem hiding this comment.
I don't know if this solves the problem that isWildcard() is being used for though - the idea is you have an address that is something like 0.0.0.0:443 and want to know if just the IP part (ignoring the port) is wildcard.
source/common/network/address_impl.h
Outdated
There was a problem hiding this comment.
Why return false? As Harvey indicates, the IPv6 equivalent of IPv4's "0.0.0.0" is "::", which is short for "0:0:0:0:0:0:0:0".
There was a problem hiding this comment.
I think this might be a source of compile errors?
There was a problem hiding this comment.
According to man (2) listen: "On success, zero is returned. On error, -1 is returned, and errno is set appropriately."
There was a problem hiding this comment.
Shouldn't need to explicitly construct a std::string?
There was a problem hiding this comment.
Otherwise it will complain 'ambiguous message type'.
There was a problem hiding this comment.
Isn't the idea here that we're trying to test findListenerByAddress? But instead it seems that this method is mocked, so it doesn't get exercised..
There was a problem hiding this comment.
The unit test of findlistenerbyaddress is in connection_handler_test.cc. This here is for use_original_dst, which can't be tested there.
|
This PR seems to have strayed away from the original goal, listen on the loopback address, and into the territory of "Update Envoy to use Network::Address::InstancePtr instead of port number". Can we separate these? I've been doing a bit of the latter as part of working on #351, which could be split into separate issues as appropriate. |
|
James, do you mean not to introduce isAllHostsWild() function? If that's
the case, i am fine with that.
…On Wed, Mar 15, 2017 at 1:42 PM, James Synge ***@***.***> wrote:
This PR seems to have strayed away from the original goal, listen on the
loopback address, and into the territory of "Update Envoy to use
Network::Address::InstancePtr instead of port number". Can we separate
these? I've been doing a bit of the latter as part of working on #351
<#351>, which could be split into
separate issues as appropriate.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#570 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AXQIZvXS1VqA9drC6a6toW_rGCLzUfJyks5rmE0vgaJpZM4MdErn>
.
|
|
Well, if we use the word "any" in the functions I suggested (rather than saying "all hosts wild"), then we should probably call the predicate function something like "isAny" or "isAnyAddress". |
|
@htuch, so can I revert this function, and only do string comparison and
leave the logic to James?
…On Wed, Mar 15, 2017 at 1:49 PM, James Synge ***@***.***> wrote:
Well, if we use the word "any" in the functions I suggested (rather than
saying "all hosts wild"), then we should probably call the predicate
function something like "isAny" or "isAnyAddress".
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#570 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AXQIZqGAf2qSvIYo3vj0Oiv2YrQyjCDCks5rmE7ogaJpZM4MdErn>
.
|
|
I'd strongly prefer not to be matching on strings. I just spoke with @jamessynge and he is fine with you adding isAnyAddress(), as long as tests are added as well. |
|
@htuch , the comments are addressed. Thanks. |
mattklein123
left a comment
There was a problem hiding this comment.
Overall great work. Some comments.
There was a problem hiding this comment.
Can we get an example of what a TCP listener address looks like, and how to specify wildcard vs. specific.
There was a problem hiding this comment.
const Network::Address::Instance&
include/envoy/server/hot_restart.h
Outdated
include/envoy/server/configuration.h
Outdated
There was a problem hiding this comment.
const std::string& (or potentially have this just return AddressPtr directly).
include/envoy/server/instance.h
Outdated
There was a problem hiding this comment.
const std::string& address
source/exe/hot_restart.cc
Outdated
source/server/configuration_impl.h
Outdated
There was a problem hiding this comment.
You are comparing pointers here. This is almost definitely not what you want. Please make sure you have tests that catch this. (Though if you fix per other comment to const Instance& it will be obvious)
There was a problem hiding this comment.
done and more tests are added
There was a problem hiding this comment.
It would be kind of nice to not have to do 2 searches here. I think this was commented on elsewhere, but wondering if we should do this in follow up. I wonder if we should have different options, "use_original_port" and "use_original_address". Not a huge deal, but throwing it out there.
There was a problem hiding this comment.
Yes, it is mentioned above we can use a map to improve efficiency. I prefer leaving a TODO here and get rid of this big PR. :)
source/server/server.cc
Outdated
There was a problem hiding this comment.
You have made it work for UDS, you can remove this TODO. :)
htuch
left a comment
There was a problem hiding this comment.
A few more minor comments then I think it's good to hand over to mattklein123@ for approval. Thanks for your patience with the nits.
There was a problem hiding this comment.
Best to use // C++ comment style for method internal comments. I think the Javadoc style is only for stuff that Doxygen processes.
There was a problem hiding this comment.
Do you also want to verify that newConnection isn't called on listenerDst?
There was a problem hiding this comment.
The name of this test is a bit confusing, since use_original_dst is set false. Shouldn't it be UseActualDst?
There was a problem hiding this comment.
Can TcpListenSocket::TcpListenSocket(uint32_t port, bool bind_to_port) below be implemented in terms of this method? That would reduce the amount of code duplication.
There was a problem hiding this comment.
Matt raised similar issue above. In config, admin still uses port. So we can have a follow up PR to update admin from using port to address and make corresponding change later. I will let him decide
There was a problem hiding this comment.
Yes we can do in follow up. Please add TODO somewhere.
There was a problem hiding this comment.
Ditto but with TcpListenSocket::TcpListenSocket(int fd, uint32_t port).
|
@mattklein123 , all checks have passed. Please take a look, thanks. |
source/exe/hot_restart.cc
Outdated
| return -1; | ||
| } | ||
| StringUtil::strlcpy(rpc.address_, address.c_str(), address.length()); | ||
| StringUtil::strlcpy(rpc.address_, address.c_str(), address.length() + 1); |
There was a problem hiding this comment.
The last param is the max length of the target buffer, which is why it didn't work before.
There was a problem hiding this comment.
I misunderstood what you meant. Updated
|
Right.
…On Thu, Mar 16, 2017 at 2:02 PM, Matt Klein ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In source/exe/hot_restart.cc
<#570 (comment)>:
> @@ -173,7 +173,7 @@ int HotRestartImpl::duplicateParentListenSocket(const std::string& address) {
if (address.length() >= sizeof(rpc.address_)) {
return -1;
}
- StringUtil::strlcpy(rpc.address_, address.c_str(), address.length());
+ StringUtil::strlcpy(rpc.address_, address.c_str(), address.length() + 1);
The last param is the max length of the target buffer, which is why it
didn't work before.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#570 (review)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AXQIZjGO7Vp0YRUEegjA8lDJB8s2NA1Hks5rmaNrgaJpZM4MdErn>
.
|
|
@mattklein123 , do you have more comments on this PR? |
|
I haven't looked yet. Can you fix the thing I just mentioned? The last param should be sizeof(rpc.address_) |
|
@mattklein123 , checks passed, please take a look when available, thanks :) |
| {# TCP listener for external port 443 (SSL). Assumes a TCP LB in front such as ELB which | ||
| supports proxy proto. #} | ||
| {{ listener(9300,True,True) }}, | ||
| {{ listener("tcp://127.0.0.1:9300",True,True) }}, |
| {# TCP listener for external port 80 (non-SSL). Assumes a TCP LB in front such as ELB which | ||
| supports proxy proto. #} | ||
| {{ listener(9301,False,True) }} | ||
| {{ listener("tcp://127.0.0.1:9301",False,True) }} |
| supports proxy proto. #} | ||
| {{ listener(9300, ssl=True, proxy_proto=True, router_file='envoy_router.template.json') }}, | ||
| {{ listener(9301, ssl=False, proxy_proto=True, router_file='envoy_router.template.json') }}, | ||
| {{ listener("tcp://127.0.0.1:9300", ssl=True, proxy_proto=True, router_file='envoy_router.template.json') }}, |
| {{ listener(9300, ssl=True, proxy_proto=True, router_file='envoy_router.template.json') }}, | ||
| {{ listener(9301, ssl=False, proxy_proto=True, router_file='envoy_router.template.json') }}, | ||
| {{ listener("tcp://127.0.0.1:9300", ssl=True, proxy_proto=True, router_file='envoy_router.template.json') }}, | ||
| {{ listener("tcp://127.0.0.1:9301", ssl=False, proxy_proto=True, router_file='envoy_router.template.json') }}, |
| {# TCP listener for backhaul traffic from the double proxy. | ||
| See envoy_double_proxy.template.json #} | ||
| {{ listener(9400, ssl=True, proxy_proto=False, pin_double_proxy_client=True, | ||
| {{ listener("tcp://127.0.0.1:9400", ssl=True, proxy_proto=False, pin_double_proxy_client=True, |
| */ | ||
| virtual Network::Listener* findListenerByPort(uint32_t port) PURE; | ||
| virtual Network::Listener* | ||
| findListenerByAddress(const Network::Address::InstancePtr& address) PURE; |
There was a problem hiding this comment.
const Network::Address::Instance& (not InstancePtr)
There was a problem hiding this comment.
I tried, it does not work in testing file because no comparison of Address::Instance is defined.
There was a problem hiding this comment.
That doesn't make sense, as you end up comparing them in the end. https://github.com/lyft/envoy/blob/master/include/envoy/network/address.h#L80
You had some other compiler error. Please try again or ask someone for an over the shoulder check.
source/common/network/address_impl.h
Outdated
| struct IpHelper : public Ip { | ||
| const std::string& addressAsString() const override { return friendly_address_; } | ||
| bool isAnyAddress() const override { | ||
| return memcmp(&ipv6_.address_.sin6_addr, &in6addr_any, sizeof(struct in6_addr)); |
There was a problem hiding this comment.
I think you actually want 0 == memcmp(...). Please add test for this.
There was a problem hiding this comment.
Yes we can do in follow up. Please add TODO somewhere.
source/exe/hot_restart.cc
Outdated
|
|
||
| RpcGetListenSocketRequest rpc; | ||
| rpc.port_ = port; | ||
| if (address.length() >= sizeof(rpc.address_)) { |
There was a problem hiding this comment.
not sure if I would bother with this check. strlcpy() will protect. I would just put in an ASSERT(address.length() < sizeof(rpc.address_))
|
|
||
| int InstanceImpl::getListenSocketFd(uint32_t port) { | ||
| int InstanceImpl::getListenSocketFd(const std::string& address) { | ||
| Network::Address::InstancePtr addr = Network::Utility::resolveUrl(address); |
There was a problem hiding this comment.
No point in resolving this, just do the string compare directly. Either it matches or it doesn't.
There was a problem hiding this comment.
I have to, address is in format 'tcp://address:port' while localAddress()->asString() is in format 'address:port'
configs/configgen.py
Outdated
| # optional external service ports: built from external_virtual_hosts above. Each external host | ||
| # that Envoy proxies to listens on its own port. | ||
| # optional mongo ports: built from mongos_servers above. | ||
| # TODO(wattli): investigate why a bad config (with port but not address) can |
There was a problem hiding this comment.
Sorry this is not an acceptable TODO, it will cause production issues.
The issue is that you are returning an std::string from the configuration, and it doesn't get bound to an address until the listeners actually start running on the workers. I mentioned this a while ago, but please have the configuration return an Address::InstancePtr instead of a string. This will cause an exception to be thrown if the address cannot be resolved at config load time.
|
|
||
| // TODO(wattli): remove this once the admin port is updated with address. | ||
| TcpListenSocket::TcpListenSocket(const std::string& address, bool bind_to_port) { | ||
| TcpListenSocket::TcpListenSocket(const Address::InstancePtr& address, bool bind_to_port) { |
There was a problem hiding this comment.
Just pass Address::InstancePtr here (passing by const& can lead to subtle bugs). Same below.
include/envoy/server/configuration.h
Outdated
| * @return std::string the address. | ||
| */ | ||
| virtual uint64_t port() PURE; | ||
| virtual const std::string& address() PURE; |
There was a problem hiding this comment.
@wattli this is where you need to return Address::InstancePtr. Resolve it inside the configuration. Otherwise https://github.com/lyft/envoy/blob/master/test/example_configs_test.cc#L32 will still potentially pass, since you first resolve it when the server starts running.
|
@mattklein123 @htuch , please take a look. |
source/server/configuration_impl.h
Outdated
| Network::FilterChainFactory& filterChainFactory() override { return *this; } | ||
| const std::string& address() override { return address_; } | ||
| Network::Address::InstancePtr address() override { | ||
| return Network::Utility::resolveUrl(address_); |
There was a problem hiding this comment.
Still not right. It doesn't help you if you don't resolve until you access. Resolve at construction time and change address_ to Address::InstancePtr
Signed-off-by: John Plevyak <jplevyak@gmail.com>
Description: I believe this was probably a bad copy paste. Risk Level: low Testing: builds. Signed-off-by: Jose Nino <jnino@lyft.com> Signed-off-by: JP Simard <jp@jpsim.com>
Description: I believe this was probably a bad copy paste. Risk Level: low Testing: builds. Signed-off-by: Jose Nino <jnino@lyft.com> Signed-off-by: JP Simard <jp@jpsim.com>
…pha.0 in the k8s-io group across 1 directory (#570)
fixes #507