class EdnsBufferSizeSchema(SchemaNode):
+ """
+ EDNS payload size advertised in DNS packets.
+
+ ---
+ upstream: Maximum EDNS upstream (towards other DNS servers) payload size.
+ downstream: Maximum EDNS downstream (towards clients) payload size for communication.
+ """
+
upstream: SizeUnit = SizeUnit("1232B")
downstream: SizeUnit = SizeUnit("1232B")
class AddressRenumberingSchema(SchemaNode):
+ """
+ Renumbers addresses in answers to different address space.
+
+ ---
+ source: Source subnet.
+ destination: Destination address prefix.
+ """
+
source: IPNetwork
destination: IPAddress
class TLSSchema(SchemaNode):
+ """
+ TLS configuration, also affects DNS over TLS and DNS over HTTPS.
+
+ ---
+ cert_file: Path to certificate file.
+ key_file: Path to certificate key file.
+ sticket_secret: Secret for TLS session resumption via tickets. (RFC 5077).
+ sticket_secret_file: Path to file with secret for TLS session resumption via tickets. (RFC 5077).
+ auto_discovery: Automatic discovery of authoritative servers supporting DNS-over-TLS.
+ padding: EDNS(0) padding of answers to queries that arrive over TLS transport.
+ """
+
cert_file: Optional[CheckedPath] = None
key_file: Optional[CheckedPath] = None
sticket_secret: Optional[str] = None
class ListenSchema(SchemaNode):
class Raw(SchemaNode):
+ """
+ Configuration of listening interface.
+
+ ---
+ unix_socket: Path to unix domain socket to listen to.
+ interface: IP address or interface name with optional port number to listen to.
+ port: Port number to listen to.
+ kind: Specifies DNS query transport protocol.
+ freebind: Used for binding to non-local address.
+ """
+
interface: Union[None, InterfaceOptionalPort, List[InterfaceOptionalPort]] = None
unix_socket: Union[None, CheckedPath, List[CheckedPath]] = None
port: Optional[PortNumber] = None
class NetworkSchema(SchemaNode):
+ """
+ Network connections and protocols configuration.
+
+ ---
+ do_ipv4: Enable/disable using IPv4 for contacting upstream nameservers.
+ do_ipv6: Enable/disable using IPv6 for contacting upstream nameservers.
+ out_interface_v4: IPv4 address used to perform queries. Not set by default, which lets the OS choose any address.
+ out_interface_v6: IPv6 address used to perform queries. Not set by default, which lets the OS choose any address.
+ tcp_pipeline: TCP pipeline limit. The number of outstanding queries that a single client connection can make in parallel.
+ edns_tcp_keepalive: Allows clients to discover the connection timeout. (RFC 7828)
+ edns_buffer_size: Maximum EDNS payload size advertised in DNS packets. Different values can be configured for communication downstream (towards clients) and upstream (towards other DNS servers).
+ address_renumbering: Renumbers addresses in answers to different address space.
+ tls: TLS configuration, also affects DNS over TLS and DNS over HTTPS.
+ listen: List of interfaces to listen to and its configuration.
+ """
+
do_ipv4: bool = True
do_ipv6: bool = True
out_interface_v4: Optional[IPv4Address] = None
out_interface_v6: Optional[IPv6Address] = None
tcp_pipeline: int = 100
- edns_keep_alive: bool = True
+ edns_tcp_keepalive: bool = True
edns_buffer_size: EdnsBufferSizeSchema = EdnsBufferSizeSchema()
address_renumbering: Optional[List[AddressRenumberingSchema]] = None
tls: TLSSchema = TLSSchema()
class FilterSchema(SchemaNode):
+ """
+ Query filtering configuration.
+
+ ---
+ suffix: Filter based on the suffix of the query name.
+ pattern: Filter based on the pattern that match query name.
+ qtype: Filter based on the DNS query type.
+ """
+
suffix: Optional[str] = None
pattern: Optional[str] = None
qtype: Optional[DNSRecordTypeEnum] = None
class AnswerSchema(SchemaNode):
- qtype: DNSRecordTypeEnum
+ """
+ Configuration of custom resource record for DNS answer.
+
+ ---
+ rtype: Type of DNS resource record.
+ rdata: Data of DNS resource record.
+ ttl: Time-to-live value for defined answer.
+ nodata: Answer with NODATA If requested type is not configured in the answer. Otherwise policy rule is ignored.
+ """
+
+ rtype: DNSRecordTypeEnum
rdata: str
ttl: TimeUnit = TimeUnit("1s")
nodata: bool = False
class PolicySchema(SchemaNode):
+ """
+ Configuration of policy rule.
+
+ ---
+ action: Policy rule action.
+ priority: Policy rule priority.
+ filter: Query filtering configuration.
+ views: Use policy rule only for clients defined by views.
+ options: Configuration flags for policy rule.
+ message: Deny message for 'deny' action.
+ reroute: Configuration for 'reroute' action.
+ answer: Answer definition for 'answer' action.
+ mirror: Mirroring parameters for 'mirror' action.
+ """
+
action: PolicyActionEnum
- order: Optional[int] = None
+ priority: Optional[int] = None
filter: Optional[FilterSchema] = None
views: Optional[List[str]] = None
options: Optional[List[PolicyFlagEnum]] = None
class StaticHintsSchema(SchemaNode):
+ """
+ Static hints for forward records (A/AAAA) and reverse records (PTR)
+
+ ---
+ ttl: TTL value used for records added from static hints.
+ nodata: Use NODATA synthesis. NODATA will be synthesised for matching hint name, but mismatching type.
+ etc_hosts: Add hints from '/etc/hosts' file.
+ root_hints: Direct addition of root hints pairs (hostname, list of addresses).
+ root_hints_file: Path to root hints in zonefile. Replaces all current root hints.
+ hints: Direct addition of hints pairs (hostname, list of addresses).
+ hints_files: Path to hints in hosts-like file.
+ """
+
ttl: Optional[TimeUnit] = None
- no_data: bool = True
+ nodata: bool = True
etc_hosts: bool = False
- root_hints_file: Optional[CheckedPath] = None
- hints_files: Optional[List[CheckedPath]] = None
root_hints: Optional[Dict[DomainName, List[IPAddress]]] = None
+ root_hints_file: Optional[CheckedPath] = None
hints: Optional[Dict[DomainName, List[IPAddress]]] = None
+ hints_files: Optional[List[CheckedPath]] = None
{%- endmacro %}
{% macro policy_answer(answer) -%}
-policy.ANSWER({[kres.type.{{ answer.qtype }}]={rdata=
-{%- if answer.qtype in ['A','AAAA'] -%}
+policy.ANSWER({[kres.type.{{ answer.rtype }}]={rdata=
+{%- if answer.rtype in ['A','AAAA'] -%}
{{ str2ip_table(answer.rdata) }},
-{%- elif answer.qtype == '' -%}
+{%- elif answer.rtype == '' -%}
{# TODO: Do the same for other record types that require a special rdata type in Lua.
By default, the raw string from config is used. #}
{%- else -%}
net.tcp_pipeline({{ cfg.network.tcp_pipeline }})
-- network.edns-keep-alive
-{% if cfg.network.edns_keep_alive %}
+{% if cfg.network.edns_tcp_keepalive %}
modules.load('edns_keepalive')
{% else %}
modules.unload('edns_keepalive')
{% endif %}
-- network.tls.padding
-net.tls_padding({{ cfg.network.tls.padding }})
+net.tls_padding(
+{%- if cfg.network.tls.padding == true -%}
+true
+{%- elif cfg.network.tls.padding == false -%}
+false
+{%- else -%}
+{{ cfg.network.tls.padding }}
+{%- endif -%}
+)
{% if cfg.network.address_renumbering %}
-- network.address-renumbering
{% endif %}
-- static-hints.no-data
-hints.use_nodata({{ 'true' if cfg.static_hints.no_data else 'false' }})
+hints.use_nodata({{ 'true' if cfg.static_hints.nodata else 'false' }})
{% if cfg.static_hints.etc_hosts %}
-- static-hints.etc-hosts
class ViewSchema(SchemaNode):
"""
- Configuration parameters that allows you to create personalized policy rules and other.
+ Configuration parameters that allow you to create personalized policy rules and other.
---
- subnets: Identifies clients based on subnets.
- tsig: Identifies clients based on a TSIG key name. This is only for testing purposes, TSIG signature is not verified!
- options: List of flags for clients specified in view.
+ subnets: Identifies the client based on his subnet.
+ tsig: Identifies the client based on a TSIG key name (for testing purposes, TSIG signature is not verified!).
+ options: Configuration flags for clients identified by the view.
"""
subnets: Optional[List[IPNetwork]] = None
def test_policy_answer():
- ans = AnswerSchema({"qtype": "AAAA", "rdata": "192.0.2.7"})
+ ans = AnswerSchema({"rtype": "AAAA", "rdata": "192.0.2.7"})
tmpl_str = """{% from 'macros/policy_macros.lua.j2' import policy_answer %}
{{ policy_answer(ans) }}"""
tmpl = template_from_str(tmpl_str)
assert (
tmpl.render(ans=ans)
- == f"policy.ANSWER({{[kres.type.{ans.qtype}]={{rdata=kres.str2ip('{ans.rdata}'),ttl={ans.ttl.seconds()}}}}},{str(ans.nodata).lower()})"
+ == f"policy.ANSWER({{[kres.type.{ans.rtype}]={{rdata=kres.str2ip('{ans.rdata}'),ttl={ans.ttl.seconds()}}}}},{str(ans.nodata).lower()})"
)
def test_answer():
- assert PolicySchema({"action": "answer", "answer": {"qtype": "AAAA", "rdata": "::1"}})
+ assert PolicySchema({"action": "answer", "answer": {"rtype": "AAAA", "rdata": "::1"}})
with raises(KresManagerException):
PolicySchema({"action": "answer"})
with raises(KresManagerException):
- PolicySchema({"action": "pass", "answer": {"qtype": "AAAA", "rdata": "::1"}})
+ PolicySchema({"action": "pass", "answer": {"rtype": "AAAA", "rdata": "::1"}})
def test_mirror():