mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-08-23 01:27:13 +02:00
all: add freebsd supports
This commit add support for ctrld to run on freebsd, supported platforms are amd64/arm64/armv6/armv7,386. Supporting freebsd also requires adding debian and openresolv resolvconf. Updates #47
This commit is contained in:
+1
-2
@@ -197,8 +197,7 @@ func initCLI() {
|
|||||||
setDependencies(sc)
|
setDependencies(sc)
|
||||||
sc.Arguments = append([]string{"run"}, osArgs...)
|
sc.Arguments = append([]string{"run"}, osArgs...)
|
||||||
if dir, err := os.UserHomeDir(); err == nil {
|
if dir, err := os.UserHomeDir(); err == nil {
|
||||||
// WorkingDirectory is not supported on Windows.
|
setWorkingDirectory(sc, dir)
|
||||||
sc.WorkingDirectory = dir
|
|
||||||
// No config path, generating config in HOME directory.
|
// No config path, generating config in HOME directory.
|
||||||
noConfigStart := isNoConfigStart(cmd)
|
noConfigStart := isNoConfigStart(cmd)
|
||||||
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
writeDefaultConfig := !noConfigStart && configBase64 == ""
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-systemd/v22/dbus"
|
"github.com/coreos/go-systemd/v22/dbus"
|
||||||
@@ -24,10 +23,6 @@ systemd-resolved=false
|
|||||||
var networkManagerCtrldConfFile = filepath.Join(nmConfDir, nmCtrldConfFilename)
|
var networkManagerCtrldConfFile = filepath.Join(nmConfDir, nmCtrldConfFilename)
|
||||||
|
|
||||||
func setupNetworkManager() error {
|
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 {
|
if content, _ := os.ReadFile(nmCtrldConfContent); string(content) == nmCtrldConfContent {
|
||||||
mainLog.Debug().Msg("NetworkManager already setup, nothing to do")
|
mainLog.Debug().Msg("NetworkManager already setup, nothing to do")
|
||||||
return nil
|
return nil
|
||||||
@@ -48,10 +43,6 @@ func setupNetworkManager() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restoreNetworkManager() error {
|
func restoreNetworkManager() error {
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
mainLog.Debug().Msg("skipping NetworkManager restoring, not on Linux")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := os.Remove(networkManagerCtrldConfFile)
|
err := os.Remove(networkManagerCtrldConfFile)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
mainLog.Debug().Msg("NetworkManager is not available")
|
mainLog.Debug().Msg("NetworkManager is not available")
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
func setupNetworkManager() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreNetworkManager() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadNetworkManager() {}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build darwin
|
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/Control-D-Inc/ctrld/internal/dns"
|
||||||
|
"github.com/Control-D-Inc/ctrld/internal/resolvconffile"
|
||||||
|
)
|
||||||
|
|
||||||
|
// set the dns server for the provided network interface
|
||||||
|
func setDNS(iface *net.Interface, nameservers []string) error {
|
||||||
|
r, err := dns.NewOSConfigurator(logf, iface.Name)
|
||||||
|
if err != nil {
|
||||||
|
mainLog.Error().Err(err).Msg("failed to create DNS OS configurator")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ns := make([]netip.Addr, 0, len(nameservers))
|
||||||
|
for _, nameserver := range nameservers {
|
||||||
|
ns = append(ns, netip.MustParseAddr(nameserver))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.SetDNS(dns.OSConfig{Nameservers: ns}); err != nil {
|
||||||
|
mainLog.Error().Err(err).Msg("failed to set DNS")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetDNS(iface *net.Interface) error {
|
||||||
|
r, err := dns.NewOSConfigurator(logf, iface.Name)
|
||||||
|
if err != nil {
|
||||||
|
mainLog.Error().Err(err).Msg("failed to create DNS OS configurator")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.Close(); err != nil {
|
||||||
|
mainLog.Error().Err(err).Msg("failed to rollback DNS setting")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentDNS(_ *net.Interface) []string {
|
||||||
|
return resolvconffile.NameServers("")
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build !linux && !darwin
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// TODO(cuonglm): implement.
|
||||||
|
func allocateIP(ip string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(cuonglm): implement.
|
||||||
|
func deAllocateIP(ip string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -14,16 +11,6 @@ import (
|
|||||||
ctrldnet "github.com/Control-D-Inc/ctrld/internal/net"
|
ctrldnet "github.com/Control-D-Inc/ctrld/internal/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(cuonglm): implement.
|
|
||||||
func allocateIP(ip string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(cuonglm): implement.
|
|
||||||
func deAllocateIP(ip string) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDNS(iface *net.Interface, nameservers []string) error {
|
func setDNS(iface *net.Interface, nameservers []string) error {
|
||||||
if len(nameservers) == 0 {
|
if len(nameservers) == 0 {
|
||||||
return errors.New("empty DNS nameservers")
|
return errors.New("empty DNS nameservers")
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/kardianos/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *prog) preRun() {
|
||||||
|
if !service.Interactive() {
|
||||||
|
p.setDNS()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDependencies(svc *service.Config) {
|
||||||
|
// TODO(cuonglm): remove once https://github.com/kardianos/service/issues/359 fixed.
|
||||||
|
_ = os.MkdirAll("/usr/local/etc/rc.d", 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setWorkingDirectory(svc *service.Config, dir string) {}
|
||||||
@@ -18,3 +18,7 @@ func setDependencies(svc *service.Config) {
|
|||||||
"After=NetworkManager-wait-online.service",
|
"After=NetworkManager-wait-online.service",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setWorkingDirectory(svc *service.Config, dir string) {
|
||||||
|
svc.WorkingDirectory = dir
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
//go:build !linux
|
//go:build !linux && !freebsd
|
||||||
// +build !linux
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -8,3 +7,8 @@ import "github.com/kardianos/service"
|
|||||||
func (p *prog) preRun() {}
|
func (p *prog) preRun() {}
|
||||||
|
|
||||||
func setDependencies(svc *service.Config) {}
|
func setDependencies(svc *service.Config) {}
|
||||||
|
|
||||||
|
func setWorkingDirectory(svc *service.Config, dir string) {
|
||||||
|
// WorkingDirectory is not supported on Windows.
|
||||||
|
svc.WorkingDirectory = dir
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux || freebsd || openbsd
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"tailscale.com/atomicfile"
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed resolvconf-workaround.sh
|
||||||
|
var workaroundScript []byte
|
||||||
|
|
||||||
|
// resolvconfConfigName is the name of the config submitted to
|
||||||
|
// resolvconf.
|
||||||
|
// The name starts with 'tun' in order to match the hardcoded
|
||||||
|
// interface order in debian resolvconf, which will place this
|
||||||
|
// configuration ahead of regular network links. In theory, this
|
||||||
|
// doesn't matter because we then fix things up to ensure our config
|
||||||
|
// is the only one in use, but in case that fails, this will make our
|
||||||
|
// configuration slightly preferred.
|
||||||
|
// The 'inet' suffix has no specific meaning, but conventionally
|
||||||
|
// resolvconf implementations encourage adding a suffix roughly
|
||||||
|
// indicating where the config came from, and "inet" is the "none of
|
||||||
|
// the above" value (rather than, say, "ppp" or "dhcp").
|
||||||
|
const resolvconfConfigName = "ctrld.inet"
|
||||||
|
|
||||||
|
// resolvconfLibcHookPath is the directory containing libc update
|
||||||
|
// scripts, which are run by Debian resolvconf when /etc/resolv.conf
|
||||||
|
// has been updated.
|
||||||
|
const resolvconfLibcHookPath = "/etc/resolvconf/update-libc.d"
|
||||||
|
|
||||||
|
// resolvconfHookPath is the name of the libc hook script we install
|
||||||
|
// to force Ctrld's DNS config to take effect.
|
||||||
|
var resolvconfHookPath = filepath.Join(resolvconfLibcHookPath, "ctrld")
|
||||||
|
|
||||||
|
// resolvconfManager manages DNS configuration using the Debian
|
||||||
|
// implementation of the `resolvconf` program, written by Thomas Hood.
|
||||||
|
type resolvconfManager struct {
|
||||||
|
logf logger.Logf
|
||||||
|
listRecordsPath string
|
||||||
|
interfacesDir string
|
||||||
|
scriptInstalled bool // libc update script has been installed
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ OSConfigurator = (*resolvconfManager)(nil)
|
||||||
|
|
||||||
|
func newDebianResolvconfManager(logf logger.Logf) (*resolvconfManager, error) {
|
||||||
|
ret := &resolvconfManager{
|
||||||
|
logf: logf,
|
||||||
|
listRecordsPath: "/lib/resolvconf/list-records",
|
||||||
|
interfacesDir: "/etc/resolvconf/run/interface", // panic fallback if nothing seems to work
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(ret.listRecordsPath); os.IsNotExist(err) {
|
||||||
|
// This might be a Debian system from before the big /usr
|
||||||
|
// merge, try /usr instead.
|
||||||
|
ret.listRecordsPath = "/usr" + ret.listRecordsPath
|
||||||
|
}
|
||||||
|
// The runtime directory is currently (2020-04) canonically
|
||||||
|
// /etc/resolvconf/run, but the manpage is making noise about
|
||||||
|
// switching to /run/resolvconf and dropping the /etc path. So,
|
||||||
|
// let's probe the possible directories and use the first one
|
||||||
|
// that works.
|
||||||
|
for _, path := range []string{
|
||||||
|
"/etc/resolvconf/run/interface",
|
||||||
|
"/run/resolvconf/interface",
|
||||||
|
"/var/run/resolvconf/interface",
|
||||||
|
} {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
ret.interfacesDir = path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ret.interfacesDir == "" {
|
||||||
|
// None of the paths seem to work, use the canonical location
|
||||||
|
// that the current manpage says to use.
|
||||||
|
ret.interfacesDir = "/etc/resolvconf/run/interfaces"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resolvconfManager) deleteCtrldConfig() error {
|
||||||
|
cmd := exec.Command("resolvconf", "-d", resolvconfConfigName)
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("running %s: %s", cmd, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resolvconfManager) SetDNS(config OSConfig) error {
|
||||||
|
if !m.scriptInstalled {
|
||||||
|
m.logf("injecting resolvconf workaround script")
|
||||||
|
if err := os.MkdirAll(resolvconfLibcHookPath, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := atomicfile.WriteFile(resolvconfHookPath, workaroundScript, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.scriptInstalled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.IsZero() {
|
||||||
|
if err := m.deleteCtrldConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stdin := new(bytes.Buffer)
|
||||||
|
writeResolvConf(stdin, config.Nameservers, config.SearchDomains) // dns_direct.go
|
||||||
|
|
||||||
|
// This resolvconf implementation doesn't support exclusive
|
||||||
|
// mode or interface priorities, so it will end up blending
|
||||||
|
// our configuration with other sources. However, this will
|
||||||
|
// get fixed up by the script we injected above.
|
||||||
|
cmd := exec.Command("resolvconf", "-a", resolvconfConfigName)
|
||||||
|
cmd.Stdin = stdin
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("running %s: %s", cmd, out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resolvconfManager) Close() error {
|
||||||
|
if err := m.deleteCtrldConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.scriptInstalled {
|
||||||
|
m.logf("removing resolvconf workaround script")
|
||||||
|
os.Remove(resolvconfHookPath) // Best-effort
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resolvconfManager) Mode() string {
|
||||||
|
return "resolvconf"
|
||||||
|
}
|
||||||
@@ -144,6 +144,10 @@ type directManager struct {
|
|||||||
lastWarnContents []byte // last resolv.conf contents that we warned about
|
lastWarnContents []byte // last resolv.conf contents that we warned about
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newDirectManager(logf logger.Logf) *directManager {
|
||||||
|
return newDirectManagerOnFS(logf, directFS{})
|
||||||
|
}
|
||||||
|
|
||||||
func newDirectManagerOnFS(logf logger.Logf, fs wholeFileFS) *directManager {
|
func newDirectManagerOnFS(logf logger.Logf, fs wholeFileFS) *directManager {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
m := &directManager{
|
m := &directManager{
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"tailscale.com/types/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
|
||||||
|
bs, err := os.ReadFile("/etc/resolv.conf")
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return newDirectManager(logf), nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch resolvOwner(bs) {
|
||||||
|
case "resolvconf":
|
||||||
|
switch resolvconfStyle() {
|
||||||
|
case "":
|
||||||
|
return newDirectManager(logf), nil
|
||||||
|
case "debian":
|
||||||
|
return newDebianResolvconfManager(logf)
|
||||||
|
case "openresolv":
|
||||||
|
return newOpenresolvManager()
|
||||||
|
default:
|
||||||
|
logf("[unexpected] got unknown flavor of resolvconf %q, falling back to direct manager", resolvconfStyle())
|
||||||
|
return newDirectManager(logf), nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return newDirectManager(logf), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,8 @@ type nmManager struct {
|
|||||||
dnsManager dbus.BusObject
|
dnsManager dbus.BusObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ OSConfigurator = (*nmManager)(nil)
|
||||||
|
|
||||||
func newNMManager(interfaceName string) (*nmManager, error) {
|
func newNMManager(interfaceName string) (*nmManager, error) {
|
||||||
conn, err := dbus.SystemBus()
|
conn, err := dbus.SystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build linux || freebsd || openbsd
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// openresolvManager manages DNS configuration using the openresolv
|
||||||
|
// implementation of the `resolvconf` program.
|
||||||
|
type openresolvManager struct{}
|
||||||
|
|
||||||
|
var _ OSConfigurator = (*openresolvManager)(nil)
|
||||||
|
|
||||||
|
func newOpenresolvManager() (openresolvManager, error) {
|
||||||
|
return openresolvManager{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m openresolvManager) deleteTailscaleConfig() error {
|
||||||
|
cmd := exec.Command("resolvconf", "-f", "-d", "ctrld")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("running %s: %s", cmd, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m openresolvManager) SetDNS(config OSConfig) error {
|
||||||
|
if config.IsZero() {
|
||||||
|
return m.deleteTailscaleConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdin bytes.Buffer
|
||||||
|
writeResolvConf(&stdin, config.Nameservers, config.SearchDomains)
|
||||||
|
|
||||||
|
cmd := exec.Command("resolvconf", "-m", "0", "-x", "-a", "ctrld")
|
||||||
|
cmd.Stdin = &stdin
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("running %s: %s", cmd, out)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m openresolvManager) Close() error {
|
||||||
|
return m.deleteTailscaleConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m openresolvManager) Mode() string {
|
||||||
|
return "resolvconf"
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ OSConfigurator = (*directManager)(nil)
|
||||||
|
|
||||||
// An OSConfigurator applies DNS settings to the operating system.
|
// An OSConfigurator applies DNS settings to the operating system.
|
||||||
type OSConfigurator interface {
|
type OSConfigurator interface {
|
||||||
// SetDNS updates the OS's DNS configuration to match cfg.
|
// SetDNS updates the OS's DNS configuration to match cfg.
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# license that can be found in the LICENSE file.
|
||||||
|
#
|
||||||
|
# This script is a workaround for a vpn-unfriendly behavior of the
|
||||||
|
# original resolvconf by Thomas Hood. Unlike the `openresolv`
|
||||||
|
# implementation (whose binary is also called resolvconf,
|
||||||
|
# confusingly), the original resolvconf lacks a way to specify
|
||||||
|
# "exclusive mode" for a provider configuration. In practice, this
|
||||||
|
# means that if Ctrld wants to install a DNS configuration, that
|
||||||
|
# config will get "blended" with the configs from other sources,
|
||||||
|
# rather than override those other sources.
|
||||||
|
#
|
||||||
|
# This script gets installed at /etc/resolvconf/update-libc.d, which
|
||||||
|
# is a directory of hook scripts that get run after resolvconf's libc
|
||||||
|
# helper has finished rewriting /etc/resolv.conf. It's meant to notify
|
||||||
|
# consumers of resolv.conf of a new configuration.
|
||||||
|
#
|
||||||
|
# Instead, we use that hook mechanism to reach into resolvconf's
|
||||||
|
# stuff, and rewrite the libc-generated resolv.conf to exclusively
|
||||||
|
# contain Ctrld's configuration - effectively implementing
|
||||||
|
# exclusive mode ourselves in post-production.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -n "$CTRLD_RESOLVCONF_HOOK_LOOP" ]; then
|
||||||
|
# Hook script being invoked by itself, skip.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f ctrld.inet ]; then
|
||||||
|
# Ctrld isn't trying to manage DNS, do nothing.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep resolvconf /etc/resolv.conf >/dev/null; then
|
||||||
|
# resolvconf isn't managing /etc/resolv.conf, do nothing.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Write out a modified /etc/resolv.conf containing just our config.
|
||||||
|
(
|
||||||
|
if [ -f /etc/resolvconf/resolv.conf.d/head ]; then
|
||||||
|
cat /etc/resolvconf/resolv.conf.d/head
|
||||||
|
fi
|
||||||
|
echo "# Ctrld workaround applied to set exclusive DNS configuration."
|
||||||
|
cat tun-tailscale.inet
|
||||||
|
if [ -f /etc/resolvconf/resolv.conf.d/base ]; then
|
||||||
|
# Keep options and sortlist, discard other base things since
|
||||||
|
# they're the things we're trying to override.
|
||||||
|
grep -e 'sortlist ' -e 'options ' /etc/resolvconf/resolv.conf.d/base || true
|
||||||
|
fi
|
||||||
|
if [ -f /etc/resolvconf/resolv.conf.d/tail ]; then
|
||||||
|
cat /etc/resolvconf/resolv.conf.d/tail
|
||||||
|
fi
|
||||||
|
) >/etc/resolv.conf
|
||||||
|
|
||||||
|
if [ -d /etc/resolvconf/update-libc.d ] ; then
|
||||||
|
# Re-notify libc watchers that we've changed resolv.conf again.
|
||||||
|
export CTRLD_RESOLVCONF_HOOK_LOOP=1
|
||||||
|
exec run-parts /etc/resolvconf/update-libc.d
|
||||||
|
fi
|
||||||
@@ -105,6 +105,8 @@ type resolvedManager struct {
|
|||||||
newManager func(conn *dbus.Conn) dbus.BusObject
|
newManager func(conn *dbus.Conn) dbus.BusObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ OSConfigurator = (*resolvedManager)(nil)
|
||||||
|
|
||||||
func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManager, error) {
|
func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManager, error) {
|
||||||
iface, err := net.InterfaceByName(interfaceName)
|
iface, err := net.InterfaceByName(interfaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user