Compare commits

..

1 Commits

Author SHA1 Message Date
Denton Gentry
239b7cce74 util/codegen: Remove year from copyright header.
Copyright notices in software are not supposed to update
the year in the header.

Because we have a CI check for `go generate`, we're failing
CI until we go update all of the copyright headers in
generated files to say 2023.

Instead, relax the requirement to always have a year in the
copyright header.

Fixes https://github.com/tailscale/tailscale/issues/6865

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2023-01-01 16:36:10 -08:00
71 changed files with 471 additions and 674 deletions

View File

@@ -6,41 +6,27 @@ Private WireGuard® networks made easy
## Overview
This repository contains the majority of Tailscale's open source code.
Notably, it includes the `tailscaled` daemon and
the `tailscale` CLI tool. The `tailscaled` daemon runs on Linux, Windows,
[macOS](https://tailscale.com/kb/1065/macos-variants/), and to varying degrees
on FreeBSD and OpenBSD. The Tailscale iOS and Android apps use this repo's
code, but this repo doesn't contain the mobile GUI code.
This repository contains all the open source Tailscale client code and
the `tailscaled` daemon and `tailscale` CLI tool. The `tailscaled`
daemon runs on Linux, Windows and [macOS](https://tailscale.com/kb/1065/macos-variants/), and to varying degrees on FreeBSD, OpenBSD, and Darwin. (The Tailscale iOS and Android apps use this repo's code, but this repo doesn't contain the mobile GUI code.)
Other [Tailscale repos](https://github.com/orgs/tailscale/repositories) of note:
The Android app is at https://github.com/tailscale/tailscale-android
* the Android app is at https://github.com/tailscale/tailscale-android
* the Synology package is at https://github.com/tailscale/tailscale-synology
* the QNAP package is at https://github.com/tailscale/tailscale-qpkg
* the Chocolatey packaging is at https://github.com/tailscale/tailscale-chocolatey
For background on which parts of Tailscale are open source and why,
see [https://tailscale.com/opensource/](https://tailscale.com/opensource/).
The Synology package is at https://github.com/tailscale/tailscale-synology
## Using
We serve packages for a variety of distros and platforms at
[https://pkgs.tailscale.com](https://pkgs.tailscale.com/).
We serve packages for a variety of distros at
https://pkgs.tailscale.com .
## Other clients
The [macOS, iOS, and Windows clients](https://tailscale.com/download)
use the code in this repository but additionally include small GUI
wrappers. The GUI wrappers on non-open source platforms are themselves
not open source.
wrappers that are not open source.
## Building
We always require the latest Go release, currently Go 1.19. (While we build
releases with our [Go fork](https://github.com/tailscale/go/), its use is not
required.)
```
go install tailscale.com/cmd/tailscale{,d}
```
@@ -57,6 +43,8 @@ If your distro has conventions that preclude the use of
`build_dist.sh`, please do the equivalent of what it does in your
distro's way, so that bug reports contain useful version information.
We require the latest Go release, currently Go 1.19.
## Bugs
Please file any issues about this code or the hosted service on

View File

@@ -80,7 +80,7 @@ func main() {
w("}")
}
cloneOutput := pkg.Name + "_clone.go"
if err := codegen.WritePackageFile("tailscale.com/cmd/cloner", pkg, cloneOutput, codegen.CopyrightYear("."), it, buf); err != nil {
if err := codegen.WritePackageFile("tailscale.com/cmd/cloner", pkg, cloneOutput, it, buf); err != nil {
log.Fatal(err)
}
}

View File

@@ -12,7 +12,7 @@
// As with most container things, configuration is passed through environment
// variables. All configuration is optional.
//
// - TS_AUTHKEY: the authkey to use for login.
// - TS_AUTH_KEY: the authkey to use for login.
// - TS_ROUTES: subnet routes to advertise.
// - TS_DEST_IP: proxy all incoming Tailscale traffic to the given
// destination.
@@ -42,7 +42,7 @@
// TS_KUBE_SECRET="" and TS_STATE_DIR=/path/to/storage/dir. The state dir should
// be persistent storage.
//
// Additionally, if TS_AUTHKEY is not set and the TS_KUBE_SECRET contains an
// Additionally, if TS_AUTH_KEY is not set and the TS_KUBE_SECRET contains an
// "authkey" field, that key is used as the tailscale authkey.
package main
@@ -73,7 +73,7 @@ func main() {
tailscale.I_Acknowledge_This_API_Is_Unstable = true
cfg := &settings{
AuthKey: defaultEnvs([]string{"TS_AUTHKEY", "TS_AUTH_KEY"}, ""),
AuthKey: defaultEnv("TS_AUTH_KEY", ""),
Routes: defaultEnv("TS_ROUTES", ""),
ProxyTo: defaultEnv("TS_DEST_IP", ""),
DaemonExtraArgs: defaultEnv("TS_TAILSCALED_EXTRA_ARGS", ""),
@@ -548,15 +548,6 @@ func defaultEnv(name, defVal string) string {
return defVal
}
func defaultEnvs(names []string, defVal string) string {
for _, name := range names {
if v, ok := os.LookupEnv(name); ok {
return v
}
}
return defVal
}
// defaultBool returns the boolean value of the given envvar name, or
// defVal if unset or not a bool.
func defaultBool(name string, defVal bool) bool {

View File

@@ -146,24 +146,6 @@ func TestContainerBoot(t *testing.T) {
{
// Userspace mode, ephemeral storage, authkey provided on every run.
Name: "authkey",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
},
Phases: []phase{
{
WantCmds: []string{
"/usr/bin/tailscaled --socket=/tmp/tailscaled.sock --state=mem: --statedir=/tmp --tun=userspace-networking",
"/usr/bin/tailscale --socket=/tmp/tailscaled.sock up --accept-dns=false --authkey=tskey-key",
},
},
{
Notify: runningNotify,
},
},
},
{
// Userspace mode, ephemeral storage, authkey provided on every run.
Name: "authkey-old-flag",
Env: map[string]string{
"TS_AUTH_KEY": "tskey-key",
},
@@ -182,7 +164,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "authkey_disk_state",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_STATE_DIR": filepath.Join(d, "tmp"),
},
Phases: []phase{
@@ -200,8 +182,8 @@ func TestContainerBoot(t *testing.T) {
{
Name: "routes",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_ROUTES": "1.2.3.0/24,10.20.30.0/24",
"TS_AUTH_KEY": "tskey-key",
"TS_ROUTES": "1.2.3.0/24,10.20.30.0/24",
},
Phases: []phase{
{
@@ -222,7 +204,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "routes_kernel_ipv4",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_ROUTES": "1.2.3.0/24,10.20.30.0/24",
"TS_USERSPACE": "false",
},
@@ -245,7 +227,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "routes_kernel_ipv6",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_ROUTES": "::/64,1::/64",
"TS_USERSPACE": "false",
},
@@ -268,7 +250,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "routes_kernel_all_families",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_ROUTES": "::/64,1.2.3.0/24",
"TS_USERSPACE": "false",
},
@@ -291,7 +273,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "proxy",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_DEST_IP": "1.2.3.4",
"TS_USERSPACE": "false",
},
@@ -313,7 +295,7 @@ func TestContainerBoot(t *testing.T) {
{
Name: "authkey_once",
Env: map[string]string{
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
"TS_AUTH_ONCE": "true",
},
Phases: []phase{
@@ -372,7 +354,7 @@ func TestContainerBoot(t *testing.T) {
// Explicitly set to an empty value, to override the default of "tailscale".
"TS_KUBE_SECRET": "",
"TS_STATE_DIR": filepath.Join(d, "tmp"),
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
},
KubeSecret: map[string]string{},
Phases: []phase{
@@ -394,7 +376,7 @@ func TestContainerBoot(t *testing.T) {
Env: map[string]string{
"KUBERNETES_SERVICE_HOST": kube.Host,
"KUBERNETES_SERVICE_PORT_HTTPS": kube.Port,
"TS_AUTHKEY": "tskey-key",
"TS_AUTH_KEY": "tskey-key",
},
KubeSecret: map[string]string{},
KubeDenyPatch: true,

View File

@@ -134,7 +134,6 @@ var debugCmd = &ffcli.Command{
fs := newFlagSet("watch-ipn")
fs.BoolVar(&watchIPNArgs.netmap, "netmap", true, "include netmap in messages")
fs.BoolVar(&watchIPNArgs.initial, "initial", false, "include initial status")
fs.BoolVar(&watchIPNArgs.showPrivateKey, "show-private-key", false, "include node private key in printed netmap")
return fs
})(),
},
@@ -320,9 +319,8 @@ func runPrefs(ctx context.Context, args []string) error {
}
var watchIPNArgs struct {
netmap bool
initial bool
showPrivateKey bool
netmap bool
initial bool
}
func runWatchIPN(ctx context.Context, args []string) error {
@@ -330,9 +328,6 @@ func runWatchIPN(ctx context.Context, args []string) error {
if watchIPNArgs.initial {
mask = ipn.NotifyInitialState | ipn.NotifyInitialPrefs | ipn.NotifyInitialNetMap
}
if !watchIPNArgs.showPrivateKey {
mask |= ipn.NotifyNoPrivateKeys
}
watcher, err := localClient.WatchIPNBus(ctx, mask)
if err != nil {
return err

View File

@@ -5,7 +5,6 @@
package cli
import (
"bytes"
"context"
"crypto/rand"
"encoding/hex"
@@ -100,22 +99,6 @@ func runNetworkLockInit(ctx context.Context, args []string) error {
return err
}
// Common mistake: Not specifying the current node's key as one of the trusted keys.
foundSelfKey := false
for _, k := range keys {
keyID, err := k.ID()
if err != nil {
return err
}
if bytes.Equal(keyID, st.PublicKey.KeyID()) {
foundSelfKey = true
break
}
}
if !foundSelfKey {
return errors.New("the tailnet lock key of the current node must be one of the trusted keys during initialization")
}
fmt.Println("You are initializing tailnet lock with the following trusted signing keys:")
for _, k := range keys {
fmt.Printf(" - tlpub:%x (%s key)\n", k.Public, k.Kind.String())
@@ -213,7 +196,7 @@ func runNetworkLockStatus(ctx context.Context, args []string) error {
line.WriteString(fmt.Sprint(k.Votes))
line.WriteString("\t")
if k.Key == st.PublicKey {
line.WriteString("(self)")
line.WriteString("(us)")
}
fmt.Println(line.String())
}
@@ -461,13 +444,8 @@ func nlDescribeUpdate(update ipnstate.NetworkLockUpdate, color bool) (string, er
var stanza strings.Builder
printKey := func(key *tka.Key, prefix string) {
fmt.Fprintf(&stanza, "%sType: %s\n", prefix, key.Kind.String())
if keyID, err := key.ID(); err == nil {
fmt.Fprintf(&stanza, "%sKeyID: %x\n", prefix, keyID)
} else {
// Older versions of the client shouldn't explode when they encounter an
// unknown key type.
fmt.Fprintf(&stanza, "%sKeyID: <Error: %v>\n", prefix, err)
}
fmt.Fprintf(&stanza, "%sKeyID: %x\n", prefix, key.ID())
fmt.Fprintf(&stanza, "%sVotes: %d\n", prefix, key.Votes)
if key.Meta != nil {
fmt.Fprintf(&stanza, "%sMetadata: %+v\n", prefix, key.Meta)
}

View File

@@ -223,8 +223,8 @@ func qnapAuthnQtoken(r *http.Request, user, token string) (string, *qnapAuthResp
"user": []string{user},
}
u := url.URL{
Scheme: "http",
Host: "127.0.0.1:8080",
Scheme: r.URL.Scheme,
Host: r.URL.Host,
Path: "/cgi-bin/authLogin.cgi",
RawQuery: query.Encode(),
}
@@ -237,8 +237,8 @@ func qnapAuthnSid(r *http.Request, user, sid string) (string, *qnapAuthResponse,
"sid": []string{sid},
}
u := url.URL{
Scheme: "http",
Host: "127.0.0.1:8080",
Scheme: r.URL.Scheme,
Host: r.URL.Host,
Path: "/cgi-bin/authLogin.cgi",
RawQuery: query.Encode(),
}

View File

@@ -247,7 +247,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/tsaddr from tailscale.com/ipn+
tailscale.com/net/tsdial from tailscale.com/control/controlclient+
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
tailscale.com/net/tstun from tailscale.com/net/dns+
tailscale.com/net/wsconn from tailscale.com/control/controlhttp+
tailscale.com/paths from tailscale.com/ipn/ipnlocal+
💣 tailscale.com/portlist from tailscale.com/ipn/ipnlocal

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || darwin || freebsd || openbsd
//go:build linux || darwin || freebsd
package main

View File

@@ -526,9 +526,6 @@ func getLocalBackend(ctx context.Context, logf logger.Logf, logid string) (_ *ip
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
}
lb.SetVarRoot(opts.VarRoot)
if logPol != nil {
lb.SetLogFlusher(logPol.Logtail.StartFlush)
}
if root := lb.TailscaleVarRoot(); root != "" {
dnsfallback.SetCachePath(filepath.Join(root, "derpmap.cached.json"))
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -384,7 +384,7 @@ func main() {
genView(buf, it, typ, pkg.Types)
}
out := pkg.Name + "_view.go"
if err := codegen.WritePackageFile("tailscale/cmd/viewer", pkg, out, codegen.CopyrightYear("."), it, buf); err != nil {
if err := codegen.WritePackageFile("tailscale/cmd/viewer", pkg, out, it, buf); err != nil {
log.Fatal(err)
}
if runCloner {

View File

@@ -16,7 +16,6 @@ import (
"tailscale.com/types/logger"
"tailscale.com/types/netmap"
"tailscale.com/types/opt"
"tailscale.com/types/views"
"tailscale.com/wgengine/filter"
)
@@ -41,7 +40,6 @@ type mapSession struct {
lastDNSConfig *tailcfg.DNSConfig
lastDERPMap *tailcfg.DERPMap
lastUserProfile map[tailcfg.UserID]tailcfg.UserProfile
lastPacketFilterRules views.Slice[tailcfg.FilterRule]
lastParsedPacketFilter []filter.Match
lastSSHPolicy *tailcfg.SSHPolicy
collectServices bool
@@ -98,7 +96,6 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
if pf := resp.PacketFilter; pf != nil {
var err error
ms.lastPacketFilterRules = views.SliceOf(pf)
ms.lastParsedPacketFilter, err = filter.MatchesFromFilterRules(pf)
if err != nil {
ms.logf("parsePacketFilter: %v", err)
@@ -150,22 +147,21 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
}
nm := &netmap.NetworkMap{
NodeKey: ms.privateNodeKey.Public(),
PrivateKey: ms.privateNodeKey,
MachineKey: ms.machinePubKey,
Peers: resp.Peers,
UserProfiles: make(map[tailcfg.UserID]tailcfg.UserProfile),
Domain: ms.lastDomain,
DomainAuditLogID: ms.lastDomainAuditLogID,
DNS: *ms.lastDNSConfig,
PacketFilter: ms.lastParsedPacketFilter,
PacketFilterRules: ms.lastPacketFilterRules,
SSHPolicy: ms.lastSSHPolicy,
CollectServices: ms.collectServices,
DERPMap: ms.lastDERPMap,
Debug: debug,
ControlHealth: ms.lastHealth,
TKAEnabled: ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
NodeKey: ms.privateNodeKey.Public(),
PrivateKey: ms.privateNodeKey,
MachineKey: ms.machinePubKey,
Peers: resp.Peers,
UserProfiles: make(map[tailcfg.UserID]tailcfg.UserProfile),
Domain: ms.lastDomain,
DomainAuditLogID: ms.lastDomainAuditLogID,
DNS: *ms.lastDNSConfig,
PacketFilter: ms.lastParsedPacketFilter,
SSHPolicy: ms.lastSSHPolicy,
CollectServices: ms.collectServices,
DERPMap: ms.lastDERPMap,
Debug: debug,
ControlHealth: ms.lastHealth,
TKAEnabled: ms.lastTKAInfo != nil && !ms.lastTKAInfo.Disabled,
}
ms.netMapBuilding = nm

5
go.mod
View File

@@ -64,7 +64,7 @@ require (
github.com/tc-hib/winres v0.1.6
github.com/tcnksm/go-httpstat v0.2.0
github.com/toqueteos/webbrowser v1.2.0
github.com/u-root/u-root v0.9.1-0.20230109201855-948a78c969ad
github.com/u-root/u-root v0.9.1-0.20221111022710-6e9699743f5d
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
go.uber.org/zap v1.21.0
go4.org/mem v0.0.0-20210711025021-927187094b94
@@ -104,7 +104,7 @@ require (
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/OpenPeeDeeP/depguard v1.0.1 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
@@ -134,7 +134,6 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/charithe/durationcheck v0.0.9 // indirect
github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.11.4 // indirect
github.com/daixiang0/gci v0.2.9 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect

13
go.sum
View File

@@ -90,8 +90,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
@@ -195,7 +195,6 @@ github.com/breml/bidichk v0.2.1 h1:SRNtZuLdfkxtocj+xyHXKC1Uv3jVi6EPYx+NHSTNQvE=
github.com/breml/bidichk v0.2.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso=
github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY=
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -213,8 +212,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -1197,9 +1194,8 @@ github.com/tommy-muehle/go-mnd/v2 v2.4.0 h1:1t0f8Uiaq+fqKteUR4N9Umr6E99R+lDnLnq7
github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90 h1:zTk5683I9K62wtZ6eUa6vu6IWwVHXPnoKK5n2unAwv0=
github.com/u-root/u-root v0.9.1-0.20230109201855-948a78c969ad h1:0lEUXaz1vhlAtoMpu18vhb16s5rGRpNCl2trxc2/Qbg=
github.com/u-root/u-root v0.9.1-0.20230109201855-948a78c969ad/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY=
github.com/u-root/u-root v0.9.1-0.20221111022710-6e9699743f5d h1:sT5Q2xFrqgm/3yrCkVLkVuEFpG07UXz9ALqxxN1SmZc=
github.com/u-root/u-root v0.9.1-0.20221111022710-6e9699743f5d/go.mod h1:jMbuI3nENTNzHW9mYwQ57b8/DSuJTq+joYY18x/WGxE=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
@@ -1568,7 +1564,6 @@ golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211002104244-808efd93c36d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@@ -70,9 +70,6 @@ const (
// SysDNSManager is the name of the net/dns manager subsystem.
SysDNSManager = Subsystem("dns-manager")
// SysTKA is the name of the tailnet key authority subsystem.
SysTKA = Subsystem("tailnet-lock")
)
// NewWarnable returns a new warnable item that the caller can mark
@@ -197,12 +194,6 @@ func SetDNSManagerHealth(err error) { setErr(SysDNSManager, err) }
// DNSOSHealth returns the net/dns.OSConfigurator error state.
func DNSOSHealth() error { return get(SysDNSOS) }
// SetTKAHealth sets the health of the tailnet key authority.
func SetTKAHealth(err error) { setErr(SysTKA, err) }
// TKAHealth returns the tailnet key authority error state.
func TKAHealth() error { return get(SysTKA) }
// SetLocalLogConfigHealth sets the error state of this client's local log configuration.
func SetLocalLogConfigHealth(err error) {
mu.Lock()

View File

@@ -65,8 +65,6 @@ const (
NotifyInitialState // if set, the first Notify message (sent immediately) will contain the current State + BrowseToURL
NotifyInitialPrefs // if set, the first Notify message (sent immediately) will contain the current Prefs
NotifyInitialNetMap // if set, the first Notify message (sent immediately) will contain the current NetMap
NotifyNoPrivateKeys // if set, private keys that would normally be sent in updates are zeroed out
)
// Notify is a communication from a backend (e.g. tailscaled) to a frontend

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -26,16 +26,6 @@ func (b *LocalBackend) handleC2N(w http.ResponseWriter, r *http.Request) {
// Test handler.
body, _ := io.ReadAll(r.Body)
w.Write(body)
case "/logtail/flush":
if r.Method != "POST" {
http.Error(w, "bad method", http.StatusMethodNotAllowed)
return
}
if b.TryFlushLogs() {
w.WriteHeader(http.StatusNoContent)
} else {
http.Error(w, "no log flusher wired up", http.StatusInternalServerError)
}
case "/debug/goroutines":
w.Header().Set("Content-Type", "text/plain")
w.Write(goroutines.ScrubbedGoroutineDump())

View File

@@ -140,7 +140,6 @@ type LocalBackend struct {
gotPortPollRes chan struct{} // closed upon first readPoller result
newDecompressor func() (controlclient.Decompressor, error)
varRoot string // or empty if SetVarRoot never called
logFlushFunc func() // or nil if SetLogFlusher wasn't called
sshAtomicBool atomic.Bool
shutdownCalled bool // if Shutdown has been called
@@ -1743,24 +1742,6 @@ func (b *LocalBackend) readPoller() {
func (b *LocalBackend) WatchNotifications(ctx context.Context, mask ipn.NotifyWatchOpt, fn func(roNotify *ipn.Notify) (keepGoing bool)) {
ch := make(chan *ipn.Notify, 128)
origFn := fn
if mask&ipn.NotifyNoPrivateKeys != 0 {
fn = func(n *ipn.Notify) bool {
if n.NetMap == nil || n.NetMap.PrivateKey.IsZero() {
return origFn(n)
}
// The netmap in n is shared across all watchers, so to mutate it for a
// single watcher we have to clone the notify and the netmap. We can
// make shallow clones, at least.
nm2 := *n.NetMap
n2 := *n
n2.NetMap = &nm2
n2.NetMap.PrivateKey = key.NodePrivate{}
return origFn(&n2)
}
}
var ini *ipn.Notify
b.mu.Lock()
@@ -2414,7 +2395,7 @@ func (b *LocalBackend) checkSSHPrefsLocked(p *ipn.Prefs) error {
if !envknob.UseWIPCode() {
return errors.New("The Tailscale SSH server is disabled on macOS tailscaled by default. To try, set env TAILSCALE_USE_WIP_CODE=1")
}
case "freebsd", "openbsd":
case "freebsd":
default:
return errors.New("The Tailscale SSH server is not supported on " + runtime.GOOS)
}
@@ -3016,25 +2997,6 @@ func (b *LocalBackend) SetVarRoot(dir string) {
b.varRoot = dir
}
// SetLogFlusher sets a func to be called to flush log uploads.
//
// It should only be called before the LocalBackend is used.
func (b *LocalBackend) SetLogFlusher(flushFunc func()) {
b.logFlushFunc = flushFunc
}
// TryFlushLogs calls the log flush function. It returns false if a log flush
// function was never initialized with SetLogFlusher.
//
// TryFlushLogs should not block.
func (b *LocalBackend) TryFlushLogs() bool {
if b.logFlushFunc == nil {
return false
}
b.logFlushFunc()
return true
}
// TailscaleVarRoot returns the root directory of Tailscale's writable
// storage area. (e.g. "/var/lib/tailscale")
//

View File

@@ -20,7 +20,6 @@ import (
"time"
"tailscale.com/envknob"
"tailscale.com/health"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
@@ -61,11 +60,9 @@ func (b *LocalBackend) permitTKAInitLocked() bool {
func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
// TODO(tom): Remove this guard for 1.35 and later.
if b.tka == nil && !b.permitTKAInitLocked() {
health.SetTKAHealth(nil)
return
}
if b.tka == nil {
health.SetTKAHealth(nil)
return // TKA not enabled.
}
@@ -114,13 +111,6 @@ func (b *LocalBackend) tkaFilterNetmapLocked(nm *netmap.NetworkMap) {
} else {
b.tka.filtered = nil
}
// Check that we ourselves are not locked out, report a health issue if so.
if nm.SelfNode != nil && b.tka.authority.NodeKeyAuthorized(nm.SelfNode.Key, nm.SelfNode.KeySignature) != nil {
health.SetTKAHealth(errors.New("this node is locked out; it will not have connectivity until it is signed. For more info, see https://tailscale.com/s/locked-out"))
} else {
health.SetTKAHealth(nil)
}
}
// tkaSyncIfNeeded examines TKA info reported from the control plane,
@@ -187,7 +177,6 @@ func (b *LocalBackend) tkaSyncIfNeeded(nm *netmap.NetworkMap, prefs ipn.PrefsVie
b.logf("Disablement failed, leaving TKA enabled. Error: %v", err)
} else {
isEnabled = false
health.SetTKAHealth(nil)
}
} else {
return fmt.Errorf("[bug] unreachable invariant of wantEnabled /w isEnabled")
@@ -677,11 +666,7 @@ func (b *LocalBackend) NetworkLockModify(addKeys, removeKeys []tka.Key) (err err
}
}
for _, removeKey := range removeKeys {
keyID, err := removeKey.ID()
if err != nil {
return err
}
if err := updater.RemoveKey(keyID); err != nil {
if err := updater.RemoveKey(removeKey.ID()); err != nil {
return err
}
}

View File

@@ -321,7 +321,7 @@ func TestTKASync(t *testing.T) {
name: "control has an update",
controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer)
if err := b.RemoveKey(someKey.MustID()); err != nil {
if err := b.RemoveKey(someKey.ID()); err != nil {
t.Fatal(err)
}
aums, err := b.Finalize(storage)
@@ -336,7 +336,7 @@ func TestTKASync(t *testing.T) {
name: "node has an update",
nodeAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer)
if err := b.RemoveKey(someKey.MustID()); err != nil {
if err := b.RemoveKey(someKey.ID()); err != nil {
t.Fatal(err)
}
aums, err := b.Finalize(storage)
@@ -351,7 +351,7 @@ func TestTKASync(t *testing.T) {
name: "node and control diverge",
controlAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer)
if err := b.SetKeyMeta(someKey.MustID(), map[string]string{"ye": "swiggity"}); err != nil {
if err := b.SetKeyMeta(someKey.ID(), map[string]string{"ye": "swiggity"}); err != nil {
t.Fatal(err)
}
aums, err := b.Finalize(storage)
@@ -362,7 +362,7 @@ func TestTKASync(t *testing.T) {
},
nodeAUMs: func(t *testing.T, a *tka.Authority, storage tka.Chonk, signer tka.Signer) []tka.AUM {
b := a.NewUpdater(signer)
if err := b.SetKeyMeta(someKey.MustID(), map[string]string{"ye": "swooty"}); err != nil {
if err := b.SetKeyMeta(someKey.ID(), map[string]string{"ye": "swooty"}); err != nil {
t.Fatal(err)
}
aums, err := b.Finalize(storage)

View File

@@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"math/rand"
"net/netip"
"runtime"
"time"
@@ -20,7 +19,6 @@ import (
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
"tailscale.com/util/strs"
"tailscale.com/util/winutil"
"tailscale.com/version"
)
@@ -324,7 +322,7 @@ func (pm *profileManager) setAsUserSelectedProfileLocked() error {
func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error) {
bs, err := pm.store.ReadState(key)
if err == ipn.ErrStateNotExist || len(bs) == 0 {
return defaultPrefs, nil
return emptyPrefs, nil
}
if err != nil {
return ipn.PrefsView{}, err
@@ -396,29 +394,15 @@ func (pm *profileManager) writeKnownProfiles() error {
func (pm *profileManager) NewProfile() {
metricNewProfile.Add(1)
pm.prefs = defaultPrefs
pm.prefs = emptyPrefs
pm.isNewProfile = true
pm.currentProfile = &ipn.LoginProfile{}
}
// defaultPrefs is the default prefs for a new profile.
var defaultPrefs = func() ipn.PrefsView {
// emptyPrefs is the default prefs for a new profile.
var emptyPrefs = func() ipn.PrefsView {
prefs := ipn.NewPrefs()
prefs.WantRunning = false
prefs.ControlURL = winutil.GetPolicyString("LoginURL", "")
if exitNode := winutil.GetPolicyString("ExitNodeIP", ""); exitNode != "" {
if ip, err := netip.ParseAddr(exitNode); err == nil {
prefs.ExitNodeIP = ip
}
}
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
// backend), so this has to convert between the two conventions.
prefs.ShieldsUp = winutil.GetPolicyString("AllowIncomingConnections", "") == "never"
prefs.ForceDaemon = winutil.GetPolicyString("UnattendedMode", "") == "always"
return prefs.View()
}()

View File

@@ -53,7 +53,7 @@ func TestProfileCurrentUserSwitch(t *testing.T) {
} else if pm.currentProfile.ID != "" {
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID)
}
if !pm.CurrentPrefs().Equals(defaultPrefs) {
if !pm.CurrentPrefs().Equals(emptyPrefs) {
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
}
@@ -67,7 +67,7 @@ func TestProfileCurrentUserSwitch(t *testing.T) {
} else if pm.currentProfile.ID != "" {
t.Fatalf("currentProfile.ID = %q, want empty", pm.currentProfile.ID)
}
if !pm.CurrentPrefs().Equals(defaultPrefs) {
if !pm.CurrentPrefs().Equals(emptyPrefs) {
t.Fatalf("CurrentPrefs() = %v, want emptyPrefs", pm.CurrentPrefs().Pretty())
}
}
@@ -159,7 +159,7 @@ func TestProfileManagement(t *testing.T) {
}
wantCurProfile := ""
wantProfiles := map[string]ipn.PrefsView{
"": defaultPrefs,
"": emptyPrefs,
}
checkProfiles := func(t *testing.T) {
t.Helper()
@@ -237,7 +237,7 @@ func TestProfileManagement(t *testing.T) {
t.Logf("Create new profile")
pm.NewProfile()
wantCurProfile = ""
wantProfiles[""] = defaultPrefs
wantProfiles[""] = emptyPrefs
checkProfiles(t)
{
@@ -276,7 +276,7 @@ func TestProfileManagement(t *testing.T) {
t.Logf("Create new profile - 2")
pm.NewProfile()
wantCurProfile = ""
wantProfiles[""] = defaultPrefs
wantProfiles[""] = emptyPrefs
checkProfiles(t)
t.Logf("Login with the existing profile")
@@ -310,7 +310,7 @@ func TestProfileManagementWindows(t *testing.T) {
}
wantCurProfile := ""
wantProfiles := map[string]ipn.PrefsView{
"": defaultPrefs,
"": emptyPrefs,
}
checkProfiles := func(t *testing.T) {
t.Helper()
@@ -363,7 +363,7 @@ func TestProfileManagementWindows(t *testing.T) {
t.Logf("Create new profile")
pm.NewProfile()
wantCurProfile = ""
wantProfiles[""] = defaultPrefs
wantProfiles[""] = emptyPrefs
checkProfiles(t)
t.Logf("Save as test profile")
@@ -380,7 +380,7 @@ func TestProfileManagementWindows(t *testing.T) {
t.Fatal(err)
}
wantCurProfile = ""
wantProfiles[""] = defaultPrefs
wantProfiles[""] = emptyPrefs
checkProfiles(t)
{

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || (darwin && !ios) || freebsd || openbsd
//go:build linux || (darwin && !ios) || freebsd
package ipnlocal

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ios || (!linux && !darwin && !freebsd && !openbsd)
//go:build ios || (!linux && !darwin && !freebsd)
package ipnlocal

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -61,41 +61,39 @@ var handler = map[string]localAPIHandler{
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
// without a trailing slash:
"bugreport": (*Handler).serveBugReport,
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
"check-prefs": (*Handler).serveCheckPrefs,
"component-debug-logging": (*Handler).serveComponentDebugLogging,
"debug": (*Handler).serveDebug,
"debug-derp-region": (*Handler).serveDebugDERPRegion,
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
"derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore,
"dial": (*Handler).serveDial,
"file-targets": (*Handler).serveFileTargets,
"goroutines": (*Handler).serveGoroutines,
"id-token": (*Handler).serveIDToken,
"login-interactive": (*Handler).serveLoginInteractive,
"logout": (*Handler).serveLogout,
"metrics": (*Handler).serveMetrics,
"ping": (*Handler).servePing,
"prefs": (*Handler).servePrefs,
"pprof": (*Handler).servePprof,
"serve-config": (*Handler).serveServeConfig,
"set-dns": (*Handler).serveSetDNS,
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
"start": (*Handler).serveStart,
"status": (*Handler).serveStatus,
"tka/init": (*Handler).serveTKAInit,
"tka/log": (*Handler).serveTKALog,
"tka/modify": (*Handler).serveTKAModify,
"tka/sign": (*Handler).serveTKASign,
"tka/status": (*Handler).serveTKAStatus,
"tka/disable": (*Handler).serveTKADisable,
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
"whois": (*Handler).serveWhoIs,
"bugreport": (*Handler).serveBugReport,
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
"check-prefs": (*Handler).serveCheckPrefs,
"component-debug-logging": (*Handler).serveComponentDebugLogging,
"debug": (*Handler).serveDebug,
"debug-derp-region": (*Handler).serveDebugDERPRegion,
"derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore,
"dial": (*Handler).serveDial,
"file-targets": (*Handler).serveFileTargets,
"goroutines": (*Handler).serveGoroutines,
"id-token": (*Handler).serveIDToken,
"login-interactive": (*Handler).serveLoginInteractive,
"logout": (*Handler).serveLogout,
"metrics": (*Handler).serveMetrics,
"ping": (*Handler).servePing,
"prefs": (*Handler).servePrefs,
"pprof": (*Handler).servePprof,
"serve-config": (*Handler).serveServeConfig,
"set-dns": (*Handler).serveSetDNS,
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
"start": (*Handler).serveStart,
"status": (*Handler).serveStatus,
"tka/init": (*Handler).serveTKAInit,
"tka/log": (*Handler).serveTKALog,
"tka/modify": (*Handler).serveTKAModify,
"tka/sign": (*Handler).serveTKASign,
"tka/status": (*Handler).serveTKAStatus,
"tka/disable": (*Handler).serveTKADisable,
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
"whois": (*Handler).serveWhoIs,
}
func randHex(n int) string {
@@ -292,7 +290,6 @@ func (h *Handler) serveBugReport(w http.ResponseWriter, r *http.Request) {
http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
return
}
defer h.b.TryFlushLogs() // kick off upload after bugreport's done logging
logMarker := func() string {
return fmt.Sprintf("BUG-%v-%v-%v", h.backendLogID, time.Now().UTC().Format("20060102150405Z"), randHex(8))
@@ -509,40 +506,6 @@ func (h *Handler) serveDevSetStateStore(w http.ResponseWriter, r *http.Request)
io.WriteString(w, "done\n")
}
func (h *Handler) serveDebugPacketFilterRules(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)
return
}
nm := h.b.NetMap()
if nm == nil {
http.Error(w, "no netmap", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", "\t")
enc.Encode(nm.PacketFilterRules)
}
func (h *Handler) serveDebugPacketFilterMatches(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)
return
}
nm := h.b.NetMap()
if nm == nil {
http.Error(w, "no netmap", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
enc.SetIndent("", "\t")
enc.Encode(nm.PacketFilter)
}
func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "debug access denied", http.StatusForbidden)

View File

@@ -57,12 +57,12 @@ Client][]. See also the dependencies in the [Tailscale CLI][].
- [golang.org/x/exp/shiny](https://pkg.go.dev/golang.org/x/exp/shiny) ([BSD-3-Clause](https://cs.opensource.google/go/x/exp/+/334a2380:shiny/LICENSE))
- [golang.org/x/image](https://pkg.go.dev/golang.org/x/image) ([BSD-3-Clause](https://cs.opensource.google/go/x/image/+/062f8c9f:LICENSE))
- [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/v0.2.0:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/v0.1.0:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/2204b661:LICENSE))
- [golang.org/x/sync/errgroup](https://pkg.go.dev/golang.org/x/sync/errgroup) ([BSD-3-Clause](https://cs.opensource.google/go/x/sync/+/886fb937:LICENSE))
- [golang.org/x/sys](https://pkg.go.dev/golang.org/x/sys) ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/v0.2.0:LICENSE))
- [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) ([BSD-3-Clause](https://cs.opensource.google/go/x/term/+/v0.2.0:LICENSE))
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) ([BSD-3-Clause](https://cs.opensource.google/go/x/text/+/v0.4.0:LICENSE))
- [golang.org/x/time/rate](https://pkg.go.dev/golang.org/x/time/rate) ([BSD-3-Clause](https://cs.opensource.google/go/x/time/+/579cf78f:LICENSE))
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/703fd9b7fbc0/LICENSE))
- [gvisor.dev/gvisor/pkg](https://pkg.go.dev/gvisor.dev/gvisor/pkg) ([Apache-2.0](https://github.com/google/gvisor/blob/846276b3dbc5/LICENSE))
- [inet.af/netaddr](https://pkg.go.dev/inet.af/netaddr) ([BSD-3-Clause](https://github.com/inetaf/netaddr/blob/097006376321/LICENSE))
- [inet.af/peercred](https://pkg.go.dev/inet.af/peercred) ([BSD-3-Clause](https://github.com/inetaf/peercred/blob/0893ea02156a/LICENSE))
- [nhooyr.io/websocket](https://pkg.go.dev/nhooyr.io/websocket) ([MIT](https://github.com/nhooyr/websocket/blob/v1.8.7/LICENSE.txt))

View File

@@ -69,7 +69,7 @@ Some packages may only be included on certain architectures or operating systems
- [github.com/tailscale/wireguard-go](https://pkg.go.dev/github.com/tailscale/wireguard-go) ([MIT](https://github.com/tailscale/wireguard-go/blob/4fa124729667/LICENSE))
- [github.com/tcnksm/go-httpstat](https://pkg.go.dev/github.com/tcnksm/go-httpstat) ([MIT](https://github.com/tcnksm/go-httpstat/blob/v0.2.0/LICENSE))
- [github.com/toqueteos/webbrowser](https://pkg.go.dev/github.com/toqueteos/webbrowser) ([MIT](https://github.com/toqueteos/webbrowser/blob/v1.2.0/LICENSE.md))
- [github.com/u-root/u-root/pkg/termios](https://pkg.go.dev/github.com/u-root/u-root/pkg/termios) ([BSD-3-Clause](https://github.com/u-root/u-root/blob/948a78c969ad/LICENSE))
- [github.com/u-root/u-root/pkg/termios](https://pkg.go.dev/github.com/u-root/u-root/pkg/termios) ([BSD-3-Clause](https://github.com/u-root/u-root/blob/6e9699743f5d/LICENSE))
- [github.com/u-root/uio](https://pkg.go.dev/github.com/u-root/uio) ([BSD-3-Clause](https://github.com/u-root/uio/blob/c3537552635f/LICENSE))
- [github.com/vishvananda/netlink/nl](https://pkg.go.dev/github.com/vishvananda/netlink/nl) ([Apache-2.0](https://github.com/vishvananda/netlink/blob/650dca95af54/LICENSE))
- [github.com/vishvananda/netns](https://pkg.go.dev/github.com/vishvananda/netns) ([Apache-2.0](https://github.com/vishvananda/netns/blob/50045581ed74/LICENSE))

View File

@@ -66,12 +66,13 @@ type Config struct {
// that's safe to embed in a JSON string literal without further escaping.
MetricsDelta func() string
// FlushDelayFn, if non-nil is a func that returns how long to wait to
// accumulate logs before uploading them. 0 or negative means to upload
// immediately.
// FlushDelay is how long to wait to accumulate logs before
// uploading them.
//
// If nil, a default value is used. (currently 2 seconds)
FlushDelayFn func() time.Duration
// If zero, a default value is used. (currently 2 seconds)
//
// Negative means to upload immediately.
FlushDelay time.Duration
// IncludeProcID, if true, results in an ephemeral process identifier being
// included in logs. The ID is random and not guaranteed to be globally
@@ -117,13 +118,13 @@ func NewLogger(cfg Config, logf tslogger.Logf) *Logger {
}
}
if s := envknob.String("TS_DEBUG_LOGTAIL_FLUSHDELAY"); s != "" {
if delay, err := time.ParseDuration(s); err == nil {
cfg.FlushDelayFn = func() time.Duration { return delay }
} else {
var err error
cfg.FlushDelay, err = time.ParseDuration(s)
if err != nil {
log.Fatalf("invalid TS_DEBUG_LOGTAIL_FLUSHDELAY: %v", err)
}
} else if cfg.FlushDelayFn == nil && envknob.Bool("IN_TS_TEST") {
cfg.FlushDelayFn = func() time.Duration { return 0 }
} else if cfg.FlushDelay == 0 && !envknob.Bool("IN_TS_TEST") {
cfg.FlushDelay = defaultFlushDelay
}
stdLogf := func(f string, a ...any) {
@@ -144,7 +145,7 @@ func NewLogger(cfg Config, logf tslogger.Logf) *Logger {
skipClientTime: cfg.SkipClientTime,
drainWake: make(chan struct{}, 1),
sentinel: make(chan int32, 16),
flushDelayFn: cfg.FlushDelayFn,
flushDelay: cfg.FlushDelay,
timeNow: cfg.TimeNow,
bo: backoff.NewBackoff("logtail", stdLogf, 30*time.Second),
metricsDelta: cfg.MetricsDelta,
@@ -178,8 +179,8 @@ type Logger struct {
skipClientTime bool
linkMonitor *monitor.Mon
buffer Buffer
drainWake chan struct{} // signal to speed up drain
flushDelayFn func() time.Duration // negative or zero return value to upload aggressively, or >0 to batch at this delay
drainWake chan struct{} // signal to speed up drain
flushDelay time.Duration // negative or zero to upload agressively, or >0 to batch at this delay
flushPending atomic.Bool
sentinel chan int32
timeNow func() time.Time
@@ -461,24 +462,12 @@ func (l *Logger) upload(ctx context.Context, body []byte, origlen int) (uploaded
return true, nil
}
// Flush uploads all logs to the server. It blocks until complete or there is an
// unrecoverable error.
//
// TODO(bradfitz): this apparently just returns nil, as of tailscale/corp@9c2ec35.
// Finish cleaning this up.
// Flush uploads all logs to the server.
// It blocks until complete or there is an unrecoverable error.
func (l *Logger) Flush() error {
return nil
}
// StartFlush starts a log upload, if anything is pending.
//
// If l is nil, StartFlush is a no-op.
func (l *Logger) StartFlush() {
if l != nil {
l.tryDrainWake()
}
}
// logtailDisabled is whether logtail uploads to logcatcher are disabled.
var logtailDisabled atomic.Bool
@@ -511,16 +500,12 @@ func (l *Logger) sendLocked(jsonBlob []byte) (int, error) {
n, err := l.buffer.Write(jsonBlob)
flushDelay := defaultFlushDelay
if l.flushDelayFn != nil {
flushDelay = l.flushDelayFn()
}
if flushDelay > 0 {
if l.flushDelay > 0 {
if l.flushPending.CompareAndSwap(false, true) {
if l.flushTimer == nil {
l.flushTimer = time.AfterFunc(flushDelay, l.tryDrainWake)
l.flushTimer = time.AfterFunc(l.flushDelay, l.tryDrainWake)
} else {
l.flushTimer.Reset(flushDelay)
l.flushTimer.Reset(l.flushDelay)
}
}
} else {

View File

@@ -19,21 +19,41 @@ import (
"golang.org/x/exp/slices"
"tailscale.com/health"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/packet"
"tailscale.com/net/tsaddr"
"tailscale.com/net/tsdial"
"tailscale.com/net/tstun"
"tailscale.com/types/dnstype"
"tailscale.com/types/ipproto"
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
"tailscale.com/util/dnsname"
"tailscale.com/wgengine/monitor"
)
var (
magicDNSIP = tsaddr.TailscaleServiceIP()
magicDNSIPv6 = tsaddr.TailscaleServiceIPv6()
)
var (
errFullQueue = errors.New("request queue full")
)
// maxActiveQueries returns the maximal number of DNS requests that can
// be running.
const maxActiveQueries = 256
// maxActiveQueries returns the maximal number of DNS requests that be
// can running.
// If EnqueueRequest is called when this many requests are already pending,
// the request will be dropped to avoid blocking the caller.
func maxActiveQueries() int32 {
if runtime.GOOS == "ios" {
// For memory paranoia reasons on iOS, match the
// historical Tailscale 1.x..1.8 behavior for now
// (just before the 1.10 release).
return 64
}
// But for other platforms, allow more burstiness:
return 256
}
// We use file-ignore below instead of ignore because on some platforms,
// the lint exception is necessary and on others it is not,
@@ -55,6 +75,13 @@ type response struct {
type Manager struct {
logf logger.Logf
// When netstack is not used, Manager implements magic DNS.
// In this case, responses tracks completed DNS requests
// which need a response, and NextPacket() synthesizes a
// fake IP+UDP header to finish assembling the response.
//
// TODO(tom): Rip out once all platforms use netstack.
responses chan response
activeQueriesAtomic int32
ctx context.Context // good until Down
@@ -71,9 +98,10 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, linkMon *monitor.Mon, di
}
logf = logger.WithPrefix(logf, "dns: ")
m := &Manager{
logf: logf,
resolver: resolver.New(logf, linkMon, linkSel, dialer),
os: oscfg,
logf: logf,
resolver: resolver.New(logf, linkMon, linkSel, dialer),
os: oscfg,
responses: make(chan response),
}
m.ctx, m.ctxCancel = context.WithCancel(context.Background())
m.logf("using %T", m.os)
@@ -288,6 +316,89 @@ func toIPsOnly(resolvers []*dnstype.Resolver) (ret []netip.Addr) {
return ret
}
// EnqueuePacket is the legacy path for handling magic DNS traffic, and is
// called with a DNS request payload.
//
// TODO(tom): Rip out once all platforms use netstack.
func (m *Manager) EnqueuePacket(bs []byte, proto ipproto.Proto, from, to netip.AddrPort) error {
if to.Port() != 53 || proto != ipproto.UDP {
return nil
}
if n := atomic.AddInt32(&m.activeQueriesAtomic, 1); n > maxActiveQueries() {
atomic.AddInt32(&m.activeQueriesAtomic, -1)
metricDNSQueryErrorQueue.Add(1)
return errFullQueue
}
go func() {
resp, err := m.resolver.Query(m.ctx, bs, from)
if err != nil {
atomic.AddInt32(&m.activeQueriesAtomic, -1)
m.logf("dns query: %v", err)
return
}
select {
case <-m.ctx.Done():
return
case m.responses <- response{resp, from}:
}
}()
return nil
}
// NextPacket is the legacy path for obtaining DNS results in response to
// magic DNS queries. It blocks until a response is available.
//
// TODO(tom): Rip out once all platforms use netstack.
func (m *Manager) NextPacket() ([]byte, error) {
var resp response
select {
case <-m.ctx.Done():
return nil, net.ErrClosed
case resp = <-m.responses:
// continue
}
// Unused space is needed further down the stack. To avoid extra
// allocations/copying later on, we allocate such space here.
const offset = tstun.PacketStartOffset
var buf []byte
switch {
case resp.to.Addr().Is4():
h := packet.UDP4Header{
IP4Header: packet.IP4Header{
Src: magicDNSIP,
Dst: resp.to.Addr(),
},
SrcPort: 53,
DstPort: resp.to.Port(),
}
hlen := h.Len()
buf = make([]byte, offset+hlen+len(resp.pkt))
copy(buf[offset+hlen:], resp.pkt)
h.Marshal(buf[offset:])
case resp.to.Addr().Is6():
h := packet.UDP6Header{
IP6Header: packet.IP6Header{
Src: magicDNSIPv6,
Dst: resp.to.Addr(),
},
SrcPort: 53,
DstPort: resp.to.Port(),
}
hlen := h.Len()
buf = make([]byte, offset+hlen+len(resp.pkt))
copy(buf[offset+hlen:], resp.pkt)
h.Marshal(buf[offset:])
}
atomic.AddInt32(&m.activeQueriesAtomic, -1)
return buf, nil
}
// Query executes a DNS query received from the given address. The query is
// provided in bs as a wire-encoded DNS query without any transport header.
// This method is called for requests arriving over UDP and TCP.
@@ -299,7 +410,7 @@ func (m *Manager) Query(ctx context.Context, bs []byte, from netip.AddrPort) ([]
// continue
}
if n := atomic.AddInt32(&m.activeQueriesAtomic, 1); n > maxActiveQueries {
if n := atomic.AddInt32(&m.activeQueriesAtomic, 1); n > maxActiveQueries() {
atomic.AddInt32(&m.activeQueriesAtomic, -1)
metricDNSQueryErrorQueue.Add(1)
return nil, errFullQueue

View File

@@ -83,26 +83,17 @@ func Parse(r io.Reader) (*Config, error) {
}
if s, ok := strs.CutPrefix(line, "search"); ok {
domains := strings.TrimSpace(s)
if len(domains) == len(s) {
domain := strings.TrimSpace(s)
if len(domain) == len(s) {
// No leading space?!
return nil, fmt.Errorf("missing space after \"search\" in %q", line)
return nil, fmt.Errorf("missing space after \"domain\" in %q", line)
}
for len(domains) > 0 {
domain := domains
i := strings.IndexAny(domain, " \t")
if i != -1 {
domain = domain[:i]
domains = strings.TrimSpace(domains[i+1:])
} else {
domains = ""
}
fqdn, err := dnsname.ToFQDN(domain)
if err != nil {
return nil, fmt.Errorf("parsing search domain %q in %q: %w", domain, line, err)
}
config.SearchDomains = append(config.SearchDomains, fqdn)
fqdn, err := dnsname.ToFQDN(domain)
if err != nil {
return nil, fmt.Errorf("parsing search domains %q: %w", line, err)
}
config.SearchDomains = append(config.SearchDomains, fqdn)
continue
}
}
return config, nil

View File

@@ -57,31 +57,6 @@ func TestParse(t *testing.T) {
},
{in: `searchtailsacle.com`, wantErr: true},
{in: `search`, wantErr: true},
// Issue 6875: there can be multiple search domains, and even if they're
// over 253 bytes long total.
{
in: "search search-01.example search-02.example search-03.example search-04.example search-05.example search-06.example search-07.example search-08.example search-09.example search-10.example search-11.example search-12.example search-13.example search-14.example search-15.example\n",
want: &Config{
SearchDomains: []dnsname.FQDN{
"search-01.example.",
"search-02.example.",
"search-03.example.",
"search-04.example.",
"search-05.example.",
"search-06.example.",
"search-07.example.",
"search-08.example.",
"search-09.example.",
"search-10.example.",
"search-11.example.",
"search-12.example.",
"search-13.example.",
"search-14.example.",
"search-15.example.",
},
},
},
}
for _, tt := range tests {

View File

@@ -43,7 +43,7 @@ func TestFallbackRootWorks(t *testing.T) {
crtFile := filepath.Join(d, "tlsdial.test.crt")
keyFile := filepath.Join(d, "tlsdial.test.key")
caFile := filepath.Join(d, "rootCA.pem")
cmd := exec.Command("go",
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"),
"run", "filippo.io/mkcert",
"--cert-file="+crtFile,
"--key-file="+keyFile,

View File

@@ -21,6 +21,19 @@ EOF
return 0
fi
done
# For the benefit of generated files, we also allow no year in
# the header. https://github.com/tailscale/tailscale/issues/6865
want=$(cat <<EOF
// Copyright (c) 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.
EOF
)
if [ "$got" = "$want" ]; then
return 0
fi
return 1
}

View File

@@ -8,7 +8,7 @@
// and groups to the specified `--uid`, `--gid` and `--groups`, and
// then launches the requested `--cmd`.
//go:build linux || (darwin && !ios) || freebsd || openbsd
//go:build linux || (darwin && !ios) || freebsd
package tailssh
@@ -24,7 +24,6 @@ import (
"os/user"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"syscall"
@@ -34,7 +33,6 @@ import (
"github.com/u-root/u-root/pkg/termios"
"go4.org/mem"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/exp/slices"
"golang.org/x/sys/unix"
"tailscale.com/cmd/tailscaled/childproc"
"tailscale.com/envknob"
@@ -695,59 +693,3 @@ func acceptEnvPair(kv string) bool {
}
return k == "TERM" || k == "LANG" || strings.HasPrefix(k, "LC_")
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func (ia *incubatorArgs) loginArgs() []string {
switch runtime.GOOS {
case "linux":
if distro.Get() == distro.Arch && !fileExists("/etc/pam.d/remote") {
// See https://github.com/tailscale/tailscale/issues/4924
//
// Arch uses a different login binary that makes the -h flag set the PAM
// service to "remote". So if they don't have that configured, don't
// pass -h.
return []string{ia.loginCmdPath, "-f", ia.localUser, "-p"}
}
return []string{ia.loginCmdPath, "-f", ia.localUser, "-h", ia.remoteIP, "-p"}
case "darwin", "freebsd", "openbsd":
return []string{ia.loginCmdPath, "-fp", "-h", ia.remoteIP, ia.localUser}
}
panic("unimplemented")
}
func setGroups(groupIDs []int) error {
if runtime.GOOS == "darwin" && len(groupIDs) > 16 {
// darwin returns "invalid argument" if more than 16 groups are passed to syscall.Setgroups
// some info can be found here:
// https://opensource.apple.com/source/samba/samba-187.8/patches/support-darwin-initgroups-syscall.auto.html
// this fix isn't great, as anyone reading this has probably just wasted hours figuring out why
// some permissions thing isn't working, due to some arbitrary group ordering, but it at least allows
// this to work for more things than it previously did.
groupIDs = groupIDs[:16]
}
err := syscall.Setgroups(groupIDs)
if err != nil && os.Geteuid() != 0 && groupsMatchCurrent(groupIDs) {
// If we're not root, ignore a Setgroups failure if all groups are the same.
return nil
}
return err
}
func groupsMatchCurrent(groupIDs []int) bool {
existing, err := syscall.Getgroups()
if err != nil {
return false
}
if len(existing) != len(groupIDs) {
return false
}
groupIDs = slices.Clone(groupIDs)
sort.Ints(groupIDs)
sort.Ints(existing)
return slices.Equal(groupIDs, existing)
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2022 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 tailssh
import "syscall"
func (ia *incubatorArgs) loginArgs() []string {
return []string{ia.loginCmdPath, "-fp", "-h", ia.remoteIP, ia.localUser}
}
func setGroups(groupIDs []int) error {
// darwin returns "invalid argument" if more than 16 groups are passed to syscall.Setgroups
// some info can be found here:
// https://opensource.apple.com/source/samba/samba-187.8/patches/support-darwin-initgroups-syscall.auto.html
// this fix isn't great, as anyone reading this has probably just wasted hours figuring out why
// some permissions thing isn't working, due to some arbitrary group ordering, but it at least allows
// this to work for more things than it previously did.
return syscall.Setgroups(groupIDs[:16])
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2022 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 tailssh
import "syscall"
func (ia *incubatorArgs) loginArgs() []string {
return []string{ia.loginCmdPath, "-fp", "-h", ia.remoteIP, ia.localUser}
}
func setGroups(groupIDs []int) error {
return syscall.Setgroups(groupIDs)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/godbus/dbus/v5"
"tailscale.com/types/logger"
"tailscale.com/version/distro"
)
func init() {
@@ -172,3 +173,24 @@ func maybeStartLoginSessionLinux(logf logger.Logf, ia incubatorArgs) (func() err
}
return nil, nil
}
func fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func (ia *incubatorArgs) loginArgs() []string {
if distro.Get() == distro.Arch && !fileExists("/etc/pam.d/remote") {
// See https://github.com/tailscale/tailscale/issues/4924
//
// Arch uses a different login binary that makes the -h flag set the PAM
// service to "remote". So if they don't have that configured, don't
// pass -h.
return []string{ia.loginCmdPath, "-f", ia.localUser, "-p"}
}
return []string{ia.loginCmdPath, "-f", ia.localUser, "-h", ia.remoteIP, "-p"}
}
func setGroups(groupIDs []int) error {
return syscall.Setgroups(groupIDs)
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || (darwin && !ios) || freebsd || openbsd
//go:build linux || (darwin && !ios) || freebsd
// Package tailssh is an SSH server integrated into Tailscale.
package tailssh

View File

@@ -88,8 +88,7 @@ type CapabilityVersion int
// - 49: 2022-11-03: Client understands EarlyNoise
// - 50: 2022-11-14: Client understands CapabilityIngress
// - 51: 2022-11-30: Client understands CapabilityTailnetLockAlpha
// - 52: 2023-01-05: client can handle c2n POST /logtail/flush
const CurrentCapabilityVersion CapabilityVersion = 52
const CurrentCapabilityVersion CapabilityVersion = 51
type StableID string

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -150,7 +150,7 @@ func (a *AUM) StaticValidate() error {
return errors.New("absent parent must be represented by a nil slice")
}
for i, sig := range a.Signatures {
if len(sig.KeyID) != 32 || len(sig.Signature) != ed25519.SignatureSize {
if len(sig.KeyID) == 0 || len(sig.Signature) != ed25519.SignatureSize {
return fmt.Errorf("signature %d has missing keyID or malformed signature", i)
}
}
@@ -196,13 +196,8 @@ func (a *AUM) StaticValidate() error {
case AUMNoOp:
default:
// An AUM with an unknown message kind was received! That means
// that a future version of tailscaled added some feature we don't
// understand.
//
// The future-compatibility contract for AUM message types is that
// they must only add new features, not change the semantics of existing
// mechanisms or features. As such, old clients can safely ignore them.
// TODO(tom): Ignore unknown AUMs for GA.
return fmt.Errorf("unknown AUM kind: %v", a.MessageKind)
}
return nil
@@ -281,20 +276,14 @@ func (a *AUM) Parent() (h AUMHash, ok bool) {
return h, false
}
func (a *AUM) sign25519(priv ed25519.PrivateKey) error {
func (a *AUM) sign25519(priv ed25519.PrivateKey) {
key := Key{Kind: Key25519, Public: priv.Public().(ed25519.PublicKey)}
sigHash := a.SigHash()
keyID, err := key.ID()
if err != nil {
return err
}
a.Signatures = append(a.Signatures, tkatype.Signature{
KeyID: keyID,
KeyID: key.ID(),
Signature: ed25519.Sign(priv, sigHash[:]),
})
return nil
}
// Weight computes the 'signature weight' of the AUM

View File

@@ -189,7 +189,7 @@ func TestAUMWeight(t *testing.T) {
{
"Unary key",
AUM{
Signatures: []tkatype.Signature{{KeyID: key.MustID()}},
Signatures: []tkatype.Signature{{KeyID: key.ID()}},
},
State{
Keys: []Key{key},
@@ -199,7 +199,7 @@ func TestAUMWeight(t *testing.T) {
{
"Multiple keys",
AUM{
Signatures: []tkatype.Signature{{KeyID: key.MustID()}, {KeyID: key2.MustID()}},
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key2.ID()}},
},
State{
Keys: []Key{key, key2},
@@ -209,7 +209,7 @@ func TestAUMWeight(t *testing.T) {
{
"Double use",
AUM{
Signatures: []tkatype.Signature{{KeyID: key.MustID()}, {KeyID: key.MustID()}},
Signatures: []tkatype.Signature{{KeyID: key.ID()}, {KeyID: key.ID()}},
},
State{
Keys: []Key{key},

View File

@@ -60,12 +60,7 @@ func (b *UpdateBuilder) mkUpdate(update AUM) error {
// AddKey adds a new key to the authority.
func (b *UpdateBuilder) AddKey(key Key) error {
keyID, err := key.ID()
if err != nil {
return err
}
if _, err := b.state.GetKey(keyID); err == nil {
if _, err := b.state.GetKey(key.ID()); err == nil {
return fmt.Errorf("cannot add key %v: already exists", key)
}
return b.mkUpdate(AUM{MessageKind: AUMAddKey, Key: &key})

View File

@@ -19,7 +19,7 @@ func (s signer25519) SignAUM(sigHash tkatype.AUMSigHash) ([]tkatype.Signature, e
key := Key{Kind: Key25519, Public: priv.Public().(ed25519.PublicKey)}
return []tkatype.Signature{{
KeyID: key.MustID(),
KeyID: key.ID(),
Signature: ed25519.Sign(priv, sigHash[:]),
}}, nil
}
@@ -54,7 +54,7 @@ func TestAuthorityBuilderAddKey(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
if _, err := a.state.GetKey(key2.MustID()); err != nil {
if _, err := a.state.GetKey(key2.ID()); err != nil {
t.Errorf("could not read new key: %v", err)
}
}
@@ -75,7 +75,7 @@ func TestAuthorityBuilderRemoveKey(t *testing.T) {
}
b := a.NewUpdater(signer25519(priv))
if err := b.RemoveKey(key2.MustID()); err != nil {
if err := b.RemoveKey(key2.ID()); err != nil {
t.Fatalf("RemoveKey(%v) failed: %v", key2, err)
}
updates, err := b.Finalize(storage)
@@ -88,7 +88,7 @@ func TestAuthorityBuilderRemoveKey(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
if _, err := a.state.GetKey(key2.MustID()); err != ErrNoSuchKey {
if _, err := a.state.GetKey(key2.ID()); err != ErrNoSuchKey {
t.Errorf("GetKey(key2).err = %v, want %v", err, ErrNoSuchKey)
}
}
@@ -107,8 +107,8 @@ func TestAuthorityBuilderSetKeyVote(t *testing.T) {
}
b := a.NewUpdater(signer25519(priv))
if err := b.SetKeyVote(key.MustID(), 5); err != nil {
t.Fatalf("SetKeyVote(%v) failed: %v", key.MustID(), err)
if err := b.SetKeyVote(key.ID(), 5); err != nil {
t.Fatalf("SetKeyVote(%v) failed: %v", key.ID(), err)
}
updates, err := b.Finalize(storage)
if err != nil {
@@ -120,7 +120,7 @@ func TestAuthorityBuilderSetKeyVote(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
k, err := a.state.GetKey(key.MustID())
k, err := a.state.GetKey(key.ID())
if err != nil {
t.Fatal(err)
}
@@ -143,7 +143,7 @@ func TestAuthorityBuilderSetKeyMeta(t *testing.T) {
}
b := a.NewUpdater(signer25519(priv))
if err := b.SetKeyMeta(key.MustID(), map[string]string{"b": "c"}); err != nil {
if err := b.SetKeyMeta(key.ID(), map[string]string{"b": "c"}); err != nil {
t.Fatalf("SetKeyMeta(%v) failed: %v", key, err)
}
updates, err := b.Finalize(storage)
@@ -156,7 +156,7 @@ func TestAuthorityBuilderSetKeyMeta(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
k, err := a.state.GetKey(key.MustID())
k, err := a.state.GetKey(key.ID())
if err != nil {
t.Fatal(err)
}
@@ -185,10 +185,10 @@ func TestAuthorityBuilderMultiple(t *testing.T) {
if err := b.AddKey(key2); err != nil {
t.Fatalf("AddKey(%v) failed: %v", key2, err)
}
if err := b.SetKeyVote(key2.MustID(), 42); err != nil {
if err := b.SetKeyVote(key2.ID(), 42); err != nil {
t.Fatalf("SetKeyVote(%v) failed: %v", key2, err)
}
if err := b.RemoveKey(key.MustID()); err != nil {
if err := b.RemoveKey(key.ID()); err != nil {
t.Fatalf("RemoveKey(%v) failed: %v", key, err)
}
updates, err := b.Finalize(storage)
@@ -201,14 +201,14 @@ func TestAuthorityBuilderMultiple(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
k, err := a.state.GetKey(key2.MustID())
k, err := a.state.GetKey(key2.ID())
if err != nil {
t.Fatal(err)
}
if got, want := k.Votes, uint(42); got != want {
t.Errorf("key.Votes = %d, want %d", got, want)
}
if _, err := a.state.GetKey(key.MustID()); err != ErrNoSuchKey {
if _, err := a.state.GetKey(key.ID()); err != ErrNoSuchKey {
t.Errorf("GetKey(key).err = %v, want %v", err, ErrNoSuchKey)
}
}
@@ -243,7 +243,7 @@ func TestAuthorityBuilderCheckpointsAfterXUpdates(t *testing.T) {
if err := a.Inform(storage, updates); err != nil {
t.Fatalf("could not apply generated updates: %v", err)
}
if _, err := a.state.GetKey(key2.MustID()); err != nil {
if _, err := a.state.GetKey(key2.ID()); err != nil {
t.Fatal(err)
}

View File

@@ -267,7 +267,7 @@ func (c *testChain) makeAUM(v *testchainNode) AUM {
sigHash := aum.SigHash()
for _, key := range c.SignAllKeys {
aum.Signatures = append(aum.Signatures, tkatype.Signature{
KeyID: c.Key[key].MustID(),
KeyID: c.Key[key].ID(),
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
})
}
@@ -276,7 +276,7 @@ func (c *testChain) makeAUM(v *testchainNode) AUM {
// sign it using that key.
if key := v.SignedWith; key != "" {
aum.Signatures = append(aum.Signatures, tkatype.Signature{
KeyID: c.Key[key].MustID(),
KeyID: c.Key[key].ID(),
Signature: ed25519.Sign(c.KeyPrivs[key], sigHash[:]),
})
}

View File

@@ -74,25 +74,14 @@ func (k Key) Clone() Key {
return out
}
// MustID returns the KeyID of the key, panicking if an error is
// encountered. This must only be used for tests.
func (k Key) MustID() tkatype.KeyID {
id, err := k.ID()
if err != nil {
panic(err)
}
return id
}
// ID returns the KeyID of the key.
func (k Key) ID() (tkatype.KeyID, error) {
func (k Key) ID() tkatype.KeyID {
switch k.Kind {
// Because 25519 public keys are so short, we just use the 32-byte
// public as their 'key ID'.
case Key25519:
return tkatype.KeyID(k.Public), nil
return tkatype.KeyID(k.Public)
default:
return nil, fmt.Errorf("unknown key kind: %v", k.Kind)
panic("unsupported key kind")
}
}

View File

@@ -49,7 +49,7 @@ func TestVerify25519(t *testing.T) {
sigHash := aum.SigHash()
aum.Signatures = []tkatype.Signature{
{
KeyID: key.MustID(),
KeyID: key.ID(),
Signature: ed25519.Sign(priv, sigHash[:]),
},
}
@@ -92,7 +92,7 @@ func TestNLPrivate(t *testing.T) {
// We manually compute the keyID, so make sure its consistent with
// tka.Key.ID().
if !bytes.Equal(k.MustID(), p.KeyID()) {
t.Errorf("private.KeyID() & tka KeyID differ: %x != %x", k.MustID(), p.KeyID())
if !bytes.Equal(k.ID(), p.KeyID()) {
t.Errorf("private.KeyID() & tka KeyID differ: %x != %x", k.ID(), p.KeyID())
}
}

View File

@@ -273,7 +273,7 @@ func TestForkingPropagation(t *testing.T) {
F1.template = removeKey1`,
optSignAllUsing("key2"),
optKey("key2", key, priv),
optTemplate("removeKey1", AUM{MessageKind: AUMRemoveKey, KeyID: s.defaultKey.MustID()})),
optTemplate("removeKey1", AUM{MessageKind: AUMRemoveKey, KeyID: s.defaultKey.ID()})),
})
s.testSyncsBetween(control, n2)
s.checkHaveConsensus(control, n2)
@@ -282,10 +282,10 @@ func TestForkingPropagation(t *testing.T) {
s.testSyncsBetween(control, n1)
s.checkHaveConsensus(n1, n2)
if _, err := n1.A.state.GetKey(s.defaultKey.MustID()); err != ErrNoSuchKey {
if _, err := n1.A.state.GetKey(s.defaultKey.ID()); err != ErrNoSuchKey {
t.Error("default key was still present")
}
if _, err := n1.A.state.GetKey(key.MustID()); err != nil {
if _, err := n1.A.state.GetKey(key.ID()); err != nil {
t.Errorf("key2 was not trusted: %v", err)
}
}
@@ -305,9 +305,7 @@ func TestInvalidAUMPropagationRejected(t *testing.T) {
l3 := n1.AUMs["L3"]
l3H := l3.Hash()
l4 := AUM{MessageKind: AUMAddKey, PrevAUMHash: l3H[:]}
if err := l4.sign25519(s.defaultPriv); err != nil {
t.Fatal(err)
}
l4.sign25519(s.defaultPriv)
l4H := l4.Hash()
n1.storage.CommitVerifiedAUMs([]AUM{l4})
n1.A.state.LastAUMHash = &l4H
@@ -373,9 +371,7 @@ func TestBadSigAUMPropagationRejected(t *testing.T) {
l3 := n1.AUMs["L3"]
l3H := l3.Hash()
l4 := AUM{MessageKind: AUMNoOp, PrevAUMHash: l3H[:]}
if err := l4.sign25519(s.defaultPriv); err != nil {
t.Fatal(err)
}
l4.sign25519(s.defaultPriv)
l4.Signatures[0].Signature[3] = 42
l4H := l4.Hash()
n1.storage.CommitVerifiedAUMs([]AUM{l4})

View File

@@ -22,7 +22,7 @@ func TestSigDirect(t *testing.T) {
sig := NodeKeySignature{
SigKind: SigDirect,
KeyID: k.MustID(),
KeyID: k.ID(),
Pubkey: nodeKeyPub,
}
sigHash := sig.SigHash()
@@ -65,7 +65,7 @@ func TestSigNested(t *testing.T) {
// the network-lock key.
nestedSig := NodeKeySignature{
SigKind: SigDirect,
KeyID: k.MustID(),
KeyID: k.ID(),
Pubkey: oldPub,
WrappingPubkey: rPub,
}
@@ -132,7 +132,7 @@ func TestSigNested_DeepNesting(t *testing.T) {
// the network-lock key.
nestedSig := NodeKeySignature{
SigKind: SigDirect,
KeyID: k.MustID(),
KeyID: k.ID(),
Pubkey: oldPub,
WrappingPubkey: rPub,
}
@@ -204,7 +204,7 @@ func TestSigCredential(t *testing.T) {
// public key.
nestedSig := NodeKeySignature{
SigKind: SigCredential,
KeyID: k.MustID(),
KeyID: k.ID(),
WrappingPubkey: cPub,
}
sigHash := nestedSig.SigHash()
@@ -280,11 +280,11 @@ func TestSigSerializeUnserialize(t *testing.T) {
key := Key{Kind: Key25519, Public: pub, Votes: 2}
sig := NodeKeySignature{
SigKind: SigDirect,
KeyID: key.MustID(),
KeyID: key.ID(),
Pubkey: nodeKeyPub,
Nested: &NodeKeySignature{
SigKind: SigDirect,
KeyID: key.MustID(),
KeyID: key.ID(),
Pubkey: nodeKeyPub,
},
}

View File

@@ -29,6 +29,9 @@ type State struct {
// DisablementSecrets are KDF-derived values which can be used
// to turn off the TKA in the event of a consensus-breaking bug.
//
// TODO(tom): This is an alpha feature, remove this mechanism once
// we have confidence in our implementation.
DisablementSecrets [][]byte `cbor:"2,keyasint"`
// Keys are the public keys currently trusted by the TKA.
@@ -45,12 +48,7 @@ type State struct {
// GetKey returns the trusted key with the specified KeyID.
func (s State) GetKey(key tkatype.KeyID) (Key, error) {
for _, k := range s.Keys {
keyID, err := k.ID()
if err != nil {
return Key{}, err
}
if bytes.Equal(keyID, key) {
if bytes.Equal(k.ID(), key) {
return k, nil
}
}
@@ -174,11 +172,7 @@ func (s State) applyVerifiedAUM(update AUM) (State, error) {
if update.Key == nil {
return State{}, errors.New("no key to add provided")
}
keyID, err := update.Key.ID()
if err != nil {
return State{}, err
}
if _, err := s.GetKey(keyID); err == nil {
if _, err := s.GetKey(update.Key.ID()); err == nil {
return State{}, errors.New("key already exists")
}
out := s.cloneForUpdate(&update)
@@ -201,11 +195,7 @@ func (s State) applyVerifiedAUM(update AUM) (State, error) {
}
out := s.cloneForUpdate(&update)
for i := range out.Keys {
keyID, err := out.Keys[i].ID()
if err != nil {
return State{}, err
}
if bytes.Equal(keyID, update.KeyID) {
if bytes.Equal(out.Keys[i].ID(), update.KeyID) {
out.Keys[i] = k
}
}
@@ -214,11 +204,7 @@ func (s State) applyVerifiedAUM(update AUM) (State, error) {
case AUMRemoveKey:
idx := -1
for i := range s.Keys {
keyID, err := s.Keys[i].ID()
if err != nil {
return State{}, err
}
if bytes.Equal(update.KeyID, keyID) {
if bytes.Equal(update.KeyID, s.Keys[i].ID()) {
idx = i
break
}
@@ -231,15 +217,9 @@ func (s State) applyVerifiedAUM(update AUM) (State, error) {
return out, nil
default:
// An AUM with an unknown message kind was received! That means
// that a future version of tailscaled added some feature we don't
// understand.
//
// The future-compatibility contract for AUM message types is that
// they must only add new features, not change the semantics of existing
// mechanisms or features. As such, old clients can safely ignore them.
out := s.cloneForUpdate(&update)
return out, nil
// TODO(tom): Instead of erroring, update lastHash and
// continue (to preserve future compatibility).
return State{}, fmt.Errorf("unhandled message: %v", update.MessageKind)
}
}
@@ -294,17 +274,7 @@ func (s *State) staticValidateCheckpoint() error {
if i == j {
continue
}
id1, err := k.ID()
if err != nil {
return fmt.Errorf("key[%d]: %w", i, err)
}
id2, err := k2.ID()
if err != nil {
return fmt.Errorf("key[%d]: %w", j, err)
}
if bytes.Equal(id1, id2) {
if bytes.Equal(k.ID(), k2.ID()) {
return fmt.Errorf("key[%d]: duplicates key[%d]", i, j)
}
}

View File

@@ -124,7 +124,7 @@ func TestForkResolutionMessageType(t *testing.T) {
L3.hashSeed = 18
`,
optTemplate("addKey", AUM{MessageKind: AUMAddKey, Key: &key}),
optTemplate("removeKey", AUM{MessageKind: AUMRemoveKey, KeyID: key.MustID()}))
optTemplate("removeKey", AUM{MessageKind: AUMRemoveKey, KeyID: key.ID()}))
l1H := c.AUMHashes["L1"]
l2H := c.AUMHashes["L2"]
@@ -165,7 +165,7 @@ func TestComputeStateAt(t *testing.T) {
if err != nil {
t.Fatalf("computeStateAt(G1) failed: %v", err)
}
if _, err := state.GetKey(key.MustID()); err != ErrNoSuchKey {
if _, err := state.GetKey(key.ID()); err != ErrNoSuchKey {
t.Errorf("expected key to be missing: err = %v", err)
}
if *state.LastAUMHash != c.AUMHashes["G1"] {
@@ -182,7 +182,7 @@ func TestComputeStateAt(t *testing.T) {
if *state.LastAUMHash != wantHash {
t.Errorf("LastAUMHash = %x, want %x", *state.LastAUMHash, wantHash)
}
if _, err := state.GetKey(key.MustID()); err != nil {
if _, err := state.GetKey(key.ID()); err != nil {
t.Errorf("expected key to be present at state: err = %v", err)
}
}
@@ -234,7 +234,7 @@ func TestOpenAuthority(t *testing.T) {
i2, i2H := fakeAUM(t, 2, &i1H)
i3, i3H := fakeAUM(t, 5, &i2H)
l2, l2H := fakeAUM(t, AUM{MessageKind: AUMNoOp, KeyID: []byte{7}, Signatures: []tkatype.Signature{{KeyID: key.MustID()}}}, &i3H)
l2, l2H := fakeAUM(t, AUM{MessageKind: AUMNoOp, KeyID: []byte{7}, Signatures: []tkatype.Signature{{KeyID: key.ID()}}}, &i3H)
l3, l3H := fakeAUM(t, 4, &i3H)
g2, g2H := fakeAUM(t, 8, nil)
@@ -266,7 +266,7 @@ func TestOpenAuthority(t *testing.T) {
t.Fatalf("New() failed: %v", err)
}
// Should include the key added in G1
if _, err := a.state.GetKey(key.MustID()); err != nil {
if _, err := a.state.GetKey(key.ID()); err != nil {
t.Errorf("missing G1 key: %v", err)
}
// The head of the chain should be L2.
@@ -338,10 +338,10 @@ func TestCreateBootstrapAuthority(t *testing.T) {
}
// Both authorities should trust the key laid down in the genesis state.
if !a1.KeyTrusted(key.MustID()) {
if !a1.KeyTrusted(key.ID()) {
t.Error("a1 did not trust genesis key")
}
if !a2.KeyTrusted(key.MustID()) {
if !a2.KeyTrusted(key.ID()) {
t.Error("a2 did not trust genesis key")
}
}

View File

@@ -11,6 +11,8 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
@@ -51,7 +53,7 @@ func TestInQemu(t *testing.T) {
}
t.Logf("using %v", look)
}
cmd := exec.Command("go",
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"),
"test",
"--exec="+execVia,
"-v",

View File

@@ -35,7 +35,6 @@ import (
_ "tailscale.com/paths"
_ "tailscale.com/safesocket"
_ "tailscale.com/smallzstd"
_ "tailscale.com/ssh/tailssh"
_ "tailscale.com/syncs"
_ "tailscale.com/tailcfg"
_ "tailscale.com/tsweb"

View File

@@ -13,11 +13,13 @@ import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)
func TestDeps(t *testing.T) {
cmd := exec.Command("go", "list", "-json", ".")
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "list", "-json", ".")
cmd.Env = append(os.Environ(), "GOOS=ios", "GOARCH=arm64")
out, err := cmd.Output()
if err != nil {

View File

@@ -13,11 +13,13 @@ import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
)
func TestDeps(t *testing.T) {
cmd := exec.Command("go", "list", "-json", ".")
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "list", "-json", ".")
cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm")
out, err := cmd.Output()
if err != nil {

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -16,7 +16,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/tka"
"tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/wgengine/filter"
)
@@ -39,10 +38,9 @@ type NetworkMap struct {
Peers []*tailcfg.Node // sorted by Node.ID
DNS tailcfg.DNSConfig
// TODO(maisem) : replace with View.
Hostinfo tailcfg.Hostinfo
PacketFilter []filter.Match
PacketFilterRules views.Slice[tailcfg.FilterRule]
SSHPolicy *tailcfg.SSHPolicy // or nil, if not enabled/allowed
Hostinfo tailcfg.Hostinfo
PacketFilter []filter.Match
SSHPolicy *tailcfg.SSHPolicy // or nil, if not enabled/allowed
// CollectServices reports whether this node's Tailnet has
// requested that info about services be included in HostInfo.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -6,7 +6,6 @@
package codegen
import (
"bufio"
"bytes"
"fmt"
"go/ast"
@@ -14,16 +13,12 @@ import (
"go/types"
"io"
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/imports"
"tailscale.com/util/mak"
"tailscale.com/util/must"
)
// LoadTypes returns all named types in pkgName, keyed by their type name.
@@ -58,13 +53,11 @@ func HasNoClone(structTag string) bool {
return false
}
const copyrightHeader = `// Copyright (c) %d Tailscale Inc & AUTHORS All rights reserved.
const header = `// Copyright (c) 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.
`
const genAndPackageHeader = `// Code generated by %v; DO NOT EDIT.
// Code generated by %v; DO NOT EDIT.
package %s
`
@@ -110,18 +103,15 @@ func (it *ImportTracker) Write(w io.Writer) {
fmt.Fprintf(w, ")\n\n")
}
func writeHeader(w io.Writer, tool, pkg string, copyrightYear int) {
if copyrightYear != 0 {
fmt.Fprintf(w, copyrightHeader, copyrightYear)
}
fmt.Fprintf(w, genAndPackageHeader, tool, pkg)
func writeHeader(w io.Writer, tool, pkg string) {
fmt.Fprintf(w, header, tool, pkg)
}
// WritePackageFile adds a file with the provided imports and contents to package.
// The tool param is used to identify the tool that generated package file.
func WritePackageFile(tool string, pkg *packages.Package, path string, copyrightYear int, it *ImportTracker, contents *bytes.Buffer) error {
func WritePackageFile(tool string, pkg *packages.Package, path string, it *ImportTracker, contents *bytes.Buffer) error {
buf := new(bytes.Buffer)
writeHeader(buf, tool, pkg.Name, copyrightYear)
writeHeader(buf, tool, pkg.Name)
it.Write(buf)
if _, err := buf.Write(contents.Bytes()); err != nil {
return err
@@ -272,51 +262,3 @@ func IsViewType(typ types.Type) bool {
}
return t.Field(0).Name() == "ж"
}
// CopyrightYear reports the greatest copyright year in non-generated *.go files
// in the current directory, for use in the copyright line of generated code.
//
// It panics on I/O error, as it's assumed this is only being used by "go
// generate" or GitHub actions.
//
// TODO(bradfitz,dgentry): determine what heuristic to use for all this: latest
// year, earliest, none? don't list years at all? IANAL. Get advice of others.
// For now we just want to unbreak the tree. See Issue 6865.
func CopyrightYear(dir string) (year int) {
files, err := os.ReadDir(dir)
if err != nil {
panic(err)
}
rxYear := regexp.MustCompile(`^// Copyright \(c\) (20\d{2}) `)
rxGenerated := regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
Files:
for _, f := range files {
name := f.Name()
if !f.Type().IsRegular() ||
strings.HasPrefix(name, ".") || // includes emacs noise
!strings.HasSuffix(name, ".go") ||
strings.HasSuffix(name, "_clone.go") ||
strings.HasSuffix(name, "_view.go") ||
strings.HasSuffix(name, "_test.go") {
continue
}
src, err := os.ReadFile(filepath.Join(dir, name))
if err != nil {
panic(err)
}
bs := bufio.NewScanner(bytes.NewReader(src))
for bs.Scan() {
line := bs.Bytes()
if m := rxYear.FindSubmatch(line); m != nil {
if y := must.Get(strconv.Atoi(string(m[1]))); y > year {
year = y
}
continue
}
if rxGenerated.Match(line) {
continue Files
}
}
}
return year
}

View File

@@ -23,7 +23,8 @@ var (
func TestFindModuleInfo(t *testing.T) {
dir := t.TempDir()
name := filepath.Join(dir, "tailscaled-version-test")
out, err := exec.Command("go", "build", "-o", name, "tailscale.com/cmd/tailscaled").CombinedOutput()
goTool := filepath.Join(runtime.GOROOT(), "bin", "go"+exe())
out, err := exec.Command(goTool, "build", "-o", name, "tailscale.com/cmd/tailscaled").CombinedOutput()
if err != nil {
t.Fatalf("failed to build tailscaled: %v\n%s", err, out)
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.

View File

@@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io"
"net"
"net/netip"
"reflect"
"runtime"
@@ -55,6 +56,13 @@ import (
"tailscale.com/wgengine/wglog"
)
const magicDNSPort = 53
var (
magicDNSIP = tsaddr.TailscaleServiceIP()
magicDNSIPv6 = tsaddr.TailscaleServiceIPv6()
)
// Lazy wireguard-go configuration parameters.
const (
// lazyPeerIdleThreshold is the idle duration after
@@ -454,6 +462,8 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
e.logf("Starting link monitor...")
e.linkMon.Start()
go e.pollResolver()
e.logf("Engine created.")
return e, nil
}
@@ -481,6 +491,19 @@ func echoRespondToAll(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
// tailscaled directly. Other packets are allowed to proceed into the
// main ACL filter.
func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
// Handle traffic to the service IP.
// TODO(tom): Netstack handles this when it is installed. Rip all
// this out once netstack is used on all platforms.
switch p.Dst.Addr() {
case magicDNSIP, magicDNSIPv6:
err := e.dns.EnqueuePacket(append([]byte(nil), p.Payload()...), p.IPProto, p.Src, p.Dst)
if err != nil {
e.logf("dns: enqueue: %v", err)
}
metricMagicDNSPacketIn.Add(1)
return filter.Drop
}
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
isLocalAddr, ok := e.isLocalAddr.LoadOk()
if !ok {
@@ -500,6 +523,27 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
return filter.Accept
}
// pollResolver reads packets from the DNS resolver and injects them inbound.
//
// TODO(tom): Remove this fallback path (via NextPacket()) once all
// platforms use netstack.
func (e *userspaceEngine) pollResolver() {
for {
bs, err := e.dns.NextPacket()
if errors.Is(err, net.ErrClosed) {
return
}
if err != nil {
e.logf("dns: error: %v", err)
continue
}
// The leading empty space required by the semantics of
// InjectInboundDirect is allocated in NextPacket().
e.tundev.InjectInboundDirect(bs, tstun.PacketStartOffset)
}
}
var debugTrimWireguard = envknob.RegisterOptBool("TS_DEBUG_TRIM_WIREGUARD")
// forceFullWireguardConfig reports whether we should give wireguard our full

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Copyright (c) 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.