fix(rewrite): fix cname target rewrite for CNAME chains#7853
Conversation
This fix corrects the cname target rewrite to handle CNAME chains: - Preserves only the CNAME records before matching the rule - Rewrites only the CNAME target that matches the rule - Includes all records from the re-resolved upstream response Signed-off-by: hide <hide@hide.net.eu.org>
|
@hdfln , thank you for contributing! Please add documentation to "CNAME Field Rewrites" section of the README.md for the plugin. |
4fff19f to
00d3e5d
Compare
|
@greenpau, thank you for the feedback! I've added a clarification to the "CNAME Field Rewrites" section describing how answer records are handled during the CNAME rewrite. |
220e5a0 to
4dad71a
Compare
…t rewrite Signed-off-by: hide <hide@hide.net.eu.org>
4dad71a to
e208e33
Compare
|
@hdfln , please see DCO. |
| my-app.example. 200 IN CNAME my-app.com.other.cdn.com. | ||
| my-app.com.other.cdn.com. 100 IN A 30.3.1.2 | ||
| ``` | ||
| Note that the answer will contain a completely different set of answer records after rewriting the `CNAME` target. |
There was a problem hiding this comment.
@hdfln , add extra line to separate between code and paragraph
|
@hdfln , how do you know that DNS clients will resolve Which of the common client libraries might have an issue with this? Also, should this behavior be default or configurable? |
|
@johnbelamaric , what do you think about this one? |
Signed-off-by: hide <hide@hide.net.eu.org>
e208e33 to
0e709be
Compare
Signed-off-by: hide <hide@hide.net.eu.org>
|
@greenpau, thank you for the review!
Thank you. I fixed my commit.
I tested with dig and nslookup using a similar CNAME chain structure (with different domain names). With this fix, both tools displayed the complete response including the final IP address.
I tested with dig and nslookup. Without this fix, both tools displayed an incomplete response missing the final A record when resolving CNAME chain. The issue was not with the clients, but with CoreDNS: without this fix, the response of CoreDNS did not include all records from the upstream lookup after rewriting the CNAME target.
I believe this should be the default behavior. I don't see a valid use case where incomplete resolution would be the desired outcome. |
@hdfln , there is slight difference between "tools" (dig and nslookup) vs "libraries" (e.g. Say one of these libraries does not do chain resolution below for whatever reason. There could be an issue. |
👍 |
@greenpau, thank you for your clarification about libraries. Does this address your concern, or did I misunderstand your point? |
|
@hdfln , that is the concern. When a single response contains a chain of resolution for CNAME. |
|
@greenpau, thank you for clarifying. Is your concern about DNS client libraries that cannot handle CNAME chains? From my understanding, DNS client libraries do not have issues with CNAME chains. I tested with miekg/dns just in case, and it received the CNAME chain correctly. Does this address your concern? |
|
@hdfln , you are proposing a change to defaults. Imagine that some client library that supports 20% of the Internet traffic does not do the chains for whatever reason. We merge it. That will not look good. I am asking you to consider doing the due diligence, understand, and then share the impact of the change. |
|
@greenpau , thank you again for your feedback. |
|
@greenpau, I tested with c-ares 1.34.5 and it successfully received and parsed the complete CNAME chain from CoreDNS. |
|
@greenpau I don't quite understand your concern. Imagine the target of the rewrite was queried directly, so without a rewrite rule. Then wouldn't the full upstream response be exactly what would be returned? So how could including all upstream records after a rewrite be any different for clients/libraries? |
|
@mwijngaard , the main principle is “not knowing what you don’t know”. Somehow, “fix” was never an issue for many years. The rewrites worked as is. Now, this changes the default behavior for all CNAME rewriting. I want to make sure that this does not impact the users’ community. Obviously this change works for @hdfln. The question is whether it works for the rest of us? |
|
@mwijngaard , how did you run into this issue? I saw you created a duplicate issue. What is the pattern you saw that necessitated this PR? On one side, it could be a coincidence that you both ran into it. On the other side, is there something else that made this pattern a thing? |
|
@hdfln , let me do some testing of my own. Will get back to you next week. |
|
@greenpau I use tailscale egress proxies to connect kubernetes workloads to an ip restricted server. The domains hosted on the server use a cname record to resolve to the server, like:
When my managed kubernetes provider had coredns <1.11, I used to use a query rewrite for each domain, but now that it has a more recent version I wanted to make use of the existing CNAME structure to just require 1 rewrite rule per server, instead of 1 rewrite rule per domain. However, the tailscale kubernetes egress proxy mechanism uses a kubernetes service, which is basically a CNAME that points to a specific pod the currently runs the proxy:
So when I use a cname rewrite rule, the end result should be:
But because currently, only the exactly matching upstream record is returned, the A record is left out. My kubernetes pods/containers use the coredns running in the cluster as their dns resolver directly, and don't have any other mechanism that facilitates recursion, so that doesn't work. As for both of us running into it at roughly the same time after cname rewrites having been implemented more than 2 years ago, I think that's mostly a coincidence, but it might have something to do with which coredns version ships with kubernetes distributions. I hope that helps for some context. |
|
@hdfln , thank you for contributing 👍 |
* fix(rewrite): fix cname target rewrite for CNAME chains This fix corrects the cname target rewrite to handle CNAME chains: - Preserves only the CNAME records before matching the rule - Rewrites only the CNAME target that matches the rule - Includes all records from the re-resolved upstream response Signed-off-by: hide <hide@hide.net.eu.org> * docs(rewrite): document how answer records are handled in CNAME target rewrite Signed-off-by: hide <hide@hide.net.eu.org> * fix(rewrite): simplify slice append per staticcheck S1011 Signed-off-by: hide <hide@hide.net.eu.org> * docs(rewrite): add extra line between code and paragraph Signed-off-by: hide <hide@hide.net.eu.org> --------- Signed-off-by: hide <hide@hide.net.eu.org> Co-authored-by: hide <hide@hide.net.eu.org>
1. Why is this pull request needed and what does it do?
This fixes CNAME target rewrite in the rewrite plugin to correctly handle CNAME chains.
When a response contains a CNAME chain, the CNAME target rewrite had three issues:
This fix:
Example (written in test):
with a suffix rewrite rule:
prod.→staging.Original response before rewrite:
Expected response after rewrite:
Current response after rewrite:
2. Which issues (if any) are related?
None.
3. Which documentation changes (if any) need to be made?
None.
4. Does this introduce a backward incompatible change or deprecation?
The response for CNAME chains changes.
Single-level CNAME responses are unaffected.