mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-22 12:58:37 +00:00
net/dns: fix systemd-resolved detection race at boot
If systemd-resolved is enabled but not running (or not yet running, such as early boot) and resolv.conf is old/dangling, we weren't detecting systemd-resolved. This moves its ping earlier, which will trigger it to start up and write its file. Updates #3362 (likely fixes) Updates #3531 (likely fixes) Change-Id: I6392944ac59f600571c43b8f7a677df224f2beed Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
60abeb027b
commit
15599323a1
@ -77,6 +77,18 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
|
|||||||
logf("dns: %v", debug)
|
logf("dns: %v", debug)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Before we read /etc/resolv.conf (which might be in a broken
|
||||||
|
// or symlink-dangling state), try to ping the D-Bus service
|
||||||
|
// for systemd-resolved. If it's active on the machine, this
|
||||||
|
// will make it start up and write the /etc/resolv.conf file
|
||||||
|
// before it replies to the ping. (see how systemd's
|
||||||
|
// src/resolve/resolved.c calls manager_write_resolv_conf
|
||||||
|
// before the sd_event_loop starts)
|
||||||
|
resolvedUp := env.dbusPing("org.freedesktop.resolve1", "/org/freedesktop/resolve1") == nil
|
||||||
|
if resolvedUp {
|
||||||
|
dbg("resolved-ping", "yes")
|
||||||
|
}
|
||||||
|
|
||||||
bs, err := env.fs.ReadFile(resolvConf)
|
bs, err := env.fs.ReadFile(resolvConf)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
dbg("rc", "missing")
|
dbg("rc", "missing")
|
||||||
@ -99,7 +111,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
|
|||||||
dbg("resolved", "not-in-use")
|
dbg("resolved", "not-in-use")
|
||||||
return "direct", nil
|
return "direct", nil
|
||||||
}
|
}
|
||||||
if err := env.dbusPing("org.freedesktop.resolve1", "/org/freedesktop/resolve1"); err != nil {
|
if !resolvedUp {
|
||||||
dbg("resolved", "no")
|
dbg("resolved", "no")
|
||||||
return "direct", nil
|
return "direct", nil
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
env: env(
|
env: env(
|
||||||
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
resolvedRunning()),
|
resolvedRunning()),
|
||||||
wantLog: "dns: [rc=resolved nm=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -88,7 +88,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.2.3", false)),
|
nmRunning("1.2.3", false)),
|
||||||
wantLog: "dns: [rc=resolved nm=yes nm-resolved=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -97,7 +97,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.26.2", true)),
|
nmRunning("1.26.2", true)),
|
||||||
wantLog: "dns: [rc=resolved nm=yes nm-resolved=yes nm-safe=yes ret=network-manager]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=yes ret=network-manager]",
|
||||||
want: "network-manager",
|
want: "network-manager",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -106,7 +106,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.27.0", true)),
|
nmRunning("1.27.0", true)),
|
||||||
wantLog: "dns: [rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -115,7 +115,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.22.0", true)),
|
nmRunning("1.22.0", true)),
|
||||||
wantLog: "dns: [rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
// Regression tests for extreme corner cases below.
|
// Regression tests for extreme corner cases below.
|
||||||
@ -141,7 +141,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"nameserver 127.0.0.53",
|
"nameserver 127.0.0.53",
|
||||||
"nameserver 127.0.0.53"),
|
"nameserver 127.0.0.53"),
|
||||||
resolvedRunning()),
|
resolvedRunning()),
|
||||||
wantLog: "dns: [rc=resolved nm=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -156,7 +156,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
|
"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
|
||||||
"nameserver 127.0.0.53"),
|
"nameserver 127.0.0.53"),
|
||||||
resolvedRunning()),
|
resolvedRunning()),
|
||||||
wantLog: "dns: [rc=resolved nm=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -183,7 +183,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"options edns0 trust-ad"),
|
"options edns0 trust-ad"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.32.12", true)),
|
nmRunning("1.32.12", true)),
|
||||||
wantLog: "dns: [rc=nm nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -206,7 +206,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"options edns0 trust-ad"),
|
"options edns0 trust-ad"),
|
||||||
resolvedRunning(),
|
resolvedRunning(),
|
||||||
nmRunning("1.26.3", true)),
|
nmRunning("1.26.3", true)),
|
||||||
wantLog: "dns: [rc=nm nm-resolved=yes nm-safe=yes ret=network-manager]",
|
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=yes ret=network-manager]",
|
||||||
want: "network-manager",
|
want: "network-manager",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -217,7 +217,7 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"nameserver 127.0.0.53",
|
"nameserver 127.0.0.53",
|
||||||
"options edns0 trust-ad"),
|
"options edns0 trust-ad"),
|
||||||
resolvedRunning()),
|
resolvedRunning()),
|
||||||
wantLog: "dns: [rc=nm nm-resolved=yes nm=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -228,7 +228,16 @@ func TestLinuxDNSMode(t *testing.T) {
|
|||||||
"search lan",
|
"search lan",
|
||||||
"nameserver 127.0.0.53"),
|
"nameserver 127.0.0.53"),
|
||||||
resolvedRunning()),
|
resolvedRunning()),
|
||||||
wantLog: "dns: [rc=nm nm-resolved=yes nm=no ret=systemd-resolved]",
|
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no ret=systemd-resolved]",
|
||||||
|
want: "systemd-resolved",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Make sure that we ping systemd-resolved to let it start up and write its resolv.conf
|
||||||
|
// before we read its file.
|
||||||
|
env: env(resolvedStartOnPingAndThen(
|
||||||
|
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
|
||||||
|
)),
|
||||||
|
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]",
|
||||||
want: "systemd-resolved",
|
want: "systemd-resolved",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -292,9 +301,14 @@ func (m memFS) WriteFile(name string, contents []byte, perm os.FileMode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dbusService struct {
|
||||||
|
name, path string
|
||||||
|
hook func() // if non-nil, run on ping
|
||||||
|
}
|
||||||
|
|
||||||
type envBuilder struct {
|
type envBuilder struct {
|
||||||
fs memFS
|
fs memFS
|
||||||
dbus []struct{ name, path string }
|
dbus []dbusService
|
||||||
nmUsingResolved bool
|
nmUsingResolved bool
|
||||||
nmVersion string
|
nmVersion string
|
||||||
resolvconfStyle string
|
resolvconfStyle string
|
||||||
@ -323,6 +337,9 @@ func env(opts ...envOption) newOSConfigEnv {
|
|||||||
dbusPing: func(name, path string) error {
|
dbusPing: func(name, path string) error {
|
||||||
for _, svc := range b.dbus {
|
for _, svc := range b.dbus {
|
||||||
if svc.name == name && svc.path == path {
|
if svc.name == name && svc.path == path {
|
||||||
|
if svc.hook != nil {
|
||||||
|
svc.hook()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,9 +365,25 @@ func resolvDotConf(ss ...string) envOption {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolvedRunning returns an option that makes resolved reply to a dbusPing.
|
||||||
func resolvedRunning() envOption {
|
func resolvedRunning() envOption {
|
||||||
|
return resolvedStartOnPingAndThen( /* nothing */ )
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolvedStartOnPingAndThen returns an option that makes resolved be
|
||||||
|
// active but not yet running. On a dbus ping, it then applies the
|
||||||
|
// provided options.
|
||||||
|
func resolvedStartOnPingAndThen(opts ...envOption) envOption {
|
||||||
return envOpt(func(b *envBuilder) {
|
return envOpt(func(b *envBuilder) {
|
||||||
b.dbus = append(b.dbus, struct{ name, path string }{"org.freedesktop.resolve1", "/org/freedesktop/resolve1"})
|
b.dbus = append(b.dbus, dbusService{
|
||||||
|
name: "org.freedesktop.resolve1",
|
||||||
|
path: "/org/freedesktop/resolve1",
|
||||||
|
hook: func() {
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt.apply(b)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +391,7 @@ func nmRunning(version string, usingResolved bool) envOption {
|
|||||||
return envOpt(func(b *envBuilder) {
|
return envOpt(func(b *envBuilder) {
|
||||||
b.nmUsingResolved = usingResolved
|
b.nmUsingResolved = usingResolved
|
||||||
b.nmVersion = version
|
b.nmVersion = version
|
||||||
b.dbus = append(b.dbus, struct{ name, path string }{"org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager/DnsManager"})
|
b.dbus = append(b.dbus, dbusService{name: "org.freedesktop.NetworkManager", path: "/org/freedesktop/NetworkManager/DnsManager"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user