Skip to content

Commit

Permalink
fix: validation of domain sets domains
Browse files Browse the repository at this point in the history
Ensure the total number of characters a domain set uses in the
dnsmasq configuration file does not exceed 1024 characters.

NethServer/nethsecurity#877
  • Loading branch information
andre8244 committed Nov 8, 2024
1 parent 9d1188e commit 375aac4
Showing 1 changed file with 38 additions and 4 deletions.
42 changes: 38 additions & 4 deletions src/nethsec/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,28 @@ def get_domain_set_ipsets(uci, id):
break
return ipsets

def get_domain_set_total_chars(name: str, domains: list[str]):
"""
Calculate the total number of characters the given domain set will use in the dnsmasq configuration file.
This file supports a maximum of 1024 characters per line.
Example syntax of a domain set in dnsmasq configuration file:
nftset=/example1.org/example2.org/example3.org/.../4#inet#fw4#domainSetName
Args:
name: name of domain set
domains: a list of valid DNS names
"""
# remove duplicated domains
unique_domains = list(dict.fromkeys(domains))
total_chars = 8 + 11 # leading "nftset=/" and trailing "4#inet#fw4#"

for domain in unique_domains:
total_chars += len(domain) + 1 # +1 for trailing "/"

total_chars += len(name) # domain set name
return total_chars

def add_domain_set(uci, name: str, family: str, domains: list[str], timeout: int = 660) -> str:
"""
Add domain set to objects config.
Expand All @@ -241,19 +263,25 @@ def add_domain_set(uci, name: str, family: str, domains: list[str], timeout: int
raise utils.ValidationError('family', 'invalid_family', family)
if timeout < 0:
raise utils.ValidationError('timeout', 'invalid_timeout', timeout)
if get_domain_set_total_chars(name, domains) > 1024 - 10: # -10 chars for safety
raise utils.ValidationError('domain', 'total_chars_exceeded')

# remove duplicated domains
unique_domains = list(dict.fromkeys(domains))

id = utils.get_random_id()
uci.set('objects', id, 'domain')
uci.set('objects', id, 'name', name)
uci.set('objects', id, 'family', family)
uci.set('objects', id, 'timeout', timeout)
uci.set('objects', id, 'domain', domains)
uci.set('objects', id, 'domain', unique_domains)
uci.save('objects')

# create ipset inside dhcp config
ipset = utils.get_random_id()
uci.set('dhcp', ipset, 'ipset')
uci.set('dhcp', ipset, 'name', [name])
uci.set('dhcp', ipset, 'domain', domains)
uci.set('dhcp', ipset, 'domain', unique_domains)
uci.set('dhcp', ipset, 'table_family', 'inet')
uci.set('dhcp', ipset, 'ns_link', f'objects/{id}')
uci.save('dhcp')
Expand Down Expand Up @@ -293,17 +321,23 @@ def edit_domain_set(uci, id: str, name: str, family: str, domains: list[str], ti
raise utils.ValidationError('family', 'invalid_family', family)
if timeout < 0:
raise utils.ValidationError('timeout', 'invalid_timeout', timeout)
if get_domain_set_total_chars(name, domains) > 1024 - 10: # -10 chars for safety
raise utils.ValidationError('domain', 'total_chars_exceeded')

# remove duplicated domains
unique_domains = list(dict.fromkeys(domains))

uci.set('objects', id, 'name', name)
uci.set('objects', id, 'family', family)
uci.set('objects', id, 'timeout', timeout)
uci.set('objects', id, 'domain', domains)
uci.set('objects', id, 'domain', unique_domains)
uci.save('objects')

# update ipset inside dhcp config
for section in uci.get_all("dhcp"):
if uci.get('dhcp', section, 'ns_link', default=None) == f'objects/{id}':
uci.set('dhcp', section, 'name', [name])
uci.set('dhcp', section, 'domain', domains)
uci.set('dhcp', section, 'domain', unique_domains)
uci.set('dhcp', section, 'ns_tag', ['automated'])
uci.save('dhcp')
break
Expand Down

0 comments on commit 375aac4

Please sign in to comment.