Compare commits

...

1 Commits

Author SHA1 Message Date
Brad Fitzpatrick
6dc3d6f787 net/dns: make directManager use /tmp/resolv.conf on gokrazy
Appliances built using https://gokrazy.org/ have a read-only root file
system, including /etc/resolv.conf, which is a symlink to
/tmp/resolv.conf.

The system’s dhcp client overwrites /tmp/resolv.conf instead,
so we need to use this path in Tailscale, too.

Fixes #8689
Updates gokrazy/gokrazy#209

Co-authored-by: Michael Stapelberg <michael@stapelberg.de>
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2023-07-23 09:41:27 -07:00
3 changed files with 34 additions and 25 deletions

View File

@@ -28,8 +28,8 @@ import (
)
const (
backupConf = "/etc/resolv.pre-tailscale-backup.conf"
resolvConf = "/etc/resolv.conf"
defaultBackupConf = "/etc/resolv.pre-tailscale-backup.conf"
defaultResolvConf = "/etc/resolv.conf"
)
// writeResolvConf writes DNS configuration in resolv.conf format to the given writer.
@@ -132,6 +132,9 @@ type directManager struct {
// but is better than having non-functioning DNS.
renameBroken bool
resolvConf string // usually "/etc/resolv.conf"
backupConf string // usually "/etc/resolv.pre-tailscale-backup.conf"
ctx context.Context // valid until Close
ctxClose context.CancelFunc // closes ctx
@@ -147,10 +150,16 @@ func newDirectManager(logf logger.Logf) *directManager {
func newDirectManagerOnFS(logf logger.Logf, fs wholeFileFS) *directManager {
ctx, cancel := context.WithCancel(context.Background())
m := &directManager{
logf: logf,
fs: fs,
ctx: ctx,
ctxClose: cancel,
logf: logf,
fs: fs,
ctx: ctx,
ctxClose: cancel,
resolvConf: defaultResolvConf,
backupConf: defaultBackupConf,
}
if distro.Get() == distro.Gokrazy {
m.resolvConf = "/tmp/resolv.conf"
m.backupConf = "/tmp/resolv.pre-tailscale-backup.conf"
}
go m.runFileWatcher()
return m
@@ -167,7 +176,7 @@ func (m *directManager) readResolvFile(path string) (OSConfig, error) {
// ownedByTailscale reports whether /etc/resolv.conf seems to be a
// tailscale-managed file.
func (m *directManager) ownedByTailscale() (bool, error) {
isRegular, err := m.fs.Stat(resolvConf)
isRegular, err := m.fs.Stat(m.resolvConf)
if err != nil {
if os.IsNotExist(err) {
return false, nil
@@ -177,7 +186,7 @@ func (m *directManager) ownedByTailscale() (bool, error) {
if !isRegular {
return false, nil
}
bs, err := m.fs.ReadFile(resolvConf)
bs, err := m.fs.ReadFile(m.resolvConf)
if err != nil {
return false, err
}
@@ -190,11 +199,11 @@ func (m *directManager) ownedByTailscale() (bool, error) {
// backupConfig creates or updates a backup of /etc/resolv.conf, if
// resolv.conf does not currently contain a Tailscale-managed config.
func (m *directManager) backupConfig() error {
if _, err := m.fs.Stat(resolvConf); err != nil {
if _, err := m.fs.Stat(m.resolvConf); err != nil {
if os.IsNotExist(err) {
// No resolv.conf, nothing to back up. Also get rid of any
// existing backup file, to avoid restoring something old.
m.fs.Remove(backupConf)
m.fs.Remove(m.backupConf)
return nil
}
return err
@@ -208,11 +217,11 @@ func (m *directManager) backupConfig() error {
return nil
}
return m.rename(resolvConf, backupConf)
return m.rename(m.resolvConf, m.backupConf)
}
func (m *directManager) restoreBackup() (restored bool, err error) {
if _, err := m.fs.Stat(backupConf); err != nil {
if _, err := m.fs.Stat(m.backupConf); err != nil {
if os.IsNotExist(err) {
// No backup, nothing we can do.
return false, nil
@@ -223,7 +232,7 @@ func (m *directManager) restoreBackup() (restored bool, err error) {
if err != nil {
return false, err
}
_, err = m.fs.Stat(resolvConf)
_, err = m.fs.Stat(m.resolvConf)
if err != nil && !os.IsNotExist(err) {
return false, err
}
@@ -232,12 +241,12 @@ func (m *directManager) restoreBackup() (restored bool, err error) {
if resolvConfExists && !owned {
// There's already a non-tailscale config in place, get rid of
// our backup.
m.fs.Remove(backupConf)
m.fs.Remove(m.backupConf)
return false, nil
}
// We own resolv.conf, and a backup exists.
if err := m.rename(backupConf, resolvConf); err != nil {
if err := m.rename(m.backupConf, m.resolvConf); err != nil {
return false, err
}
@@ -307,7 +316,7 @@ func (m *directManager) checkForFileTrample() {
return
}
cur, err := m.fs.ReadFile(resolvConf)
cur, err := m.fs.ReadFile(m.resolvConf)
if err != nil {
m.logf("trample: read error: %v", err)
return
@@ -365,7 +374,7 @@ func (m *directManager) SetDNS(config OSConfig) (err error) {
buf := new(bytes.Buffer)
writeResolvConf(buf, config.Nameservers, config.SearchDomains)
if err := m.atomicWriteFile(m.fs, resolvConf, buf.Bytes(), 0644); err != nil {
if err := m.atomicWriteFile(m.fs, m.resolvConf, buf.Bytes(), 0644); err != nil {
return err
}
@@ -414,9 +423,9 @@ func (m *directManager) GetBaseConfig() (OSConfig, error) {
if err != nil {
return OSConfig{}, err
}
fileToRead := resolvConf
fileToRead := m.resolvConf
if owned {
fileToRead = backupConf
fileToRead = m.backupConf
}
return m.readResolvFile(fileToRead)
@@ -429,7 +438,7 @@ func (m *directManager) Close() error {
// things. Clean it up if it's still there.
m.fs.Remove("/etc/resolv.tailscale.conf")
if _, err := m.fs.Stat(backupConf); err != nil {
if _, err := m.fs.Stat(m.backupConf); err != nil {
if os.IsNotExist(err) {
// No backup, nothing we can do.
return nil
@@ -440,7 +449,7 @@ func (m *directManager) Close() error {
if err != nil {
return err
}
_, err = m.fs.Stat(resolvConf)
_, err = m.fs.Stat(m.resolvConf)
if err != nil && !os.IsNotExist(err) {
return err
}
@@ -449,12 +458,12 @@ func (m *directManager) Close() error {
if resolvConfExists && !owned {
// There's already a non-tailscale config in place, get rid of
// our backup.
m.fs.Remove(backupConf)
m.fs.Remove(m.backupConf)
return nil
}
// We own resolv.conf, and a backup exists.
if err := m.rename(backupConf, resolvConf); err != nil {
if err := m.rename(m.backupConf, m.resolvConf); err != nil {
return err
}

View File

@@ -43,7 +43,7 @@ func (m *directManager) runFileWatcher() {
}
var match bool
for _, ev := range events {
if ev.Name == resolvConf {
if ev.Name == m.resolvConf {
match = true
break
}

View File

@@ -121,7 +121,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
dbg("resolved-ping", "yes")
}
bs, err := env.fs.ReadFile(resolvConf)
bs, err := env.fs.ReadFile(defaultResolvConf)
if os.IsNotExist(err) {
dbg("rc", "missing")
return "direct", nil