NS records for child zones should be allowed to exist in parent zones

Bug #1917099 reported by Alex Carder
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Designate
Expired
Undecided
Unassigned

Bug Description

Users should be able to create NS records in parent zones for child zones. This would allow for DNS recursion on designate zones which is DNS RFC compliant. For example, if you have child.parent.example.com and parent.example.com as designate configured zones then you should be able to configure NS records for child.parent.example.com in the parent.example.com zone to enable DNS recursion. This is not possible due to existing designate limits restricting records to their child zone:

$ openstack recordset create parent.example.com. child --type NS --record 'ns1.website.net.' --record 'ns2.website.net.'
RecordSet belongs in a child zone: child.parent.example.com.

This is the correct approach for all records besides NS records. The source of this problem is here: https://github.com/openstack/designate/blob/master/designate/central/service.py#L352-L380

I've attached a basic patch to resolve this issue. Disclaimer: I did not test the implementation at all.

Revision history for this message
Alex Carder (carderalex) wrote :
Revision history for this message
Dr. Jens Harbott (j-harbott) wrote :

I don't think that in this case NS records are needed. A NS record for parent.example.com will be valid for anything below, be it child.parent.example.com, host.child.parent.example.com or host.parent.example.com.

Also, we cannot accept patched submitted via launchpad, please have a look at https://docs.openstack.org/contributors/code-and-documentation/quick-start.html .

Changed in designate:
status: New → Incomplete
Revision history for this message
Alex Carder (carderalex) wrote :

They are needed for DNS recursion. If parent.example.com is zone transferred to other servers (lets say ns4.website.com) and then a client queries ns4.website.com for something in child.parent.example.com then the client will not get an answer because ns4.website.com does not know who is authoritative for child.parent.example.com due to not having NS records for that zone. DNS recursion, in my understanding, would work like so:

root nameservers -> .com nameservers -> example.com nameservers -> parent.example.com nameservers -> child.parent.example.com -> SOA record.

I will submit a patch via Gerrit, apologies.

Revision history for this message
Alex Carder (carderalex) wrote :

I've created a patch in Gerrit for this change: https://review.opendev.org/c/openstack/designate/+/777866

Revision history for this message
Dr. Jens Harbott (j-harbott) wrote :

If the same nameserver serves both client and parent zone, no further delegation should be necessary. Please show some example where this is actually failing.

Revision history for this message
Alex Carder (carderalex) wrote :

I will have some time to demonstrate the problem on a couple PowerDNS servers tomorrow, but this is the closest I could get to finding it described in RFCs:
https://tools.ietf.org/html/rfc5936#section-3.2
https://tools.ietf.org/html/rfc1034#section-4.2.1

>The RRs that describe cuts around the bottom of the zone are NS RRs that
name the servers for the subzones.

>One of the goals of the zone structure is that any zone have all the
data required to set up communications with the name servers for any
subzones. That is, parent zones have all the information needed to
access servers for their children zones. The NS RRs that name the
servers for subzones are often not enough for this task since they name
the servers, but do not give their addresses.

The problem is that the nameserver being queried doesn't serve both client and parent zone.. if it only serves the parent zone then the nameserver doesn't know where to send the queries if you are relying on recursion as described in the RFCs.

Revision history for this message
Alex Carder (carderalex) wrote :

Here is the failure before adding NS records for the child zone (child.example.sandbox.domain.net) to the parent zone (example.sandbox.domain.net) demonstrated using PowerDNS:

# parent zone only exists on the host
[root@dev76-sandbox-pdnsslave01]# curl -v -H 'X-API-Key: -----------' https://dev76-sandbox-pdnsslave01.domain.net:8443/api/v1/servers/localhost/zones | jq .
[
  {
    "account": "admin",
    "dnssec": false,
    "edited_serial": 1615589654,
    "id": "example.sandbox.domain.net.",
    "kind": "Slave",
    "last_check": 1615589374,
    "masters": [
      "10.63.70.196"
    ],
    "name": "example.sandbox.domain.net.",
    "notified_serial": 1615589374,
    "serial": 1615589374,
    "url": "/api/v1/servers/localhost/zones/example.sandbox.domain.net."
  },
]

# we can get SOA records and NS records for the parent domain
[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 SOA example.sandbox.domain.net
dev76-sandbox-pdnsmaster01.domain.net. admin.domain.net. 1615588649 10800 3600 604800 86400

[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 NS example.sandbox.domain.net
dev76-sandbox-pdnsmaster01.domain.net.
dev76-sandbox-pdnsmaster02.domain.net.

# can't get SOA record for child zone
[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 SOA child.example.sandbox.domain.net
[root@dev76-sandbox-pdnsslave01]#

# can't get any records frmo child zone
[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 record.child.example.sandbox.domain.net
[root@dev76-sandbox-pdnsslave01]#

# trace is attached as failure_with_no_child_ns_records.txt

After adding NS records for child.example.sandbox.domain.net IN example.sandbox.domain.net everything works with no other changes

# zone still doesn't exist on the host
[root@dev76-sandbox-pdnsslave01]# curl -v -H 'X-API-Key: -----------' https://dev76-sandbox-pdnsslave01.domain.net:8443/api/v1/servers/localhost/zones | jq .
[
  {
    "account": "admin",
    "dnssec": false,
    "edited_serial": 1615589834,
    "id": "example.sandbox.domain.net.",
    "kind": "Slave",
    "last_check": 1615589464,
    "masters": [
      "10.63.70.196"
    ],
    "name": "example.sandbox.domain.net.",
    "notified_serial": 1615589464,
    "serial": 1615589464,
    "url": "/api/v1/servers/localhost/zones/example.sandbox.domain.net."
  },
]

# SOA records can now be found
[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 SOA child.example.sandbox.domain.net
dev76-sandbox-pdnsmaster01.domain.net. admin.domain.net. 1615589596 10800 3600 604800 86400

# same with any other child zone record
[root@dev76-sandbox-pdnsslave01]# dig +short @127.0.0.1 record.child.example.sandbox.domain.net
8.8.8.8

# trace is attached to success_with_child_ns_records.txt

I attached both these outputs + traces on the recursive server to this post. I also attached JSON output of the actual zones (parent) and child (pre and post adding NS records).

Revision history for this message
Michael Johnson (johnsom) wrote :

So, I agree that you should be able to do a sub-zone delegation.

However, this works for me, so I'm curious what is causing your issue. I am running the master branch of Wallaby:

commit 37b3fa2ea454183e4a5c6a099eca5129959d10e1 (HEAD -> master, origin/master, origin/HEAD)

$ openstack zone create --email <email address hidden> parent.example.com.

+----------------+--------------------------------------+
| Field | Value |
+----------------+--------------------------------------+
| action | CREATE |
| attributes | |
| created_at | 2021-03-22T21:14:24.000000 |
| description | None |
| email | <email address hidden> |
| id | bcfa2e0e-e26a-4e83-a635-05fedada47ac |
| masters | |
| name | parent.example.com. |
| pool_id | 794ccc2c-d751-44fe-b57f-8894c9f5c842 |
| project_id | 270d45a47a104f95a7c669c694f2ffc6 |
| serial | 1616447664 |
| status | PENDING |
| transferred_at | None |
| ttl | 3600 |
| type | PRIMARY |
| updated_at | None |
| version | 1 |
+----------------+--------------------------------------+

$ openstack recordset create --record ns2.example.com. --type NS bcfa2e0e-e26a-4e83-a635-05fedada47ac child

+-------------+--------------------------------------+
| Field | Value |
+-------------+--------------------------------------+
| action | CREATE |
| created_at | 2021-03-22T21:15:22.000000 |
| description | None |
| id | cedd9862-d253-4b68-95a7-c43c61df313e |
| name | child.parent.example.com. |
| project_id | 270d45a47a104f95a7c669c694f2ffc6 |
| records | ns2.example.com. |
| status | PENDING |
| ttl | None |
| type | NS |
| updated_at | None |
| version | 1 |
| zone_id | bcfa2e0e-e26a-4e83-a635-05fedada47ac |
| zone_name | parent.example.com. |
+-------------+--------------------------------------+

; Zone dump of 'parent.example.com/IN'
;
parent.example.com. 3600 IN SOA ns1.devstack.org. me.here.com. 1616447721 3530 600 86400 3600
parent.example.com. 3600 IN NS ns1.devstack.org.
child.parent.example.com. 3600 IN NS ns2.example.com.

This looks like it works for me, do you see a difference? What version are you running?

Revision history for this message
Michael Johnson (johnsom) wrote :

Same with two delegated addresses:

$ openstack recordset create --record ns3.example.com. --record ns44.example.com. --type NS bcfa2e0e-e26a-4e83-a635-05fedada47ac child

+-------------+--------------------------------------+
| Field | Value |
+-------------+--------------------------------------+
| action | CREATE |
| created_at | 2021-03-22T21:23:34.000000 |
| description | None |
| id | ebbe9473-b599-43f7-aa19-a2c11a2cd1e1 |
| name | child.parent.example.com. |
| project_id | 270d45a47a104f95a7c669c694f2ffc6 |
| records | ns44.example.com. |
| | ns3.example.com. |
| status | PENDING |
| ttl | None |
| type | NS |
| updated_at | None |
| version | 1 |
| zone_id | bcfa2e0e-e26a-4e83-a635-05fedada47ac |
| zone_name | parent.example.com. |
+-------------+--------------------------------------+

; Zone dump of 'parent.example.com/IN'
parent.example.com. 3600 IN SOA ns1.devstack.org. me.here.com. 1616448213 3530 600 86400 3600
parent.example.com. 3600 IN NS ns1.devstack.org.
child.parent.example.com. 3600 IN NS ns3.example.com.
child.parent.example.com. 3600 IN NS ns44.example.com.

Revision history for this message
Alex Carder (carderalex) wrote :

The problem is that child.parent.example.com exists in my scenario. If you create child.parent.example.com as a zone and then try to set NS records for that zone inside the parent.example.com zone you will reproduce the issue.

If you child zone doesn't exist in designate then designate doesn't complain.

Revision history for this message
Michael Johnson (johnsom) wrote :

Ok, so why would you create child.parent.example.com as a zone inside Designate if you are delegating it to another environment?

If you do plan to manage child.parent.example.com inside Designate, you don't need to setup a delegation (i.e. no NS records for child required).

Revision history for this message
Alex Carder (carderalex) wrote :

Because designate isn't the only DNS server that serves these zones. We manage the zones in designate and we zone transfer them other places to serve the records. If we zone transfer the child and parent zones to the same authoritative host it works fine, but if we only zone transfer the parent.. then that authoritative host doesn't know where to look for child. Normal DNS protocol would use a stub entry or DNS recursion (which is what we are relying on). I linked the relevant RFCs before, but if you can't put NS records for child into the parent zone then recursion won't work.

This is normal DNS practice as defined in the RFCs. Lets say a user is trying to get to child.parent.example.com but the DNS server they are hitting doesn't know anything about child.parent.example.com only parent.example.com..

1. first the DNS server would look for NS records in parent.example.com for child.parent.example.com
2. then it would query those nameservers for the original answer

Another scenario for recursion... if someone queried for record.child.parent.example.com but the DNS server they are querying doesn't know anything about child or parent...

1. first the DNS server would check if it knows anything about example.com (no)
2. then it checks if it knows how to get to .com (yes -- check the root servers)
3. the server queries root servers and gets the .com nameservers and asks them about child.parent.example.com or parent.example.com (assuming we didn't put a stub entry here for child.parent.example.com INSIDE example.com.. it should know where parent.example.com is because parent.example NS records exist in exmaple.com.. )
4. then the original DNS server would query the NS records it found for parent.exammpe.com
5. then the original DNS server should find the NS records for child.parent.example.com inside parent.example.com

Revision history for this message
Michael Johnson (johnsom) wrote :

Yeah, I am very well aware of the RFCs and how delegation works.

The part that was missing from this bug report is you are trying to create secondaries off of these two zones on different servers.

So, the missing reproduction steps are:

1. openstack zone create --email <email address hidden> parent.example.com.
2. openstack zone create --email <email address hidden> child.parent.example.com.
3. openstack recordset create --record ns3.example.com. --record ns44.example.com. --type NS bb7af787-a6d7-48ad-a4b0-00f694c20ec6 child

Which gives you:
RecordSet belongs in a child zone: child.parent.example.com.

I think the issue you are running into relates to the pool configuration. Both of these zones are being added to the same pool (as in same pool of DNS servers). But you are attempting to split them.
I will look into this more tomorrow and comment again now that the scenario is more clear.

Revision history for this message
Alex Carder (carderalex) wrote :

I don't think it's actually a pool problem because we do only want to transfer to the first set of DNS hosts directly from designate, but we then zone transfer the child and parent zone out to different sets of DNS servers.

Revision history for this message
Michael Johnson (johnsom) wrote :

I looked into this more today.

As I suspected, if you put child.parent.example.com. in a separate pool you are able to create the delegation NS record.

By splitting it into a separate pool you let Designate know that they are not in a direct parent/child relationship and the child doesn't fall under the zone of the parent zone.

This also makes sense for your child zones as if they are being distributed to alternate secondaries, wouldn't you want a different NS record list for those alternate servers? I wouldn't expect you would want to share the same ns_records pools.yaml setting across those two zones if they are being served by difference secondaries.

Revision history for this message
Alex Carder (carderalex) wrote :

That makes sense, though it still doesn't make recursion work without designing you your infrastructure to always zone transfer for designate directly. Ultimately I think you should be able to add NS records for zones outside of the pool configuration. And I still believe you should be able to add records for child zones in a parent zone. We don't necessarily want all our DNS clusters to communicate with designate; we would rather re-notify and zone transfer off the primary cluster. For us this simplifies out designate configuration and our firewall configurations.

Revision history for this message
Michael Johnson (johnsom) wrote :

Maybe I wasn't clear, when you split those two zones across two pools, you are then allowed to add the NS delegation record without error.

The NS records in the pool configuration are the authoritative names server records, not the delegation records.

Revision history for this message
Alex Carder (carderalex) wrote :

Ah I understand now, thanks for the clarification. I will verify on our end in the lab on Monday. We have since resolved this in our environment by adding forwarding records on our downstream recursor DNS servers.

Revision history for this message
Launchpad Janitor (janitor) wrote :

[Expired for Designate because there has been no activity for 60 days.]

Changed in designate:
status: Incomplete → Expired
Revision history for this message
OpenStack Infra (hudson-openstack) wrote : Change abandoned on designate (master)

Change abandoned by "Erik Olof Gunnar Andersson <email address hidden>" on branch: master
Review: https://review.opendev.org/c/openstack/designate/+/777866

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.