-
Notifications
You must be signed in to change notification settings - Fork 103
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
Initial DNS proxy implementation #536
Conversation
a16c08c
to
de51a9e
Compare
FYI @arvindsree @shakti67 |
f186881
to
4e53ef4
Compare
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.
Just minor comments after a cursory look, what's here seems good.
crates/dns/src/proxy.rs
Outdated
|
||
/// A Trust-DNS [ResponseHandler] that proxies all DNS requests. | ||
/// | ||
/// A proxy is fundamentally different than an `Authority` in TrustDNS, since the answers may |
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.
Do we know if upstream would be amenable to this in theory? If not, can we ask?
it's hard to grok the cost of carrying this (more-verbose) workaround if we don't have a read on whether it can ever be upstreamed or not.
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.
I'd like to land this PR before I approach the Trust-DNS folks, since I'd like some evidence that it actually works :). It definitely seems to be a use case they hadn't considered, so I think it's worth at least having the discussion. Even if they don't accept it, it's not a lot of code and it's all stuff that needs to be done anyway if we're going to use Trust-DNS.
crates/dns/src/proxy.rs
Outdated
// We are the authority here, since we control DNS for known hostnames | ||
response_header.set_authoritative(answer.is_authoritative()); |
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.
(possibly stupid Q from a non-DNS expert) Does this mimic the current Go DNS logic? Is this normal? Are we going to confuse anything if some responses are marked as authoritative and some not, from what the client (believes is) the same DNS server?
Since (AFAIK) the whole DNS server is either authoritative or not, usually?
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.
The behavior should be the same as the Istio code: https://github.com/istio/istio/blob/5aa2900dca83190962aea2eac8712ca88ca63da3/pkg/dns/client/dns.go#L289
Typically an entire server is authoritative or not ... but that's where a DNS proxy is a little different. We're only authoritative over the records we know about and defer to the upstream resolver for everything else.
This is a cleanup resulting from istio#536. This moves all service-related datastructures to a sub repository called `ServiceStore`. It has a simple API, which is used by the `WorkloadStore` and clarifies the contract between the two. The separation also helps to generally simplify the `WorkloadStore`, which was becoming increasingly monolithic.
This is a cleanup resulting from istio#536. This moves all service-related datastructures to a sub repository called `ServiceStore`. It has a simple API, which is used by the `WorkloadStore` and clarifies the contract between the two. The separation also helps to generally simplify the `WorkloadStore`, which was becoming increasingly monolithic.
This is a cleanup resulting from istio#536. This moves all service-related datastructures to a sub repository called `ServiceStore`. It has a simple API, which is used by the `WorkloadStore` and clarifies the contract between the two. The separation also helps to generally simplify the `WorkloadStore`, which was becoming increasingly monolithic.
This is a cleanup resulting from istio#536. This moves all service-related datastructures to a sub repository called `ServiceStore`. It has a simple API, which is used by the `WorkloadStore` and clarifies the contract between the two. The separation also helps to generally simplify the `WorkloadStore`, which was becoming increasingly monolithic.
1ddf53c
to
1762057
Compare
@howardjohn @kdorosh @bleggett I believe this is ready to go. The relevant Istio DNS tests were copied here and are passing. There are a couple of tests missing (due to current system limitations), which are marked as Tests are currently unit/integration-level (DNS client/server using loopback) .. they do not use iptables/eBPF redirection. Happy to add it if folks can suggest how/where best to do so... otherwise, we can address in a follow-on PR. |
I think a follow-on PR is fine - ideally we want to just re-run the existing I'd like that to be a followup as a Definition-Of-Done just to prove (now as well as ongoing) that ambient DNS redirection works at all and to keep us from breaking things. This PR LGTM tho. |
FYI I should be able to test truncation after this lands: https://github.com/bluejekyll/trust-dns/pull/1975 |
|
Ideally the fix is not in |
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.
I mostly reviewed the impact on existing codepaths as this already has some LGTMs, I don't have a lot of time to thoroughly review the whole change, and I don't want to block it. Looks safe. main comment is the testing issue of the additional crate
@@ -67,7 +69,7 @@ impl Proxy { | |||
drain: Watch, | |||
) -> Result<Proxy, Error> { | |||
let mut pi = ProxyInputs { | |||
cfg, | |||
cfg: cfg.clone(), |
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.
cfg: cfg.clone(), | |
cfg, |
const DNS_CAPTURE_METADATA: &str = "ISTIO_META_DNS_CAPTURE"; | ||
let dns_enabled = pi | ||
.cfg | ||
.proxy_metadata |
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.
cfg should probably have a dns_proxy_enabled
(or perhaps Option<SocketAddr>
) and we just use it directly?
Also I think the istio_meta_ is stripped (
Line 403 in 1762057
pc.proxy_metadata = pc |
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.
I was hoping to leverage the existing configuration for DNS capture for now, rather than introducing something new. I don't have a strong opinion here.
@bleggett thoughts?
Regarding using proxy metadata (since it seems I'm probably not using it correctly here), do we have other examples in ztunnel that use it? If we have constants defined somewhere, I can add the DNS one there.
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.
I don't mean anything user facing, just we currently process all settings into cfg
struct and now we are parsing them on-demand
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.
Ok sure .. just trying to figure out the desired end state here. Are you suggesting that I read the metadata and then set the flag on the config based on that?
/// Runs this DNS proxy to completion. | ||
pub async fn run(self) { | ||
// TODO(nmittler): Do we need to use drain? | ||
if let Err(e) = self.server.block_until_done().await { |
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.
What marks it "done"? Do wen eed to close the listeners ourself?
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.
Seems we get an error on sigterm:
WARN ztunnel::proxy::dns: DNS server shutdown error: Internal error in spawn: task 4 was cancelled
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.
Yeah, probably worth upstreaming a fix here. I'm guessing the behavior we want would be similar to our draining logic?
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.
Maybe yeah. It probably matters less for DNS though since there is less persistent connections (none for UDP)
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.
Raised an issue with Trust-DNS: https://github.com/bluejekyll/trust-dns/issues/1976. Let me know if the ask sounds right to you.
Ah right. I think the "correct" way to support multiple crates would be to use a workspace. That has a number of advantages anyway, such as mananging one version for depdencies across crates. Anyway, since the code in the crate was really something that should be upstreamed anyway .. I'll just get rid of the extra crate for now. |
2a7da15
to
a62a9da
Compare
1c47461
to
0d20005
Compare
This copies much of the logic from Istio, however it also supports a multi-tenant proxy. The new `istio-dns` crate extends the Trust-DNS API to specifically support the DNS proxy use case. The existing Trust-DNS `Catalog` does not allow an `Authority` to conditionally indicate its answers as authoritative, which is something that a proxy needs to do. The code here was written with the intent of eventually upstreaming to Trust-DNS. Within ztunnel, a new `dns` package implements the `istio-dns` `Resolver`, and serves DNS directly from the `WorkloadStore`. The logic for handling host aliases has been somewhat inverted from Istio. Istio pre-generated aliases for each client and added entries for all possible hosts to the lookup table. However, in shared proxy mode we have to handle the problem that some host aliases are client-specific (e.g. just service-name without namespace). To account for this, we dynamically run the alias logic in reverse, trying to figure out the FQDN from the requested hostname. This means that no additional entries for aliases were necessary in the `WorkloadStore`. Ztunnel dns uses one of two types of DNS forwarders, depending on if shared or dedicated mode. When in shared mode, it needs to use the configuration for the client pod in order to forward to the appropriate upstream resolver. Fixes istio#487
Adds support for a subset of the service entry api to ambient mesh. The api supported is as follows: - addresses (the VIPS) - auto VIP address allocation (addresses is optional) - endpoints (static only) - location (prefer mesh external for now for DIRECT passthrough in ztunnel) - workloadSelector (selects pods and workload entries) notable exclusions: - hosts (coming per istio/ztunnel#536) - exportTo - resolution (everything is static) - subjectAltNames (pending ztunnel support) Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io>
* ambient: service entry initial impl Adds support for a subset of the service entry api to ambient mesh. The api supported is as follows: - addresses (the VIPS) - auto VIP address allocation (addresses is optional) - endpoints (static only) - location (prefer mesh external for now for DIRECT passthrough in ztunnel) - workloadSelector (selects pods and workload entries) notable exclusions: - hosts (coming per istio/ztunnel#536) - exportTo - resolution (everything is static) - subjectAltNames (pending ztunnel support) Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * update more tests to use common functions * add release note * Prefer updates.Insert() Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Move lock lower so there's less lock contention Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Ambient SE handler should work regardless of EnableK8SServiceSelectWorkloadEntries setting Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Update test after local testing for auto assign vips Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Stronger assertion for load balancing Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Fix unit tests for ndots in test app Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Remove dead code Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * DRY SE ports construction Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * DRY some internal network address conversion code Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Stop testing with load balancing; tests run super slowly with that Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Ensure service entries cannot select across namespaces Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Remove customizations for skipped test Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Optimize pod xds events if not selected by service entry Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Optimize workload entry xds events if not selected by service entry Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Move logic for HandleServiceEntry into AmbientIndex interface Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Don't pass around entire ambient index Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Prefer ValidatedSetSelector for performance Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Rename function to be clearer Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Don't pass around ambient index child Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> * Remove auto vip allocation Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> --------- Signed-off-by: Kevin Dorosh <kevin.dorosh@solo.io> Co-authored-by: Kevin Dorosh <kevin.dorosh@solo.io>
This copies much of the logic from Istio, however it also supports a multi-tenant proxy.
New crate
The new
istio-dns
crate extends the Trust-DNS API to specifically support the DNS proxy use case. The existing Trust-DNSCatalog
does not allow anAuthority
to conditionally indicate its answers as authoritative, which is something that a proxy needs to do. The code here was written with the intent of eventually upstreaming to Trust-DNS.Host aliases
Within ztunnel, a new
dns
package implements theistio-dns
Resolver
, and serves DNS directly from theWorkloadStore
.The logic for handling host aliases has been somewhat inverted from Istio. Istio pre-generated aliases for each client and added entries for all possible hosts to the lookup table.
However, in shared proxy mode we have to handle the problem that some host aliases are client-specific (e.g. just service-name without namespace). To account for this, we dynamically run the alias logic in reverse, trying to figure out the FQDN from the requested hostname. This means that no additional entries for aliases were necessary in the
WorkloadStore
.Forwarding
Ztunnel dns uses one of two types of DNS forwarders, depending on if shared or dedicated mode. When in shared mode, it needs to use the configuration for the client pod in order to forward to the appropriate upstream resolver.
Fixes #487