dnsmasq
dnsmasq
¶
Per-container dnsmasq config generation, reload, and domain management.
dnsmasq runs inside the container's network namespace (via nsenter)
on a runtime-dependent listen address — 127.0.0.1:53 for ordinary
runtimes that share the netns loopback, a link-local address under
krun whose guest can't reach netns 127.0.0.1. --nftset
auto-populates nft allow sets on every DNS resolution to handle IP
rotation that static pre-start resolution cannot.
This module is the single package-side owner of dnsmasq config format
and CLI args; the per-container start/stop dance is owned by the OCI
hook resource (resources/nft_hook.py), which has its own stdlib-
only copy because hook scripts run outside the package venv.
logger = logging.getLogger(__name__)
module-attribute
¶
reload(state_dir, upstream_dns, domains)
¶
Regenerate dnsmasq config and signal the daemon to reload.
Sends SIGHUP to the running dnsmasq, which re-reads its config file. No-op if dnsmasq is not running (PID file absent).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state_dir
|
Path
|
Per-container state directory. |
required |
upstream_dns
|
str
|
Upstream DNS forwarder address. |
required |
domains
|
list[str]
|
Updated domain names for nftset auto-population. |
required |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If dnsmasq PID exists but the process is gone (stale PID). The caller should log this — it means the container's DNS is broken. |
Source code in src/terok_shield/dns/dnsmasq.py
add_domain(state_dir, domain)
¶
Append a domain to the live.domains file.
Writes to live.domains (not profile.domains) so that
runtime additions survive container restarts without overwriting
the profile-derived domain list.
Returns True if the domain was added, False if already present in the merged domain set (profile + live - denied).
Source code in src/terok_shield/dns/dnsmasq.py
remove_domain(state_dir, domain)
¶
Remove a domain by adding it to the denied.domains file.
Writes to denied.domains so the denial persists across
dnsmasq reloads. Also removes from live.domains if present.
Returns True if the domain was removed, False if not found in the merged domain set.
Source code in src/terok_shield/dns/dnsmasq.py
read_domains(domains_path)
¶
Read and normalize domain names from a domains file.
Validates and lowercases each entry so comparisons with
add_domain()/remove_domain() are consistent.
Invalid entries are silently skipped.
Source code in src/terok_shield/dns/dnsmasq.py
read_merged_domains(state_dir)
¶
Compute effective domains: (profile + live) - denied.
Returns a deduplicated, stable-order list.
Source code in src/terok_shield/dns/dnsmasq.py
generate_config(upstream_dns, domains, pid_path, *, listen_address, log_path=None)
¶
Generate a complete dnsmasq configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
upstream_dns
|
str
|
Upstream DNS forwarder (pasta or slirp4netns address). |
required |
domains
|
list[str]
|
Domain names for |
required |
pid_path
|
Path
|
Path for the dnsmasq PID file. |
required |
listen_address
|
str
|
Address dnsmasq binds to inside the netns. See
|
required |
log_path
|
Path | None
|
If set, enable query logging to this file (for |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If upstream_dns or listen_address is not a valid IP address. |
Source code in src/terok_shield/dns/dnsmasq.py
nftset_entry(domain)
¶
Generate a dnsmasq nftset config line for a domain.
Maps A records to the IPv4 allow set and AAAA records to the IPv6 allow set. dnsmasq automatically matches the domain and all its subdomains.
Example::
nftset=/github.com/4#inet#terok_shield#allow_v4,6#inet#terok_shield#allow_v6
Source code in src/terok_shield/dns/dnsmasq.py
has_nftset_support(runner)
¶
Return True if the installed dnsmasq supports --nftset.
Parses dnsmasq --version compile-time options for the nftset
feature flag. Returns False if dnsmasq is not installed or its
output contains no-nftset (explicitly disabled).