Compare commits

...

9 Commits

Author SHA1 Message Date
Denton Gentry 5b81baa7d3 VERSION.txt: this is v1.26.1
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2 years ago
Brad Fitzpatrick 32f26a723b cmd/tailscale/cli, ipn/ipnlocal: give SSH tips when off/unconfigured
Updates #3802

Change-Id: I6b9a3175f68a6daa670f912561f2c2ececc07770
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 467eb2eca0)
2 years ago
Brad Fitzpatrick c1795c6af9 tailcfg: define some Node.Capabilities about SSH, its config
Updates #3802

Change-Id: Icb4ccbc6bd1c6304013bfc553d04007844a5c0bf
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 99ed54926b)
2 years ago
Brad Fitzpatrick 4ae7eff7c2 util/deephash: fix map hashing when key & element have the same type
Regression from 09afb8e35b, in which the
same reflect.Value scratch value was being used as the map iterator
copy destination.

Also: make nil and empty maps hash differently, add test.

Fixes #4871

Co-authored-by: Josh Bleecher Snyder <josharian@gmail.com>
Change-Id: I67f42524bc81f694c1b7259d6682200125ea4a66
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 757ecf7e80)
2 years ago
Brad Fitzpatrick d76cce4ee7 go.mod: bump go4.org/unsafe/assume-no-moving-gc for Go 1.19beta1
Otherwise we crash at startup with Go 1.19beta1.

Updates #4872

Change-Id: I371df4146735f7e066efd2edd48c1a305906c13d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 22c544bca7)
2 years ago
Brad Fitzpatrick 7968313a33 util/deephash: fix map hashing to actually hash elements
Fixes #4868

Change-Id: I574fd139cb7f7033dd93527344e6aa0e625477c7
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 36ea837736)
2 years ago
Brad Fitzpatrick 4e6a465d8c tailcfg: clarify some of the MapRequest variants
Change-Id: Ia09bd69856e372c3a6b64cda7ddb34029b32a11b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit 4005134263)
2 years ago
Brad Fitzpatrick 89ac48af9d cmd/tailscale: add 'debug ts2021' Noise connectivity subcommand
Updates #3488

Change-Id: I9272e68f66c4cf36fb98dd1248a74d3817447690
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit d3643fa151)
2 years ago
Denton Gentry 9fc6551b4e VERSION.txt: this is v1.26.0
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2 years ago

@ -1 +1 @@
1.25.0 1.26.1

@ -15,6 +15,8 @@ import (
"fmt" "fmt"
"io" "io"
"log" "log"
"net"
"net/http"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@ -23,11 +25,14 @@ import (
"github.com/peterbourgon/ff/v3/ffcli" "github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/control/controlhttp"
"tailscale.com/hostinfo" "tailscale.com/hostinfo"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/net/tsaddr" "tailscale.com/net/tsaddr"
"tailscale.com/paths" "tailscale.com/paths"
"tailscale.com/safesocket" "tailscale.com/safesocket"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
) )
var debugCmd = &ffcli.Command{ var debugCmd = &ffcli.Command{
@ -118,6 +123,17 @@ var debugCmd = &ffcli.Command{
Exec: runVia, Exec: runVia,
ShortHelp: "convert between site-specific IPv4 CIDRs and IPv6 'via' routes", ShortHelp: "convert between site-specific IPv4 CIDRs and IPv6 'via' routes",
}, },
{
Name: "ts2021",
Exec: runTS2021,
ShortHelp: "debug ts2021 protocol connectivity",
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("ts2021")
fs.StringVar(&ts2021Args.host, "host", "controlplane.tailscale.com", "hostname of control plane")
fs.IntVar(&ts2021Args.version, "version", int(tailcfg.CurrentCapabilityVersion), "protocol version")
return fs
})(),
},
}, },
} }
@ -425,3 +441,67 @@ func runVia(ctx context.Context, args []string) error {
} }
return nil return nil
} }
var ts2021Args struct {
host string // "controlplane.tailscale.com"
version int // 27 or whatever
}
func runTS2021(ctx context.Context, args []string) error {
log.SetOutput(os.Stdout)
log.SetFlags(log.Ltime | log.Lmicroseconds)
machinePrivate := key.NewMachine()
var dialer net.Dialer
var keys struct {
PublicKey key.MachinePublic
}
keysURL := "https://" + ts2021Args.host + "/key?v=" + strconv.Itoa(ts2021Args.version)
log.Printf("Fetching keys from %s ...", keysURL)
req, err := http.NewRequestWithContext(ctx, "GET", keysURL, nil)
if err != nil {
return err
}
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("Do: %v", err)
return err
}
if res.StatusCode != 200 {
log.Printf("Status: %v", res.Status)
return errors.New(res.Status)
}
if err := json.NewDecoder(res.Body).Decode(&keys); err != nil {
log.Printf("JSON: %v", err)
return fmt.Errorf("decoding /keys JSON: %w", err)
}
res.Body.Close()
dialFunc := func(ctx context.Context, network, address string) (net.Conn, error) {
log.Printf("Dial(%q, %q) ...", network, address)
c, err := dialer.DialContext(ctx, network, address)
if err != nil {
log.Printf("Dial(%q, %q) = %v", network, address, err)
} else {
log.Printf("Dial(%q, %q) = %v / %v", network, address, c.LocalAddr(), c.RemoteAddr())
}
return c, err
}
conn, err := controlhttp.Dial(ctx, net.JoinHostPort(ts2021Args.host, "80"), machinePrivate, keys.PublicKey, uint16(ts2021Args.version), dialFunc)
log.Printf("controlhttp.Dial = %p, %v", conn, err)
if err != nil {
return err
}
log.Printf("did noise handshake")
gotPeer := conn.Peer()
if gotPeer != keys.PublicKey {
log.Printf("peer = %v, want %v", gotPeer, keys.PublicKey)
return errors.New("key mismatch")
}
log.Printf("final underlying conn: %v / %v", conn.LocalAddr(), conn.RemoteAddr())
return nil
}

@ -404,7 +404,7 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
return simpleUp, justEditMP, nil return simpleUp, justEditMP, nil
} }
func runUp(ctx context.Context, args []string) error { func runUp(ctx context.Context, args []string) (retErr error) {
if len(args) > 0 { if len(args) > 0 {
fatalf("too many non-flag arguments: %q", args) fatalf("too many non-flag arguments: %q", args)
} }
@ -481,6 +481,12 @@ func runUp(ctx context.Context, args []string) error {
} }
} }
defer func() {
if retErr == nil {
checkSSHUpWarnings(ctx)
}
}()
simpleUp, justEditMP, err := updatePrefs(prefs, curPrefs, env) simpleUp, justEditMP, err := updatePrefs(prefs, curPrefs, env)
if err != nil { if err != nil {
fatalf("%s", err) fatalf("%s", err)
@ -676,6 +682,28 @@ func runUp(ctx context.Context, args []string) error {
} }
} }
func checkSSHUpWarnings(ctx context.Context) {
if !upArgs.runSSH {
return
}
st, err := localClient.Status(ctx)
if err != nil {
// Ignore. Don't spam more.
return
}
if len(st.Health) == 0 {
return
}
if len(st.Health) == 1 && strings.Contains(st.Health[0], "SSH") {
printf("%s\n", st.Health[0])
return
}
printf("# Health check:\n")
for _, m := range st.Health {
printf(" - %s\n", m)
}
}
func printUpDoneJSON(state ipn.State, errorString string) { func printUpDoneJSON(state ipn.State, errorString string) {
js := &upOutputJSON{BackendState: state.String(), Error: errorString} js := &upOutputJSON{BackendState: state.String(), Error: errorString}
data, err := json.MarshalIndent(js, "", " ") data, err := json.MarshalIndent(js, "", " ")

@ -8,7 +8,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
github.com/kballard/go-shellquote from tailscale.com/cmd/tailscale/cli github.com/kballard/go-shellquote from tailscale.com/cmd/tailscale/cli
L github.com/klauspost/compress/flate from nhooyr.io/websocket github.com/klauspost/compress/flate from nhooyr.io/websocket
L 💣 github.com/mdlayher/netlink from github.com/jsimonetti/rtnetlink+ L 💣 github.com/mdlayher/netlink from github.com/jsimonetti/rtnetlink+
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+ L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink L 💣 github.com/mdlayher/socket from github.com/mdlayher/netlink
@ -31,14 +31,16 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
go4.org/unsafe/assume-no-moving-gc from go4.org/intern go4.org/unsafe/assume-no-moving-gc from go4.org/intern
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+ W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+ inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
L nhooyr.io/websocket from tailscale.com/derp/derphttp+ nhooyr.io/websocket from tailscale.com/derp/derphttp+
L nhooyr.io/websocket/internal/errd from nhooyr.io/websocket nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
L nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
tailscale.com from tailscale.com/version tailscale.com from tailscale.com/version
tailscale.com/atomicfile from tailscale.com/ipn+ tailscale.com/atomicfile from tailscale.com/ipn+
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+ tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
tailscale.com/client/tailscale/apitype from tailscale.com/cmd/tailscale/cli+ tailscale.com/client/tailscale/apitype from tailscale.com/cmd/tailscale/cli+
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
tailscale.com/control/controlbase from tailscale.com/control/controlhttp
tailscale.com/control/controlhttp from tailscale.com/cmd/tailscale/cli
tailscale.com/control/controlknobs from tailscale.com/net/portmapper tailscale.com/control/controlknobs from tailscale.com/net/portmapper
tailscale.com/derp from tailscale.com/derp/derphttp tailscale.com/derp from tailscale.com/derp/derphttp
tailscale.com/derp/derphttp from tailscale.com/net/netcheck tailscale.com/derp/derphttp from tailscale.com/net/netcheck
@ -48,21 +50,22 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+
tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+ tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+
💣 tailscale.com/metrics from tailscale.com/derp 💣 tailscale.com/metrics from tailscale.com/derp
tailscale.com/net/dnscache from tailscale.com/derp/derphttp tailscale.com/net/dnscache from tailscale.com/derp/derphttp+
tailscale.com/net/dnsfallback from tailscale.com/control/controlhttp
tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+ tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+
💣 tailscale.com/net/interfaces from tailscale.com/cmd/tailscale/cli+ 💣 tailscale.com/net/interfaces from tailscale.com/cmd/tailscale/cli+
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli
tailscale.com/net/neterror from tailscale.com/net/netcheck+ tailscale.com/net/neterror from tailscale.com/net/netcheck+
tailscale.com/net/netknob from tailscale.com/net/netns tailscale.com/net/netknob from tailscale.com/net/netns
tailscale.com/net/netns from tailscale.com/derp/derphttp+ tailscale.com/net/netns from tailscale.com/derp/derphttp+
tailscale.com/net/netutil from tailscale.com/client/tailscale tailscale.com/net/netutil from tailscale.com/client/tailscale+
tailscale.com/net/packet from tailscale.com/wgengine/filter tailscale.com/net/packet from tailscale.com/wgengine/filter
tailscale.com/net/portmapper from tailscale.com/net/netcheck+ tailscale.com/net/portmapper from tailscale.com/net/netcheck+
tailscale.com/net/stun from tailscale.com/net/netcheck tailscale.com/net/stun from tailscale.com/net/netcheck
tailscale.com/net/tlsdial from tailscale.com/derp/derphttp tailscale.com/net/tlsdial from tailscale.com/derp/derphttp+
tailscale.com/net/tsaddr from tailscale.com/net/interfaces+ tailscale.com/net/tsaddr from tailscale.com/net/interfaces+
💣 tailscale.com/net/tshttpproxy from tailscale.com/derp/derphttp+ 💣 tailscale.com/net/tshttpproxy from tailscale.com/derp/derphttp+
L tailscale.com/net/wsconn from tailscale.com/derp/derphttp tailscale.com/net/wsconn from tailscale.com/derp/derphttp+
tailscale.com/paths from tailscale.com/cmd/tailscale/cli+ tailscale.com/paths from tailscale.com/cmd/tailscale/cli+
tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli+ tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli+
tailscale.com/syncs from tailscale.com/net/interfaces+ tailscale.com/syncs from tailscale.com/net/interfaces+
@ -93,12 +96,13 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+ tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
tailscale.com/wgengine/filter from tailscale.com/types/netmap tailscale.com/wgengine/filter from tailscale.com/types/netmap
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from tailscale.com/control/controlbase
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305 golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+ golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+ golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 from crypto/tls+ golang.org/x/crypto/curve25519 from crypto/tls+
golang.org/x/crypto/hkdf from crypto/tls golang.org/x/crypto/hkdf from crypto/tls+
golang.org/x/crypto/nacl/box from tailscale.com/types/key golang.org/x/crypto/nacl/box from tailscale.com/types/key
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+ golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+

@ -61,7 +61,7 @@ require (
golang.zx2c4.com/wireguard/windows v0.4.10 golang.zx2c4.com/wireguard/windows v0.4.10
gvisor.dev/gvisor v0.0.0-20220407223209-21871174d445 gvisor.dev/gvisor v0.0.0-20220407223209-21871174d445
honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83 honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 inet.af/netaddr v0.0.0-20220617031823-097006376321
inet.af/peercred v0.0.0-20210906144145-0893ea02156a inet.af/peercred v0.0.0-20210906144145-0893ea02156a
inet.af/wf v0.0.0-20211204062712-86aaea0a7310 inet.af/wf v0.0.0-20211204062712-86aaea0a7310
nhooyr.io/websocket v1.8.7 nhooyr.io/websocket v1.8.7
@ -258,7 +258,7 @@ require (
github.com/xanzy/ssh-agent v0.3.1 // indirect github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/yeya24/promlinter v0.1.0 // indirect github.com/yeya24/promlinter v0.1.0 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb // indirect golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect

@ -1231,8 +1231,9 @@ go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofe
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -1855,8 +1856,8 @@ howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCU
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls= inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw= inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU=
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8= inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg= inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU= inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=
inet.af/wf v0.0.0-20211204062712-86aaea0a7310 h1:0jKHTf+W75kYRyg5bto1UT+r18QmAz2u/5pAs/fx4zo= inet.af/wf v0.0.0-20211204062712-86aaea0a7310 h1:0jKHTf+W75kYRyg5bto1UT+r18QmAz2u/5pAs/fx4zo=

@ -416,6 +416,9 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
s.Health = append(s.Health, err.Error()) s.Health = append(s.Health, err.Error())
} }
} }
if m := b.sshOnButUnusableHealthCheckMessageLocked(); m != "" {
s.Health = append(s.Health, m)
}
if b.netMap != nil { if b.netMap != nil {
s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...) s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...)
s.MagicDNSSuffix = b.netMap.MagicDNSSuffix() s.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
@ -1826,11 +1829,21 @@ func (b *LocalBackend) CheckPrefs(p *ipn.Prefs) error {
} }
func (b *LocalBackend) checkPrefsLocked(p *ipn.Prefs) error { func (b *LocalBackend) checkPrefsLocked(p *ipn.Prefs) error {
var errs []error
if p.Hostname == "badhostname.tailscale." { if p.Hostname == "badhostname.tailscale." {
// Keep this one just for testing. // Keep this one just for testing.
return errors.New("bad hostname [test]") errs = append(errs, errors.New("bad hostname [test]"))
}
if err := b.checkSSHPrefsLocked(p); err != nil {
errs = append(errs, err)
}
return multierr.New(errs...)
}
func (b *LocalBackend) checkSSHPrefsLocked(p *ipn.Prefs) error {
if !p.RunSSH {
return nil
} }
if p.RunSSH {
switch runtime.GOOS { switch runtime.GOOS {
case "linux": case "linux":
if distro.Get() == distro.Synology && !envknob.UseWIPCode() { if distro.Get() == distro.Synology && !envknob.UseWIPCode() {
@ -1851,14 +1864,53 @@ func (b *LocalBackend) checkPrefsLocked(p *ipn.Prefs) error {
if !canSSH { if !canSSH {
return errors.New("The Tailscale SSH server has been administratively disabled.") return errors.New("The Tailscale SSH server has been administratively disabled.")
} }
if b.netMap != nil && b.netMap.SSHPolicy == nil && if envknob.SSHIgnoreTailnetPolicy() || envknob.SSHPolicyFile() != "" {
envknob.SSHPolicyFile() == "" && !envknob.SSHIgnoreTailnetPolicy() { return nil
return errors.New("Unable to enable local Tailscale SSH server; not enabled/configured on Tailnet.") }
if b.netMap != nil {
if !hasCapability(b.netMap, tailcfg.CapabilitySSH) {
if b.isDefaultServerLocked() {
return errors.New("Unable to enable local Tailscale SSH server; not enabled on Tailnet. See https://tailscale.com/s/ssh")
}
return errors.New("Unable to enable local Tailscale SSH server; not enabled on Tailnet.")
} }
} }
return nil return nil
} }
func (b *LocalBackend) sshOnButUnusableHealthCheckMessageLocked() (healthMessage string) {
if b.prefs == nil || !b.prefs.RunSSH {
return ""
}
if envknob.SSHIgnoreTailnetPolicy() || envknob.SSHPolicyFile() != "" {
return "development SSH policy in use"
}
nm := b.netMap
if nm == nil {
return ""
}
if nm.SSHPolicy != nil && len(nm.SSHPolicy.Rules) > 0 {
return ""
}
isDefault := b.isDefaultServerLocked()
isAdmin := hasCapability(nm, tailcfg.CapabilityAdmin)
if !isAdmin {
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Ask your admin to update your tailnet's ACLs to allow access."
}
if !isDefault {
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Update your tailnet's ACLs to allow access."
}
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Update your tailnet's ACLs at https://tailscale.com/s/ssh-policy"
}
func (b *LocalBackend) isDefaultServerLocked() bool {
if b.prefs == nil {
return true // assume true until set otherwise
}
return b.prefs.ControlURLOrDefault() == ipn.DefaultControlURL
}
func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (*ipn.Prefs, error) { func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (*ipn.Prefs, error) {
b.mu.Lock() b.mu.Lock()
p0 := b.prefs.Clone() p0 := b.prefs.Clone()

@ -878,13 +878,20 @@ type MapRequest struct {
// start-up before their first real endpoint update. // start-up before their first real endpoint update.
ReadOnly bool `json:",omitempty"` ReadOnly bool `json:",omitempty"`
// OmitPeers is whether the client is okay with the Peers list // OmitPeers is whether the client is okay with the Peers list being omitted
// being omitted in the response. (For example, a client on // in the response.
// start up using ReadOnly to get the DERP map.) //
// The behavior of OmitPeers being true varies based on Stream and ReadOnly:
// //
// If OmitPeers is true, Stream is false, and ReadOnly is false, // If OmitPeers is true, Stream is false, and ReadOnly is false,
// then the server will let clients update their endpoints without // then the server will let clients update their endpoints without
// breaking existing long-polling (Stream == true) connections. // breaking existing long-polling (Stream == true) connections.
// In this case, the server can omit the entire response; the client
// only checks the HTTP response status code.
//
// If OmitPeers is true, Stream is false, but ReadOnly is true,
// then all the response fields are included. (This is what the client does
// when initially fetching the DERP map.)
OmitPeers bool `json:",omitempty"` OmitPeers bool `json:",omitempty"`
// DebugFlags is a list of strings specifying debugging and // DebugFlags is a list of strings specifying debugging and
@ -1467,6 +1474,8 @@ const (
CapabilityFileSharing = "https://tailscale.com/cap/file-sharing" CapabilityFileSharing = "https://tailscale.com/cap/file-sharing"
CapabilityAdmin = "https://tailscale.com/cap/is-admin" CapabilityAdmin = "https://tailscale.com/cap/is-admin"
CapabilitySSH = "https://tailscale.com/cap/ssh" // feature enabled/available
CapabilitySSHRuleIn = "https://tailscale.com/cap/ssh-rule-in" // some SSH rule reach this node
// Inter-node capabilities. // Inter-node capabilities.

@ -98,10 +98,14 @@ func (s Sum) String() string {
} }
var ( var (
once sync.Once seedOnce sync.Once
seed uint64 seed uint64
) )
func initSeed() {
seed = uint64(time.Now().UnixNano())
}
func (h *hasher) sum() (s Sum) { func (h *hasher) sum() (s Sum) {
h.bw.Flush() h.bw.Flush()
// Sum into scratch & copy out, as hash.Hash is an interface // Sum into scratch & copy out, as hash.Hash is an interface
@ -121,9 +125,7 @@ func Hash(v any) (s Sum) {
h := hasherPool.Get().(*hasher) h := hasherPool.Get().(*hasher)
defer hasherPool.Put(h) defer hasherPool.Put(h)
h.reset() h.reset()
once.Do(func() { seedOnce.Do(initSeed)
seed = uint64(time.Now().UnixNano())
})
h.hashUint64(seed) h.hashUint64(seed)
h.hashValue(reflect.ValueOf(v)) h.hashValue(reflect.ValueOf(v))
return h.sum() return h.sum()
@ -299,7 +301,7 @@ func (h *hasher) hashValue(v reflect.Value) {
type mapHasher struct { type mapHasher struct {
h hasher h hasher
val valueCache // re-usable values for map iteration valKey, valElem valueCache // re-usable values for map iteration
iter reflect.MapIter // re-usable map iterator iter reflect.MapIter // re-usable map iterator
} }
@ -334,15 +336,19 @@ func (h *hasher) hashMap(v reflect.Value) {
defer iter.Reset(reflect.Value{}) // avoid pinning v from mh.iter when we return defer iter.Reset(reflect.Value{}) // avoid pinning v from mh.iter when we return
var sum Sum var sum Sum
k := mh.val.get(v.Type().Key()) if v.IsNil() {
e := mh.val.get(v.Type().Elem()) sum.sum[0] = 1 // something non-zero
}
k := mh.valKey.get(v.Type().Key())
e := mh.valElem.get(v.Type().Elem())
mh.h.visitStack = h.visitStack // always use the parent's visit stack to avoid cycles mh.h.visitStack = h.visitStack // always use the parent's visit stack to avoid cycles
for iter.Next() { for iter.Next() {
k.SetIterKey(iter) k.SetIterKey(iter)
e.SetIterValue(iter) e.SetIterValue(iter)
mh.h.reset() mh.h.reset()
mh.h.hashValue(k) mh.h.hashValue(k)
mh.h.hashValue(v) mh.h.hashValue(e)
sum.xor(mh.h.sum()) sum.xor(mh.h.sum())
} }
h.bw.Write(append(h.scratch[:0], sum.sum[:]...)) // append into scratch to avoid heap allocation h.bw.Write(append(h.scratch[:0], sum.sum[:]...)) // append into scratch to avoid heap allocation

@ -11,9 +11,11 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"math" "math"
"math/rand"
"reflect" "reflect"
"runtime" "runtime"
"testing" "testing"
"testing/quick"
"go4.org/mem" "go4.org/mem"
"inet.af/netaddr" "inet.af/netaddr"
@ -132,6 +134,49 @@ func TestDeepHash(t *testing.T) {
} }
} }
// Tests that we actually hash map elements. Whoops.
func TestIssue4868(t *testing.T) {
m1 := map[int]string{1: "foo"}
m2 := map[int]string{1: "bar"}
if Hash(m1) == Hash(m2) {
t.Error("bogus")
}
}
func TestIssue4871(t *testing.T) {
m1 := map[string]string{"": "", "x": "foo"}
m2 := map[string]string{}
if h1, h2 := Hash(m1), Hash(m2); h1 == h2 {
t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
}
}
func TestNilVsEmptymap(t *testing.T) {
m1 := map[string]string(nil)
m2 := map[string]string{}
if h1, h2 := Hash(m1), Hash(m2); h1 == h2 {
t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
}
}
func TestMapFraming(t *testing.T) {
m1 := map[string]string{"foo": "", "fo": "o"}
m2 := map[string]string{}
if h1, h2 := Hash(m1), Hash(m2); h1 == h2 {
t.Errorf("bogus: h1=%x, h2=%x", h1, h2)
}
}
func TestQuick(t *testing.T) {
initSeed()
err := quick.Check(func(v, w map[string]string) bool {
return (Hash(v) == Hash(w)) == reflect.DeepEqual(v, w)
}, &quick.Config{MaxCount: 1000, Rand: rand.New(rand.NewSource(int64(seed)))})
if err != nil {
t.Fatalf("seed=%v, err=%v", seed, err)
}
}
func getVal() []any { func getVal() []any {
return []any{ return []any{
&wgcfg.Config{ &wgcfg.Config{

Loading…
Cancel
Save