From eb0dd6235e72e8093f720c765334ea9df78b0d6a Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Thu, 2 Feb 2023 13:56:30 +0700 Subject: [PATCH] cmd/ctrld: use NetworkManager to disable DNS manager Currently, ctrld force NetworkManager ignore auto DNS setup from DHCP per connection. This does not work well, because an interface can be attached to many connections. So if `ctrld` started with a connection, then user connect to new one, the DNS configured by ctrld will be override. Instead, we can force NetworkManager not to manage DNS by: - Using dns=none - Set systemd-resolved=false So NetworkManager won't attempt to send DNS setup to systemd-resolved, leaving what ctrld set as-is. --- cmd/ctrld/cli.go | 8 ++-- cmd/ctrld/network_manager.go | 85 ++++++++++++++++++++++++++++++++++++ cmd/ctrld/os_linux.go | 39 ----------------- cmd/ctrld/os_mac.go | 6 --- cmd/ctrld/os_windows.go | 6 --- cmd/ctrld/prog.go | 8 ++++ go.mod | 1 + go.sum | 1 + 8 files changed, 100 insertions(+), 54 deletions(-) create mode 100644 cmd/ctrld/network_manager.go diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index 3de242e..805db1a 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -228,7 +228,6 @@ func initCLI() { {s.Start, true}, } if doTasks(tasks) { - disableAutoDNS(iface) prog.setDNS() mainLog.Info().Msg("Service started") } @@ -260,7 +259,6 @@ func initCLI() { } initLogging() if doTasks([]task{{s.Stop, true}}) { - enableAutoDNS(iface) prog.resetDNS() mainLog.Info().Msg("Service stopped") } @@ -330,7 +328,6 @@ func initCLI() { } initLogging() if doTasks(tasks) { - enableAutoDNS(iface) prog.resetDNS() mainLog.Info().Msg("Service uninstalled") return @@ -551,6 +548,10 @@ func processCDFlags() { } if netIface, _ := netInterface(iface); netIface != nil { + if err := restoreNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not restore NetworkManager") + return + } logger.Debug().Str("iface", netIface.Name).Msg("Restoring DNS for interface") if err := resetDNS(netIface); err != nil { logger.Warn().Err(err).Msg("something went wrong while restoring DNS") @@ -558,6 +559,7 @@ func processCDFlags() { logger.Debug().Str("iface", netIface.Name).Msg("Restoring DNS successfully") } } + tasks := []task{{s.Uninstall, true}} if doTasks(tasks) { logger.Info().Msg("uninstalled service") diff --git a/cmd/ctrld/network_manager.go b/cmd/ctrld/network_manager.go new file mode 100644 index 0000000..670fe9c --- /dev/null +++ b/cmd/ctrld/network_manager.go @@ -0,0 +1,85 @@ +package main + +import ( + "context" + "os" + "path/filepath" + "runtime" + "time" + + "github.com/coreos/go-systemd/v22/dbus" +) + +const ( + nmConfDir = "/etc/NetworkManager/conf.d" + nmCtrldConfFilename = "99-ctrld.conf" + nmCtrldConfContent = `[main] +dns=none +systemd-resolved=false +` + nmSystemdUnitName = "NetworkManager.service" + systemdEnabledState = "enabled" +) + +var networkManagerCtrldConfFile = filepath.Join(nmConfDir, nmCtrldConfFilename) + +func setupNetworkManager() error { + if runtime.GOOS != "linux" { + mainLog.Debug().Msg("skipping NetworkManager setup, not on Linux") + return nil + } + if content, _ := os.ReadFile(nmCtrldConfContent); string(content) == nmCtrldConfContent { + mainLog.Debug().Msg("NetworkManager already setup, nothing to do") + return nil + } + err := os.WriteFile(networkManagerCtrldConfFile, []byte(nmCtrldConfContent), os.FileMode(0644)) + if os.IsNotExist(err) { + mainLog.Debug().Msg("NetworkManager is not available") + return nil + } + if err != nil { + mainLog.Debug().Err(err).Msg("could not write NetworkManager ctrld config file") + return err + } + + reloadNetworkManager() + mainLog.Debug().Msg("setup NetworkManager done") + return nil +} + +func restoreNetworkManager() error { + if runtime.GOOS != "linux" { + mainLog.Debug().Msg("skipping NetworkManager restoring, not on Linux") + return nil + } + err := os.Remove(networkManagerCtrldConfFile) + if os.IsNotExist(err) { + mainLog.Debug().Msg("NetworkManager is not available") + return nil + } + if err != nil { + mainLog.Debug().Err(err).Msg("could not remove NetworkManager ctrld config file") + return err + } + + reloadNetworkManager() + mainLog.Debug().Msg("restore NetworkManager done") + return nil +} + +func reloadNetworkManager() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + conn, err := dbus.NewSystemConnectionContext(ctx) + if err != nil { + mainLog.Error().Err(err).Msg("could not create new system connection") + return + } + defer conn.Close() + + waitCh := make(chan string) + if _, err := conn.ReloadUnitContext(ctx, nmSystemdUnitName, "ignore-dependencies", waitCh); err != nil { + mainLog.Debug().Err(err).Msg("could not reload NetworkManager") + } + <-waitCh +} diff --git a/cmd/ctrld/os_linux.go b/cmd/ctrld/os_linux.go index 01c52c0..582ae3c 100644 --- a/cmd/ctrld/os_linux.go +++ b/cmd/ctrld/os_linux.go @@ -9,7 +9,6 @@ import ( "net/netip" "os/exec" "reflect" - "runtime" "strings" "syscall" "time" @@ -175,25 +174,6 @@ func getDNSByNmcli(iface string) []string { return dns } -func getConnByNmcli(iface string) string { - if iface == "auto" { - iface = defaultIfaceName() - } - b, err := exec.Command("nmcli", "dev", "show", iface).Output() - if err != nil { - return "" - } - s := bufio.NewScanner(bytes.NewReader(b)) - for s.Scan() { - line := s.Text() - if _, connName, found := strings.Cut(line, "GENERAL.CONNECTION:"); found { - return strings.TrimSpace(connName) - } - - } - return "" -} - func ignoringEINTR(fn func() error) error { for { err := fn() @@ -202,22 +182,3 @@ func ignoringEINTR(fn func() error) error { } } } - -func disableAutoDNS(iface string) { - networkManagerIgnoreAutoDNS(iface, "yes") -} - -func enableAutoDNS(iface string) { - networkManagerIgnoreAutoDNS(iface, "no") -} - -func networkManagerIgnoreAutoDNS(iface, answer string) { - if runtime.GOOS != "linux" { - return - } - if connName := getConnByNmcli(iface); connName != "" { - mainLog.Debug().Msg("enable auto DNS from network manager") - _ = exec.Command("nmcli", "con", "mod", connName, "ipv4.ignore-auto-dns", answer).Run() - _ = exec.Command("nmcli", "con", "mod", connName, "ipv6.ignore-auto-dns", answer).Run() - } -} diff --git a/cmd/ctrld/os_mac.go b/cmd/ctrld/os_mac.go index ba9dc0d..95786f3 100644 --- a/cmd/ctrld/os_mac.go +++ b/cmd/ctrld/os_mac.go @@ -60,9 +60,3 @@ func resetDNS(iface *net.Interface) error { func currentDNS(_ *net.Interface) []string { return resolvconffile.NameServers("") } - -func disableAutoDNS(iface string) { -} - -func enableAutoDNS(iface string) { -} diff --git a/cmd/ctrld/os_windows.go b/cmd/ctrld/os_windows.go index 1652225..213c104 100644 --- a/cmd/ctrld/os_windows.go +++ b/cmd/ctrld/os_windows.go @@ -104,9 +104,3 @@ func currentDNS(iface *net.Interface) []string { } return ns } - -func disableAutoDNS(iface string) { -} - -func enableAutoDNS(iface string) { -} diff --git a/cmd/ctrld/prog.go b/cmd/ctrld/prog.go index 532f57e..8a9cd0e 100644 --- a/cmd/ctrld/prog.go +++ b/cmd/ctrld/prog.go @@ -209,6 +209,10 @@ func (p *prog) setDNS() { logger.Error().Err(err).Msg("could not get interface") return } + if err := setupNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not patch NetworkManager") + return + } logger.Debug().Msg("setting DNS for interface") if err := setDNS(netIface, []string{cfg.Listener["0"].IP}); err != nil { logger.Error().Err(err).Msgf("could not set DNS for interface") @@ -230,6 +234,10 @@ func (p *prog) resetDNS() { logger.Error().Err(err).Msg("could not get interface") return } + if err := restoreNetworkManager(); err != nil { + logger.Error().Err(err).Msg("could not restore NetworkManager") + return + } logger.Debug().Msg("Restoring DNS for interface") if err := resetDNS(netIface); err != nil { logger.Error().Err(err).Msgf("could not reset DNS") diff --git a/go.mod b/go.mod index 1e6f67c..433326b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Control-D-Inc/ctrld go 1.19 require ( + github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 github.com/go-playground/validator/v10 v10.11.1 github.com/hashicorp/golang-lru/v2 v2.0.1 github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e diff --git a/go.sum b/go.sum index 2a4fe13..a450fe2 100644 --- a/go.sum +++ b/go.sum @@ -52,6 +52,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=