You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hey @jedisct1 - saw an old feature request (#1437) that got closed but never implemented, and I think it would be useful for cases where dnscrypt-proxy runs in a complex networking environment like a firewall (pfSense, for example). If it landed, I could wire the option into my DNSCrypt pfSense package.
On a box with more than one path out (multi-WAN, or a VPN tunnel alongside the regular WAN), there's currently no way to tell dnscrypt-proxy which one to use for upstream queries. It binds to whatever source the OS picks by default, so you can't say "send my encrypted DNS out the VPN" or "pin it to the secondary WAN." The ask in #1437 was a way to control the source address for outgoing queries so a multi-homed box doesn't just grab the default interface.
I went and looked at how outbound dialing works today to see how invasive this would actually be, and it looks pretty contained.
Proposed config surface
Opt-in, with an empty default that keeps current behavior exactly:
# Source address for outgoing queries. Empty = OS default (current behavior).outgoing_address = ''# IPv4 source, e.g. '10.0.8.2'outgoing_address_v6 = ''# IPv6 source, e.g. 'fd00::2'
Two typed keys instead of the array from the original issue, because the source has to match the target's address family. With a single list you'd have to figure out at runtime which entry is v4 vs v6; two keys are unambiguous and self-documenting. Bind the IP only, port stays ephemeral, since pinning a source port would break connection reuse and the UDP pool.
Semantics (default path stays untouched)
Empty string -> LocalAddr stays nil everywhere -> identical to today, no new code path runs.
Set -> parse once at startup into a *net.UDPAddr / *net.TCPAddr (port 0), then at each dial site: if localAddr != nil, use it. A v4 target uses the v4 source, a v6 target uses the v6 source.
Partial config (only one family set) -> the other family falls back to default rather than erroring, with a startup log line.
If a proxy is configured the proxy owns egress, so the source bind is ignored there. Could warn at startup if both are set.
Non-empty but unparseable as an IP -> fatal config error, fail loud.
Outbound paths that need LocalAddr
From what I can tell:
plain DNSCrypt UDP (the UDP conn pool, currently net.DialUDP with a nil local)
plain DNSCrypt TCP (net.DialTimeout -> net.Dialer with LocalAddr)
DoH / ODoH over HTTP (the net.Dialer in the transport)
DoH3 / QUIC (the ListenUDP call)
So roughly four dial sites guarded by a nil check, plus config parsing and validation at load.
Why this matters on a multi-homed firewall
On a firewall the OS picks the egress interface by destination route, independent of source address. Binding a known source IP is the piece that lets the firewall selectively policy-route just the DNS traffic (match on that source, send it out a chosen WAN or VPN gateway). Without a stable source IP, dnscrypt's traffic is indistinguishable from everything else the box originates, so you can't steer it. The bind is what makes that control possible, which is the real reason it's valuable in this environment.
Offer
I'd be happy to take a crack at the PR if you'd entertain it, or if it's something you'd rather do yourself that's fine too. Let me know what you think.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Hey @jedisct1 - saw an old feature request (#1437) that got closed but never implemented, and I think it would be useful for cases where dnscrypt-proxy runs in a complex networking environment like a firewall (pfSense, for example). If it landed, I could wire the option into my DNSCrypt pfSense package.
On a box with more than one path out (multi-WAN, or a VPN tunnel alongside the regular WAN), there's currently no way to tell dnscrypt-proxy which one to use for upstream queries. It binds to whatever source the OS picks by default, so you can't say "send my encrypted DNS out the VPN" or "pin it to the secondary WAN." The ask in #1437 was a way to control the source address for outgoing queries so a multi-homed box doesn't just grab the default interface.
I went and looked at how outbound dialing works today to see how invasive this would actually be, and it looks pretty contained.
Proposed config surface
Opt-in, with an empty default that keeps current behavior exactly:
Two typed keys instead of the array from the original issue, because the source has to match the target's address family. With a single list you'd have to figure out at runtime which entry is v4 vs v6; two keys are unambiguous and self-documenting. Bind the IP only, port stays ephemeral, since pinning a source port would break connection reuse and the UDP pool.
Semantics (default path stays untouched)
LocalAddrstaysnileverywhere -> identical to today, no new code path runs.*net.UDPAddr/*net.TCPAddr(port 0), then at each dial site:if localAddr != nil, use it. A v4 target uses the v4 source, a v6 target uses the v6 source.Outbound paths that need
LocalAddrFrom what I can tell:
net.DialUDPwith anillocal)net.DialTimeout->net.DialerwithLocalAddr)net.Dialerin the transport)ListenUDPcall)So roughly four dial sites guarded by a nil check, plus config parsing and validation at load.
Why this matters on a multi-homed firewall
On a firewall the OS picks the egress interface by destination route, independent of source address. Binding a known source IP is the piece that lets the firewall selectively policy-route just the DNS traffic (match on that source, send it out a chosen WAN or VPN gateway). Without a stable source IP, dnscrypt's traffic is indistinguishable from everything else the box originates, so you can't steer it. The bind is what makes that control possible, which is the real reason it's valuable in this environment.
Offer
I'd be happy to take a crack at the PR if you'd entertain it, or if it's something you'd rather do yourself that's fine too. Let me know what you think.
Beta Was this translation helpful? Give feedback.
All reactions