dns/dnscrypt-proxy: listen_addresses CSVListField does not support IPv6 bracket notation, preventing [::]:53 from being saved correctly
incomplete
## Summary
The listen_addresses field in the DNSCrypt-proxy plugin uses CSVListField with no
validation mask. The tokenize UI widget corrupts IPv6 bracket notation (e.g. [::]:53)
when saving, causing DNSCrypt-proxy to either fail to start or fall back to [::1]:53
(loopback only) instead of binding to all IPv6 interfaces.
## Environment
- OPNsense 25.x
- os-dnscrypt-proxy plugin
- DNSCrypt-proxy 2.1.15
## Problem
On dual-stack networks, OPNsense advertises the router's LAN IPv6 address as the DNS
server via DHCPv6/RA. Clients send DNS queries to that IPv6 address. DNSCrypt-proxy
must listen on [::]:53 to handle these queries.
Attempting to enter [::]:53 via the UI results in DNSCrypt-proxy failing to start.
The value is either mangled by the tokenize widget or rejected before reaching the
template, leaving only [::1]:53 (loopback) in the rendered TOML.
## Root Cause
In General.xml, listen_addresses is defined as:
<listen_addresses type="CSVListField">
<Default>0.0.0.0:5353</Default>
<Required>Y</Required>
</listen_addresses>
CSVListField has no Mask validator and no awareness of IPv6 address format. The
tokenize widget in general.xml renders the field as select_multiple with allownew=true,
which does not handle bracket notation in IPv6 addresses correctly when serializing
back to config.xml.
Additionally, the Jinja2 template renders listen_addresses with a naive comma-split:
listen_addresses = [{{ "'" + ("','".join(
OPNsense.dnscryptproxy.general.listen_addresses.split(','))) + "'" }}]
This works for IPv4 but produces malformed TOML for IPv6 addresses containing colons
and brackets.
## Proposed Fix
Two changes are needed:
**1. General.xml — add a Mask that accepts both IPv4 and IPv6 address:port format:**
<listen_addresses type="CSVListField">
<Default>0.0.0.0:5353</Default>
<Required>Y</Required>
<Mask>/^(\[?[0-9a-fA-F:\.]+\]?:[0-9]{1,5})(,\[?[0-9a-fA-F:\.]+\]?:[0-9]{1,5})*$/</Mask>
<ValidationMessage>Please enter valid address:port combinations,
e.g. 0.0.0.0:53 or [::]:53</ValidationMessage>
</listen_addresses>
**2. dnscrypt-proxy.toml template — handle IPv6 entries correctly when rendering:**
The current naive split on ',' is ambiguous for IPv6. Since IPv6 addresses in
bracket notation do not contain commas, the split is technically safe, but the
template should be updated to make this explicit and ensure bracket notation is
preserved verbatim:
listen_addresses = [{{ "'" + ("','".join(
OPNsense.dnscryptproxy.general.listen_addresses.split(','))) + "'" }}]
No change needed here if the model correctly stores and retrieves the bracket
notation — the split-on-comma approach works as long as the value is not corrupted
upstream. The primary fix is in the model/UI layer.
## Workaround
Until fixed, manually append '[::]:53' in the template at:
/usr/local/opnsense/service/templates/OPNsense/Dnscryptproxy/dnscrypt-proxy.toml
Change:
listen_addresses = [{{ "'" + ("','".join(
OPNsense.dnscryptproxy.general.listen_addresses.split(','))) + "'" }}]
To:
listen_addresses = [{{ "'" + ("','".join(
OPNsense.dnscryptproxy.general.listen_addresses.split(','))) + "'" }}, '[::]:53']
Then reload and restart:
configctl template reload OPNsense/Dnscryptproxy
service dnscrypt-proxy restart
关闭于 2026-06-07 2 条评论