Compare commits

...

9 Commits

Author SHA1 Message Date
Denton Gentry
958917dce8 VERSION.txt: this is v1.20.0
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-01-12 17:11:42 -08:00
Brad Fitzpatrick
7c1a1aa5d9 tailcfg: no-op bump of MapRequest.Version
So 1.18 and 1.20 don't have the same.

Change-Id: Ib2cac7c11eb37d9a0c2fcb66630f1cae619a97f4
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit a7da236d3d)
2022-01-12 17:04:53 -08:00
Brad Fitzpatrick
90423bf3de wgengine/netstack: make userspace ping work when tailscaled has CAP_NET_RAW
Updates #3710

Change-Id: Ief56c7ac20f5f09a2f940a1906b9efbf1b0d6932
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit a93937abc3)
2022-01-12 14:24:04 -08:00
Maisem Ali
0028a8d4d5 cmd/tailscale/cli/web: fix typo where the html template data was being
replaced instead of being appended to.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
(cherry picked from commit 9e8a432146)
2022-01-12 12:45:32 -08:00
Brad Fitzpatrick
8519cab83d net/dns/resolver: handle tabs as whitespace when ExitDNS parses resolv.conf
On Synology, the /etc/resolv.conf has tabs in it, which this
resolv.conf parser (we have two, sigh) didn't handle.

Updates #3710

Change-Id: I86f8e09ad1867ee32fa211e85c382a27191418ea
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 24a04d07d1)
2022-01-12 12:45:31 -08:00
Brad Fitzpatrick
4cd062071c net/netns: remove a useless probe of the "ip" command
We stopped using it in 1.18.

Change-Id: If5adf1d99275286a89e2a05f0bce5193d9f6e5e3
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 51bc9a6d9d)
2022-01-11 20:27:24 -08:00
Brad Fitzpatrick
04a7f5066d cmd/tailscale: let 'tailscale up --reset' do a pref edit
The --reset shouldn't imply that a Backend.Start is necessary.  With
this, it can do a Backend.EditPrefs instead, which then doesn't do all
the heavy work that Start does. Also, Start on Windows behaves
slightly differently than Linux etc in some cases because of tailscaled
running in client mode on Windows (where the GUI supplies the prefs).

Fixes #3702

Change-Id: I75c9f08d5e0052bf623074030a3a7fcaa677abf6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit e6626366a2)
2022-01-11 13:02:22 -08:00
Brad Fitzpatrick
a14d445fc7 net/dns: make WSL network configuration opt-in for now
Tailscale seems to be breaking WSL configurations lately.  Until we
understand what changed, turn off Tailscale's involvement by default
and make it opt-in.

Updates #2815

Change-Id: I9977801f8debec7d489d97761f74000a4a33f71b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 8df3fa4638)
2022-01-11 10:11:02 -08:00
Aaron Bieber
1a4293c15c net/dns: teach OpenBSD's manager to talk to resolvd(8). (#2789)
OpenBSD 6.9 and up has a daemon which handles nameserver configuration. This PR
teaches the OpenBSD dns manager to check if resolvd is being used. If it is, it
will use the route(8) command to tell resolvd to add the Tailscale dns entries
to resolv.conf

Signed-off-by: Aaron Bieber <aaron@bolddaemon.com>
2022-01-11 08:59:18 -08:00
12 changed files with 283 additions and 18 deletions

View File

@@ -1 +1 @@
1.19.0
1.20.0

View File

@@ -786,6 +786,16 @@ func TestUpdatePrefs(t *testing.T) {
wantSimpleUp: true,
wantJustEditMP: &ipn.MaskedPrefs{WantRunningSet: true},
},
{
name: "just_edit_reset",
flags: []string{"--reset"},
curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL,
Persist: &persist.Persist{LoginName: "crawshaw.github"},
},
env: upCheckEnv{backendState: "Running"},
wantJustEditMP: &ipn.MaskedPrefs{WantRunningSet: true},
},
{
name: "control_synonym",
flags: []string{},

View File

@@ -413,7 +413,6 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
justEdit := env.backendState == ipn.Running.String() &&
!env.upArgs.forceReauth &&
!env.upArgs.reset &&
env.upArgs.authKeyOrFile == "" &&
!controlURLChanged &&
!tagsChanged

View File

@@ -375,7 +375,7 @@ func webHandler(w http.ResponseWriter, r *http.Request) {
data.AdvertiseExitNode = true
} else {
if data.AdvertiseRoutes != "" {
data.AdvertiseRoutes = ","
data.AdvertiseRoutes += ","
}
data.AdvertiseRoutes += r.String()
}

View File

@@ -4,8 +4,71 @@
package dns
import "tailscale.com/types/logger"
import (
"bytes"
"fmt"
"os"
func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
"tailscale.com/types/logger"
)
type kv struct {
k, v string
}
func (kv kv) String() string {
return fmt.Sprintf("%s=%s", kv.k, kv.v)
}
func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) {
return newOSConfigurator(logf, interfaceName,
newOSConfigEnv{
rcIsResolvd: rcIsResolvd,
fs: directFS{},
})
}
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
type newOSConfigEnv struct {
fs directFS
rcIsResolvd func(resolvConfContents []byte) bool
}
func newOSConfigurator(logf logger.Logf, interfaceName string, env newOSConfigEnv) (ret OSConfigurator, err error) {
var debug []kv
dbg := func(k, v string) {
debug = append(debug, kv{k, v})
}
defer func() {
if ret != nil {
dbg("ret", fmt.Sprintf("%T", ret))
}
logf("dns: %v", debug)
}()
bs, err := env.fs.ReadFile(resolvConf)
if os.IsNotExist(err) {
dbg("rc", "missing")
return newDirectManager(logf), nil
}
if err != nil {
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
}
if env.rcIsResolvd(bs) {
dbg("resolvd", "yes")
return newResolvdManager(logf, interfaceName)
}
dbg("resolvd", "missing")
return newDirectManager(logf), nil
}
func rcIsResolvd(resolvConfContents []byte) bool {
// If we have the string "# resolvd:" in resolv.conf resolvd(8) is
// managing things.
if bytes.Contains(resolvConfContents, []byte("# resolvd:")) {
return true
}
return false
}

View File

@@ -7,8 +7,10 @@ package dns
import (
"errors"
"fmt"
"os"
"os/exec"
"sort"
"strconv"
"strings"
"syscall"
"time"
@@ -34,6 +36,8 @@ const (
versionKey = `SOFTWARE\Microsoft\Windows NT\CurrentVersion`
)
var configureWSL, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_CONFIGURE_WSL"))
type windowsManager struct {
logf logger.Logf
guid string
@@ -307,13 +311,15 @@ func (m windowsManager) SetDNS(cfg OSConfig) error {
// On initial setup of WSL, the restart caused by --shutdown is slow,
// so we do it out-of-line.
go func() {
if err := m.wslManager.SetDNS(cfg); err != nil {
m.logf("WSL SetDNS: %v", err) // continue
} else {
m.logf("WSL SetDNS: success")
}
}()
if configureWSL {
go func() {
if err := m.wslManager.SetDNS(cfg); err != nil {
m.logf("WSL SetDNS: %v", err) // continue
} else {
m.logf("WSL SetDNS: success")
}
}()
}
return nil
}

141
net/dns/resolvd.go Normal file
View File

@@ -0,0 +1,141 @@
// 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 openbsd
// +build openbsd
package dns
import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
)
func newResolvdManager(logf logger.Logf, interfaceName string) (*resolvdManager, error) {
return &resolvdManager{
logf: logf,
ifName: interfaceName,
fs: directFS{},
}, nil
}
// resolvdManager is an OSConfigurator which uses route(1) to teach OpenBSD's
// resolvd(8) about DNS servers.
type resolvdManager struct {
logf logger.Logf
ifName string
fs directFS
}
func (m *resolvdManager) SetDNS(config OSConfig) error {
args := []string{
"nameserver",
m.ifName,
}
origResolv, err := m.readAndCopy(resolvConf, backupConf, 0644)
if err != nil {
return err
}
newResolvConf := removeSearchLines(origResolv)
for _, ns := range config.Nameservers {
args = append(args, ns.String())
}
var newSearch = []string{
"search",
}
for _, s := range config.SearchDomains {
newSearch = append(newSearch, s.WithoutTrailingDot())
}
newResolvConf = append(newResolvConf, []byte(strings.Join(newSearch, " "))...)
err = m.fs.WriteFile(resolvConf, newResolvConf, 0644)
if err != nil {
return err
}
cmd := exec.Command("/sbin/route", args...)
return cmd.Run()
}
func (m *resolvdManager) SupportsSplitDNS() bool {
return false
}
func (m *resolvdManager) GetBaseConfig() (OSConfig, error) {
cfg, err := m.readResolvConf()
if err != nil {
return OSConfig{}, err
}
return cfg, nil
}
func (m *resolvdManager) Close() error {
// resolvd handles teardown of nameservers so we only need to write back the original
// config and be done.
_, err := m.readAndCopy(backupConf, resolvConf, 0644)
if err != nil {
return err
}
return m.fs.Remove(backupConf)
}
func (m *resolvdManager) readAndCopy(a, b string, mode os.FileMode) ([]byte, error) {
orig, err := m.fs.ReadFile(a)
if err != nil {
return nil, err
}
err = m.fs.WriteFile(b, orig, mode)
if err != nil {
return nil, err
}
return orig, nil
}
func (m resolvdManager) readResolvConf() (config OSConfig, err error) {
b, err := m.fs.ReadFile(resolvConf)
if err != nil {
return OSConfig{}, err
}
scanner := bufio.NewScanner(bytes.NewReader(b))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// resolvd manages "nameserver" lines, we only need to handle
// "search".
if strings.HasPrefix(line, "search") {
domain := strings.TrimPrefix(line, "search")
domain = strings.TrimSpace(domain)
fqdn, err := dnsname.ToFQDN(domain)
if err != nil {
return OSConfig{}, fmt.Errorf("parsing search domains %q: %w", line, err)
}
config.SearchDomains = append(config.SearchDomains, fqdn)
continue
}
}
return config, nil
}
func removeSearchLines(orig []byte) []byte {
re := regexp.MustCompile(`(?m)^search\s+.+$`)
return re.ReplaceAll(orig, []byte(""))
}

View File

@@ -529,6 +529,10 @@ func stubResolverForOS() (ip netaddr.IP, err error) {
if c, ok := resolvConfCacheValue.Load().(resolvConfCache); ok && c.mod == cur.mod && c.size == cur.size {
return c.ip, nil
}
// TODO(bradfitz): unify this /etc/resolv.conf parsing code with readResolv
// in net/dns, which we can't use due to circular dependency reasons.
// Move it to a leaf, including the OSConfig type (perhaps in its own dnstype
// package?)
err = lineread.File("/etc/resolv.conf", func(line []byte) error {
if !ip.IsZero() {
return nil
@@ -537,6 +541,12 @@ func stubResolverForOS() (ip netaddr.IP, err error) {
if len(line) == 0 || line[0] == '#' {
return nil
}
// Normalize tabs to spaces to simplify parsing code later.
for i, b := range line {
if b == '\t' {
line[i] = ' '
}
}
if mem.HasPrefix(mem.B(line), mem.S("nameserver ")) {
s := strings.TrimSpace(strings.TrimPrefix(string(line), "nameserver "))
ip, err = netaddr.ParseIP(s)

View File

@@ -11,7 +11,6 @@ import (
"fmt"
"net"
"os"
"os/exec"
"sync"
"syscall"
@@ -67,8 +66,7 @@ func socketMarkWorks() bool {
// If it doesn't, we have to use SO_BINDTODEVICE on our sockets instead.
func useSocketMark() bool {
socketMarkWorksOnce.Do(func() {
ipRuleWorks := exec.Command("ip", "rule").Run() == nil
socketMarkWorksOnce.v = ipRuleWorks && socketMarkWorks()
socketMarkWorksOnce.v = socketMarkWorks()
})
return socketMarkWorksOnce.v
}

View File

@@ -48,7 +48,8 @@ import (
// 23: 2021-08-25: DNSConfig.Routes values may be empty (for ExtraRecords support in 1.14.1+)
// 24: 2021-09-18: MapResponse.Health from control to node; node shows in "tailscale status"
// 25: 2021-11-01: MapResponse.Debug.Exit
const CurrentMapRequestVersion = 25
// 26: 2022-01-12: (nothing, just bumping for 1.20.0)
const CurrentMapRequestVersion = 26
type StableID string

View File

@@ -41,6 +41,7 @@ import (
"tailscale.com/syncs"
"tailscale.com/types/logger"
"tailscale.com/types/netmap"
"tailscale.com/version/distro"
"tailscale.com/wgengine"
"tailscale.com/wgengine/filter"
"tailscale.com/wgengine/magicsock"
@@ -394,8 +395,14 @@ func (ns *Impl) shouldProcessInbound(p *packet.Parsed, t *tstun.Wrapper) bool {
return false
}
// setAmbientCapsRaw is non-nil on Linux for Synology, to run ping with
// CAP_NET_RAW from tailscaled's binary.
var setAmbientCapsRaw func(*exec.Cmd)
var userPingSem = syncs.NewSemaphore(20) // 20 child ping processes at once
var isSynology = runtime.GOOS == "linux" && distro.Get() == distro.Synology
// userPing tried to ping dstIP and if it succeeds, injects pingResPkt
// into the tundev.
//
@@ -426,11 +433,21 @@ func (ns *Impl) userPing(dstIP netaddr.IP, pingResPkt []byte) {
}
err = exec.Command(ping, "-c", "1", "-w", "3", dstIP.String()).Run()
default:
err = exec.Command("ping", "-c", "1", "-W", "3", dstIP.String()).Run()
ping := "ping"
if isSynology {
ping = "/bin/ping"
}
cmd := exec.Command(ping, "-c", "1", "-W", "3", dstIP.String())
if isSynology && os.Getuid() != 0 {
// On DSM7 we run as non-root and need to pass
// CAP_NET_RAW if our binary has it.
setAmbientCapsRaw(cmd)
}
err = cmd.Run()
}
d := time.Since(t0)
if err != nil {
ns.logf("exec ping of %v failed in %v", dstIP, d)
ns.logf("exec ping of %v failed in %v: %v", dstIP, d, err)
return
}
if debugNetstack {

View File

@@ -0,0 +1,20 @@
// 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.
package netstack
import (
"os/exec"
"syscall"
"golang.org/x/sys/unix"
)
func init() {
setAmbientCapsRaw = func(cmd *exec.Cmd) {
cmd.SysProcAttr = &syscall.SysProcAttr{
AmbientCaps: []uintptr{unix.CAP_NET_RAW},
}
}
}