From 99d249db2df062dfca1ebbe68de3c1b964352e8f Mon Sep 17 00:00:00 2001 From: Owen Date: Sat, 18 Jul 2026 17:28:56 -0400 Subject: [PATCH] add PreferLocalRoutes option --- config.go | 66 ++++++++++++++++++++++++++++++++-------------------- main.go | 1 + olm/olm.go | 1 + olm/types.go | 8 +++++++ 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/config.go b/config.go index edebfe7..44b030c 100644 --- a/config.go +++ b/config.go @@ -47,11 +47,12 @@ type OlmConfig struct { PingTimeout string `json:"pingTimeout"` // Advanced - DisableHolepunch bool `json:"disableHolepunch"` - TlsClientCert string `json:"tlsClientCert"` - OverrideDNS bool `json:"overrideDNS"` - TunnelDNS bool `json:"tunnelDNS"` - DisableRelay bool `json:"disableRelay"` + DisableHolepunch bool `json:"disableHolepunch"` + TlsClientCert string `json:"tlsClientCert"` + OverrideDNS bool `json:"overrideDNS"` + TunnelDNS bool `json:"tunnelDNS"` + DisableRelay bool `json:"disableRelay"` + PreferLocalRoutes bool `json:"preferLocalRoutes"` // DoNotCreateNewClient bool `json:"doNotCreateNewClient"` // Parsed values (not in JSON) @@ -118,6 +119,7 @@ func DefaultConfig() *OlmConfig { config.sources["overrideDNS"] = string(SourceDefault) config.sources["tunnelDNS"] = string(SourceDefault) config.sources["disableRelay"] = string(SourceDefault) + config.sources["preferLocalRoutes"] = string(SourceDefault) // config.sources["doNotCreateNewClient"] = string(SourceDefault) return config @@ -281,6 +283,10 @@ func loadConfigFromEnv(config *OlmConfig) { config.DisableRelay = true config.sources["disableRelay"] = string(SourceEnv) } + if val := os.Getenv("PREFER_LOCAL_ROUTES"); val == "true" { + config.PreferLocalRoutes = true + config.sources["preferLocalRoutes"] = string(SourceEnv) + } if val := os.Getenv("TUNNEL_DNS"); val == "true" { config.TunnelDNS = true config.sources["tunnelDNS"] = string(SourceEnv) @@ -297,26 +303,27 @@ func loadConfigFromCLI(config *OlmConfig, args []string) (bool, bool, error) { // Store original values to detect changes origValues := map[string]interface{}{ - "endpoint": config.Endpoint, - "id": config.ID, - "secret": config.Secret, - "org": config.OrgID, - "userToken": config.UserToken, - "mtu": config.MTU, - "dns": config.DNS, - "upstreamDNS": fmt.Sprintf("%v", config.UpstreamDNS), - "matchDomains": fmt.Sprintf("%v", config.MatchDomains), - "logLevel": config.LogLevel, - "interface": config.InterfaceName, - "httpAddr": config.HTTPAddr, - "socketPath": config.SocketPath, - "pingInterval": config.PingInterval, - "pingTimeout": config.PingTimeout, - "enableApi": config.EnableAPI, - "disableHolepunch": config.DisableHolepunch, - "overrideDNS": config.OverrideDNS, - "disableRelay": config.DisableRelay, - "tunnelDNS": config.TunnelDNS, + "endpoint": config.Endpoint, + "id": config.ID, + "secret": config.Secret, + "org": config.OrgID, + "userToken": config.UserToken, + "mtu": config.MTU, + "dns": config.DNS, + "upstreamDNS": fmt.Sprintf("%v", config.UpstreamDNS), + "matchDomains": fmt.Sprintf("%v", config.MatchDomains), + "logLevel": config.LogLevel, + "interface": config.InterfaceName, + "httpAddr": config.HTTPAddr, + "socketPath": config.SocketPath, + "pingInterval": config.PingInterval, + "pingTimeout": config.PingTimeout, + "enableApi": config.EnableAPI, + "disableHolepunch": config.DisableHolepunch, + "overrideDNS": config.OverrideDNS, + "disableRelay": config.DisableRelay, + "preferLocalRoutes": config.PreferLocalRoutes, + "tunnelDNS": config.TunnelDNS, // "doNotCreateNewClient": config.DoNotCreateNewClient, } @@ -342,6 +349,7 @@ func loadConfigFromCLI(config *OlmConfig, args []string) (bool, bool, error) { serviceFlags.BoolVar(&config.DisableHolepunch, "disable-holepunch", config.DisableHolepunch, "Disable hole punching") serviceFlags.BoolVar(&config.OverrideDNS, "override-dns", config.OverrideDNS, "When enabled, the client uses custom DNS servers to resolve internal resources and aliases. This overrides your system's default DNS settings. Queries that cannot be resolved as a Pangolin resource will be forwarded to your configured Upstream DNS Server. (default false)") serviceFlags.BoolVar(&config.DisableRelay, "disable-relay", config.DisableRelay, "Disable relay connections") + serviceFlags.BoolVar(&config.PreferLocalRoutes, "prefer-local-routes", config.PreferLocalRoutes, "Add tunnel routes with a high metric so overlapping local/connected routes take precedence (default false)") serviceFlags.BoolVar(&config.TunnelDNS, "tunnel-dns", config.TunnelDNS, "When enabled, DNS queries are routed through the tunnel for remote resolution. To ensure queries are tunneled correctly, you must define the DNS server as a Pangolin resource and enter its address as an Upstream DNS Server. (default false)") // serviceFlags.BoolVar(&config.DoNotCreateNewClient, "do-not-create-new-client", config.DoNotCreateNewClient, "Do not create new client") @@ -426,6 +434,9 @@ func loadConfigFromCLI(config *OlmConfig, args []string) (bool, bool, error) { if config.DisableRelay != origValues["disableRelay"].(bool) { config.sources["disableRelay"] = string(SourceCLI) } + if config.PreferLocalRoutes != origValues["preferLocalRoutes"].(bool) { + config.sources["preferLocalRoutes"] = string(SourceCLI) + } if config.TunnelDNS != origValues["tunnelDNS"].(bool) { config.sources["tunnelDNS"] = string(SourceCLI) } @@ -557,6 +568,10 @@ func mergeConfigs(dest, src *OlmConfig) { dest.DisableRelay = src.DisableRelay dest.sources["disableRelay"] = string(SourceFile) } + if src.PreferLocalRoutes { + dest.PreferLocalRoutes = src.PreferLocalRoutes + dest.sources["preferLocalRoutes"] = string(SourceFile) + } // if src.DoNotCreateNewClient { // dest.DoNotCreateNewClient = src.DoNotCreateNewClient // dest.sources["doNotCreateNewClient"] = string(SourceFile) @@ -649,6 +664,7 @@ func (c *OlmConfig) ShowConfig() { fmt.Printf(" override-dns = %v [%s]\n", c.OverrideDNS, getSource("overrideDNS")) fmt.Printf(" tunnel-dns = %v [%s]\n", c.TunnelDNS, getSource("tunnelDNS")) fmt.Printf(" disable-relay = %v [%s]\n", c.DisableRelay, getSource("disableRelay")) + fmt.Printf(" prefer-local-routes = %v [%s]\n", c.PreferLocalRoutes, getSource("preferLocalRoutes")) // fmt.Printf(" do-not-create-new-client = %v [%s]\n", c.DoNotCreateNewClient, getSource("doNotCreateNewClient")) if c.TlsClientCert != "" { fmt.Printf(" tls-cert = %s [%s]\n", c.TlsClientCert, getSource("tlsClientCert")) diff --git a/main.go b/main.go index ddd4876..060dc79 100644 --- a/main.go +++ b/main.go @@ -272,6 +272,7 @@ func runOlmMainWithArgs(ctx context.Context, cancel context.CancelFunc, signalCt OrgID: config.OrgID, OverrideDNS: config.OverrideDNS, DisableRelay: config.DisableRelay, + PreferLocalRoutes: config.PreferLocalRoutes, EnableUAPI: true, } go olm.StartTunnel(tunnelConfig) diff --git a/olm/olm.go b/olm/olm.go index c30c17d..fa638be 100644 --- a/olm/olm.go +++ b/olm/olm.go @@ -398,6 +398,7 @@ func (o *Olm) StartTunnel(config TunnelConfig) { o.tunnelRunning = true // Also set it here in case it is called externally o.tunnelConfig = config + network.PreferLocalRoutes = config.PreferLocalRoutes // Determine whether the system DNS monitor should also manage UpstreamDNS. // If the caller did not provide an explicit UpstreamDNS (it was defaulted to diff --git a/olm/types.go b/olm/types.go index 56c7aac..b5b3a5c 100644 --- a/olm/types.go +++ b/olm/types.go @@ -109,4 +109,12 @@ type TunnelConfig struct { InitialPostures map[string]any DisableRelay bool + + // PreferLocalRoutes, when enabled, adds tunnel routes with an explicit + // high metric/priority so that an overlapping local/connected route to + // the same destination always takes precedence over the VPN route, + // rather than the two racing based on insertion order. Defaults to + // false, preserving the routing behavior from before this option was + // introduced. + PreferLocalRoutes bool }