Also exclude the dns servers from the tunnel

This commit is contained in:
Owen
2026-09-28 12:21:56 -04:00
parent 9ef3399e8f
commit 8d58df58e6
4 changed files with 82 additions and 0 deletions
+1
View File
@@ -266,6 +266,7 @@ func (o *Olm) handleConnect(msg websocket.WSMessage) {
// "olm/wg/connect" message - so push in whatever hole-punch bypass
// endpoints it already recorded now that there's somewhere to put them.
o.flushPendingHolepunchBypassEndpoints()
o.flushPendingDNSBypassEndpoints()
if o.dnsProxy != nil {
if err := o.dnsProxy.Start(); err != nil { // start DNS proxy first so there is no downtime
+1
View File
@@ -26,6 +26,7 @@ func (o *Olm) applyDNSConfigUpdate(cfg DNSConfigUpdate) {
if o.dnsProxy != nil {
o.dnsProxy.SetUpstreamDNS(cfg.UpstreamDNS)
}
o.updateDNSBypassEndpoints(cfg.UpstreamDNS)
}
if len(cfg.MatchDomains) > 0 {
+60
View File
@@ -214,6 +214,66 @@ func (o *Olm) flushPendingHolepunchBypassEndpoints() {
}
}
// updateDNSBypassEndpoints diffs servers (the DNS proxy's upstream/primary
// and secondary DNS servers - see TunnelConfig.UpstreamDNS and
// dns.DNSProxy.SetUpstreamDNS) against the currently-registered set and
// adds/removes gateway bypass routes for the difference, via the same
// AddGatewayBypassEndpoint/RemoveGatewayBypassEndpoint machinery used for
// hole-punch endpoints above. This keeps the DNS proxy's own outbound queries
// to its real upstream resolvers off the gateway default-route-equivalent, so
// they reach the real servers directly instead of looping back through the
// tunnel. Called from StartTunnel (initial value and dynamic system-DNS
// updates, including SetSystemDNS pushes) and applyDNSConfigUpdate (live
// server-pushed overrides). Safe to call before the peer manager exists (see
// flushPendingDNSBypassEndpoints) and safe to call repeatedly with the same
// servers (no-op).
func (o *Olm) updateDNSBypassEndpoints(servers []string) {
pm := o.getPeerManager()
newBypassEndpoints := make(map[string]bool, len(servers))
for _, server := range servers {
newBypassEndpoints[server] = true
}
o.dnsBypassMu.Lock()
defer o.dnsBypassMu.Unlock()
if pm != nil {
for server := range newBypassEndpoints {
if !o.dnsBypassEndpoints[server] {
pm.AddGatewayBypassEndpoint(server)
}
}
for server := range o.dnsBypassEndpoints {
if !newBypassEndpoints[server] {
pm.RemoveGatewayBypassEndpoint(server)
}
}
}
o.dnsBypassEndpoints = newBypassEndpoints
}
// flushPendingDNSBypassEndpoints re-registers every currently-known upstream
// DNS bypass endpoint with the peer manager. Mirrors
// flushPendingHolepunchBypassEndpoints: updateDNSBypassEndpoints typically
// runs before the peer manager exists (the initial UpstreamDNS value is
// applied in StartTunnel, and a DNS config override may arrive at the very
// start of handleConnect - see olm/dns_config.go - both well before
// handleConnect constructs the peer manager further down), so whatever was
// recorded needs to be pushed in once it becomes available.
// AddGatewayBypassEndpoint is idempotent.
func (o *Olm) flushPendingDNSBypassEndpoints() {
pm := o.getPeerManager()
if pm == nil {
return
}
o.dnsBypassMu.Lock()
defer o.dnsBypassMu.Unlock()
for server := range o.dnsBypassEndpoints {
pm.AddGatewayBypassEndpoint(server)
}
}
// extractControlEndpointHost returns the bare host (no scheme/port) of the
// Pangolin server olm is registered against, for gateway bypass-route
// purposes. Falls back to the raw endpoint string on parse failure -
+20
View File
@@ -81,6 +81,18 @@ type Olm struct {
hpBypassEndpoints map[string]bool
hpBypassMu sync.Mutex
// dnsBypassEndpoints tracks the "host:port" upstream DNS servers (see
// TunnelConfig.UpstreamDNS / dns.DNSProxy's upstreamDNS - the DNS proxy's
// primary/secondary real resolvers) currently registered as gateway bypass
// targets, so the proxy's own outbound DNS queries aren't captured by the
// gateway default-route-equivalent and end up looping back through the
// tunnel. Diffed against every update - the initial value in StartTunnel, a
// live server-pushed DNS config override, or system DNS detection/
// SetSystemDNS - so stale entries are unregistered and new ones protected
// while gateway mode is active. Mirrors hpBypassEndpoints.
dnsBypassEndpoints map[string]bool
dnsBypassMu sync.Mutex
// primaryTunnelIP is the site tunnel's own address (wgData.TunnelIP), set once
// per connect in handleConnect. It's the interface's first/primary address -
// on macOS/iOS NetworkExtension, an unbound outbound socket's source gets
@@ -526,6 +538,7 @@ func (o *Olm) StartTunnel(config TunnelConfig) {
if o.dnsProxy != nil {
o.dnsProxy.SetUpstreamDNS(servers)
}
o.updateDNSBypassEndpoints(servers)
} else {
logger.Debug("Not updating UpstreamDNS: statically configured to %v", config.UpstreamDNS)
}
@@ -549,6 +562,13 @@ func (o *Olm) StartTunnel(config TunnelConfig) {
if len(o.tunnelConfig.UpstreamDNS) == 0 {
o.tunnelConfig.UpstreamDNS = []string{"8.8.8.8:53"}
}
// Register the startup upstream DNS servers as gateway bypass targets
// regardless of how they were determined (statically configured, defaulted
// above, or already applied from a dynamic detection callback a moment
// ago) - the peer manager doesn't exist yet at this point, so this only
// records intent; flushPendingDNSBypassEndpoints (called from
// handleConnect) pushes it in once the peer manager is created.
o.updateDNSBypassEndpoints(o.tunnelConfig.UpstreamDNS)
// Reset terminated status when tunnel starts
o.apiServer.SetTerminated(false)