Skip to content

Conversation

@peterthomassen
Copy link
Member

No description provided.

@peterthomassen peterthomassen force-pushed the 20230928_check_delegation branch 7 times, most recently from b2fef24 to 52b1215 Compare September 29, 2023 08:31
@peterthomassen peterthomassen force-pushed the 20230928_check_delegation branch from d0e286c to 690331a Compare November 3, 2023 16:12
@peterthomassen peterthomassen force-pushed the 20230928_check_delegation branch from 690331a to 916ae8d Compare November 19, 2023 21:54
@nils-wisiol nils-wisiol force-pushed the 20230928_check_delegation branch 2 times, most recently from afa92f9 to 493f14e Compare April 9, 2025 20:38
@nils-wisiol nils-wisiol force-pushed the 20230928_check_delegation branch 2 times, most recently from a1030fb to 53a4f98 Compare April 10, 2025 09:30
@nils-wisiol nils-wisiol force-pushed the 20230928_check_delegation branch from e505269 to 76503d0 Compare April 21, 2025 16:58
Copy link
Member Author

@peterthomassen peterthomassen left a comment

Choose a reason for hiding this comment

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

very cool work!

I've only reviewed the actual code, and not the tests so far. Will look at tests in the next iteration.

Comment on lines +71 to +73
PARTIAL = 2
EXCLUSIVE = 3
MULTI = 4
Copy link
Member Author

Choose a reason for hiding this comment

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

I supposed PARTIAL means that one, but not all deSEC NS are there, and MULTI means that there are additional foreign NS. What if both is the case?

This suggests splitting up into DelegationStatusOur (no/partial/full) and DelegationStatusForeign (no/yes) or something, for more granularity.

Comment on lines +104 to +117
delegation_status_touched = models.DateTimeField(
default=None, null=True, blank=True
)
delegation_status_changed = models.DateTimeField(
default=None, null=True, blank=True
)
security_status = models.IntegerField(
choices=SecurityStatus.choices,
default=None,
null=True,
blank=True,
)
security_status_touched = models.DateTimeField(default=None, null=True, blank=True)
security_status_changed = models.DateTimeField(default=None, null=True, blank=True)
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think the _touched/_changed members need to be nullable, as the status itself can be null. That's redundant, so we can just have the fields set to the current time on creation (which probably is the default anyway).

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed

Comment on lines +242 to +252
"""
Queries the DNS to determine the delegation status of this domian and
update the delegation status on record.
The delegation status is evaluated by trying to determine the NS records
as defined in the parent zone to the this `Domain`.
The parent zone's apex is determined by walking up the DNS tree, querying
NS records until an answer is provided. Once the apex and nameservers are
found, they are queried for the nameservers of `self.name`.
"""
Copy link
Member Author

Choose a reason for hiding this comment

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

This docstring needs some proofreading :)

rr.address
for name in settings.DEFAULT_NS
for rrtype in {"A", "AAAA"}
for rr in list(resolver_CD.resolve(name, rrtype))
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
for rr in list(resolver_CD.resolve(name, rrtype))
for rr in resolver_CD.resolve(name, rrtype)

else:
parent_apex_candidate = parent_apex_candidate.parent()

# TODO what if no parnet_nameservers? what if len(parent_apex_candidate) == 0?
Copy link
Member Author

Choose a reason for hiding this comment

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

If no parent nameservers, then the domain is not delegated (because the TLD does not exist, so it is unregisterable). Should we have a special status for that? (I can see arguments both ways.)

Is the second questions about len(parent_apex_candidate) == 0 the same?

elif auth_ns_addrs == set():
# empty
self.delegation_status = self.DelegationStatus.NOT_DELEGATED
elif auth_ns_addrs is None:
Copy link
Member Author

Choose a reason for hiding this comment

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

How can this branch be reached? (auth_ns_addrs is assigned as a set comprehension)

Copy link
Contributor

Choose a reason for hiding this comment

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

likely due to refactoring

Comment on lines +356 to +361
if self.delegation_status not in [
self.DelegationStatus.MULTI,
self.DelegationStatus.EXCLUSIVE,
]:
self.security_status = None
return None
Copy link
Member Author

Choose a reason for hiding this comment

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

Should also include DelegationStatus.PARTIAL for folks who accidentally use ns2.desec.io (instead of .org)

self.security_status = self.SecurityStatus.SECURE_EXCLUSIVE
elif our_ds_set < auth_ds:
self.security_status = self.SecurityStatus.SECURE
elif auth_ds != set():
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
elif auth_ds != set():
elif auth_ds:

Comment on lines +364 to +368
auth_ds = set(
resolver_CD.resolve(
self.name, dns.rdatatype.DS, raise_on_no_answer=False
)
)
Copy link
Member Author

Choose a reason for hiding this comment

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

I think here we should check the AD bit (because otherwise the parent is insecure, and effectively the zone is not delegated securely). This means that we probably can't use the CD flag / resolver_CD, because (at least during an ad-hoc test), I did not get AD bits when setting CD. (As per RFC 6840 Section 5.7, it seems like requesting AD bit during CD should work (so you get unauthenticated data instead of SERVFAIL on validation failure), but it seems like nobody implements this.)

Perhaps we should store this as an extra column (boolean security_chain_complete?), so we can take different actions when the domain itself has no DS records vs. when it is under an insecure TLD (or similar)?

Copy link
Contributor

Choose a reason for hiding this comment

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

yes should add another column

resolver_CD = dns.resolver.Resolver(configure=False)
resolver_CD.nameservers = settings.RESOLVERS
resolver_CD.flags = (
(resolver_CD.flags or 0) | dns.flags.CD | dns.flags.AD | dns.flags.RD
Copy link
Member Author

Choose a reason for hiding this comment

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

I have not succeeded in getting an AD bit in a response when querying with CD, even when also querying with AD. (See RFC 6840 Section 5.7-5.9, and also my comment below in update_dns_security_status().)

As a result, I'm not sure if this is a meaningful combination of query flags.

Copy link
Contributor

Choose a reason for hiding this comment

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

Flags should instruct resolve to answer with DNSSEC records but omit checking

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.

3 participants