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.
This commit is contained in:
Cuong Manh Le
2023-02-02 13:56:30 +07:00
committed by Cuong Manh Le
parent 1c2cd555bd
commit eb0dd6235e
8 changed files with 100 additions and 54 deletions

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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()
}
}

View File

@@ -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) {
}

View File

@@ -104,9 +104,3 @@ func currentDNS(iface *net.Interface) []string {
}
return ns
}
func disableAutoDNS(iface string) {
}
func enableAutoDNS(iface string) {
}

View File

@@ -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")

1
go.mod
View File

@@ -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

1
go.sum
View File

@@ -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=