Compare commits

..

330 Commits

Author SHA1 Message Date
Denton Gentry
1691898a17 CI: try GitHub's Large runners
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-09-24 14:34:55 -07:00
Denton Gentry
3d24611e32 CI: use BuildJet & self-hosted Windows runners
1. Use buildjet for the longer Linux CI workflows.
2. Use a self-hosted Windows runner.
3. Make CIFuzz run on merge to main or release branch.

Two runs each of the original workflow files and the updated
workflows in this PR:

                                GitHub  GitHub   BuildJet BuildJet
codeql-analysis.yml             4m 30s  cached    2m 56s  2m 59s
cross-darwin.yml                3m 10s  3m 19s    1m 33s  1m 30s
cross-freebsd.yml               3m 33s  3m 10s    1m 28s  1m 22s
cross-openbsd.yml               3m 4s   2m 36s    1m 29s  1m 22s
cross-wasm.yml                  1m 59s  2m  2s    1m 12s  1m 16s
cross-windows.yml               2m 45s  3m  0s    1m 44s  1m 25s
linux32.yml                     4m 27s  4m  0s    1m 55s  2m  8s
linux-race.yml                  3m 54s  4m  7s    2m 22s  2m 12s
linux.yml                       4m 23s  4m 39s    2m 37s  2m 15s
static-analysis.yml
 /vet                           1m 41s  2m 22s       52s     56s
 /staticcheck(linux, amd64)     2m 47s  2m 38s    1m  7s  1m 10s
 /staticcheck(windows, amd64)   2m 5s   2m  4s    1m  6s  1m  8s
 /staticcheck(darwin, amd64)    2m 14s  2m 20s    1m 10s  1m 10s
 /staticcheck(windows, 386)     2m 36s  1m 58s    1m 23s  1m  8s
vm.yml                          1m 30s  1m 32s    2m 31s  2m 23s
windows.yml                     6m 23s  6m 19s    3m 40s  3m 53s

A few very short workflows are being left on GitHub-hosted runners, like
licenses and gofmt. These benefit from the quicker dispatch to GitHub
hosted runners.

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-09-24 13:54:24 -07:00
Brad Fitzpatrick
fb4e23506f control/controlclient: stop restarting map polls on health change
At some point we started restarting map polls on health change, but we
don't remember why. Maybe it was a desperate workaround for something.
I'm not sure it ever worked.

Rather than have a haunted graveyard, remove it.

In its place, though, and somewhat as a safety backup, send those
updates over the HTTP/2 noise channel if we have one open. Then if
there was a reason that a map poll restart would help we could do it
server-side. But mostly we can gather error stats and show
machine-level health info for debugging.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-24 08:51:34 -07:00
Brad Fitzpatrick
6d04184325 control/controlclient: add a noiseClient.post helper method
In prep for a future change that would've been very copy/paste-y.

And because the set-dns call doesn't currently use a context,
so timeouts/cancelations are plumbed.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-23 13:18:22 -07:00
Will Norris
8c72aabbdf licenses: remove win.md file
This was renamed to windows.md
2022-09-23 11:21:25 -07:00
James Tucker
f7cb535693 net/speedtest: retune to meet iperf on localhost in a VM
- removed some in-flow time calls
- increase buffer size to 2MB to overcome syscall cost
- move relative time computation from record to report time

Signed-off-by: James Tucker <james@tailscale.com>
2022-09-23 10:46:04 -07:00
James Tucker
146f51ce76 net/packet: fix filtering of short IPv4 fragments
The fragment offset is an 8 byte offset rather than a byte offset, so
the short packet limit is now in fragment block size in order to compare
with the offset value.

The packet flags are in the first 3 bits of the flags/frags byte, and
so after conversion to a uint16 little endian value they are at the
start, not the end of the value - the mask for extracting "more
fragments" is adjusted to match this byte.

Extremely short fragments less than 80 bytes are dropped, but fragments
over 80 bytes are now accepted.

Fixes #5727

Signed-off-by: James Tucker <james@tailscale.com>
2022-09-23 10:43:28 -07:00
Mihai Parparita
c66e15772f tsweb: consider 304s as successful for quiet logging
Static resource handlers will generate lots of 304s, which are
effectively successful responses.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-23 10:40:32 -07:00
Andrew Dunham
e1bdbfe710 tailcfg, control/controlhttp, control/controlclient: add ControlDialPlan field (#5648)
* tailcfg, control/controlhttp, control/controlclient: add ControlDialPlan field

This field allows the control server to provide explicit information
about how to connect to it; useful if the client's link status can
change after the initial connection, or if the DNS settings pushed by
the control server break future connections.

Change-Id: I720afe6289ec27d40a41b3dcb310ec45bd7e5f3e
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-23 13:06:55 -04:00
Aaron Klotz
acc7baac6d tailcfg, util/deephash: add DataPlaneAuditLogID to Node and DomainDataPlaneAuditLogID to MapResponse
We're adding two log IDs to facilitate data-plane audit logging: a node-specific
log ID, and a domain-specific log ID.

Updated util/deephash/deephash_test.go with revised expectations for tailcfg.Node.

Updates https://github.com/tailscale/corp/issues/6991

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-09-22 17:18:28 -06:00
Kyle Carberry
91794f6498 wgengine/magicsock: move firstDerp check after nil derpMap check
This fixes a race condition which caused `c.muCond.Broadcast()` to
never fire in the `firstDerp` if block. It resulted in `Close()`
hanging forever.

Signed-off-by: Kyle Carberry <kyle@carberry.com>
2022-09-22 11:54:56 -07:00
Brad Fitzpatrick
2c447de6cc cmd/tailscaled: use explicit equal sign in --port=$PORT in tailscaled.service
Personal preference (so it's obvious it's not a bool flag), but it
also matches the --state= before it.

Bonus: stop allowing PORT to sneak in extra flags to be passed as
their own arguments, as $FOO and ${FOO} expand differently. (${FOO} is
required to concat to strings)

Change-Id: I994626a5663fe0948116b46a971e5eb2c4023216
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-22 11:54:22 -07:00
Anton Schubert
021bedfb89 docker: add ability to use a custom control socket
Signed-off-by: Anton Schubert <anton.schubert@riedel.net>
2022-09-22 08:48:26 -07:00
hlts2
d988c9f098 fix auth key name
Signed-off-by: hlts2 <hiroto.funakoshi.hiroto@gmail.com>
2022-09-22 03:55:05 -07:00
Andrew Dunham
0607832397 wgengine/netstack: always respond to 4via6 echo requests (#5712)
As the comment in the code says, netstack should always respond to ICMP
echo requests to a 4via6 address, even if the netstack instance isn't
normally processing subnet traffic.

Follow-up to #5709

Change-Id: I504d0776c5824071b2a2e0e687bc33e24f6c4746
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-21 18:07:57 -04:00
Will Norris
565dbc599a Revert "licenses: update win/apple licenses"
This reverts commit aadf63da1d.
2022-09-21 14:28:43 -07:00
License Updater
aadf63da1d licenses: update win/apple licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-09-21 14:19:57 -07:00
Maisem Ali
d5781f61a9 ipn/ipnlocal: return usernames when Tailscale SSH is enabled
It was checking if the sshServer was initialized as a proxy, but that
could either not have been initialized yet or Tailscale SSH could have
been disabled after intialized.

Also bump tailcfg.CurrentCapabilityVersion

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-09-21 14:06:40 -07:00
Mihai Parparita
a7a0baf6b9 cmd/tsconnect: add error callback for SSH sessions
We were just logging them to the console, which is useful for debugging,
but we may want to show them in the UI too.

Updates tailscale/corp#6939

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-21 13:09:53 -07:00
Tom DNetto
e9b98dd2e1 control/controlclient,ipn/ipnlocal: wire tka enable/disable
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-09-21 12:57:59 -07:00
Andrew Dunham
b9b0bf65a0 wgengine/netstack: handle 4via6 packets when pinging (#5709)
Change-Id: Ib6ebbaa11219fb91b550ed7fc6ede61f83262e89
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-21 14:19:34 -04:00
Andrew Dunham
c6162c2a94 net/netcheck: add check for captive portal (#5593)
This doesn't change any behaviour for now, other than maybe running a
full netcheck more often. The intent is to start gathering data on
captive portals, and additionally, seeing this in the 'tailscale
netcheck' command should provide a bit of additional information to
users.

Updates #1634

Change-Id: I6ba08f9c584dc0200619fa97f9fde1a319f25c76
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-09-20 15:31:49 -04:00
Brad Fitzpatrick
aa5e494aba tsweb: export go_version in standard expvar vars
For monitoring.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-20 09:59:43 -07:00
Berk D. Demir
ff13c66f55 cmd/tailscale: fix configure-host command for Synology
d5e7e309 changed the `hostinfo.GetVersion` from distro and distro version
to UTS Name Release and moved distribution information under
`hostinfo.Distro*`.

`tailscale configure-host` command implementation for Synology DSM
environments relies on the old semantics of this string for matching DSM
Major version so it's been broken for a few days.

Pull in `hostinfo` and prefix match `hostinfo.DistroVersion` to match
DSM major version.

Signed-off-by: Berk D. Demir <bdd@mindcast.org>
2022-09-19 21:15:21 -07:00
Brad Fitzpatrick
ed248b04a7 cmd/tailscale: remove leftover debug prints from earlier commit
From 6632504f45

Change-Id: If21789232b3ecc14c1639cf87814af6fa73f535f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-19 21:13:56 -07:00
Mihai Parparita
8158dd2edc cmd/tsconnect: allow SSH connection timeout to be overridden
5 seconds may not be enough if we're still loading the derp map and
connecting to a slow machine.

Updates #5693

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-19 18:00:12 -07:00
Maisem Ali
6632504f45 cmd/tailscale/cli: [up] move lose-ssh check after other validations
The check was happening too early and in the case of error would wait 5
s and then error out. This makes it so that it does validations before
the SSH check.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-09-19 12:04:14 -07:00
Maisem Ali
054ef4de56 tailcfg: mark CapabilityFileSharingTarget as inter-node
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-09-19 11:08:34 -07:00
Brad Fitzpatrick
d045462dfb ipn/ipnlocal: add c2n method to get SSH username candidates
For control to fetch a list of Tailscale SSH username candidates to
filter against the Tailnet's SSH policy to present some valid
candidates to a user.

Updates #3802
Updates tailscale/corp#7007

Change-Id: I3dce57b7a35e66891d5e5572e13ae6ef3c898498
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-19 10:37:04 -07:00
Brad Fitzpatrick
d8eb111ac8 .github/workflows: add cross-android
This would've caught the regression from 7c49db02a before it was
submitted so 42f1d92ae0 wouldn't have been necessary to fix it.

Updates #4482

Change-Id: Ia4a9977e21853f68df96f043672c86a86c0181db
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-18 09:56:19 -07:00
Brad Fitzpatrick
832031d54b wgengine/magicsock: fix recently introduced data race
From 5c42990c2f, not yet released in a stable build.
Caught by existing tests.

Fixes #5685

Change-Id: Ia76bb328809d9644e8b96910767facf627830600
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-18 08:07:57 -07:00
Denton Gentry
42f1d92ae0 net/netns: implement UseSocketMark for Android.
Build fails on Android:
`../../../../go/pkg/mod/tailscale.com@v1.1.1-0.20220916223019-65c24b6334e9/wgengine/magicsock/magicsock_linux.go:133:12: undefined: netns.UseSocketMark`

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-09-17 23:19:24 -07:00
Brad Fitzpatrick
41bb47de0e cmd/tailscaled: respect $PORT on all platforms, not just Linux
Updates #5114

Change-Id: I6c6e28c493d6a026a03088157d08f9fd182ef373
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-17 12:30:29 -07:00
Brad Fitzpatrick
3562b5bdfa envknob, health: support Synology, show parse errors in status
Updates #5114

Change-Id: I8ac7a22a511f5a7d0dcb8cac470d4a403aa8c817
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-17 08:42:41 -07:00
phirework
5c42990c2f wgengine/magicsock: add client flag and envknob to disable heartbeat (#5638)
Baby steps towards turning off heartbeat pings entirely as per #540.
This doesn't change any current magicsock functionality and requires additional
changes to send/disco paths before the flag can be turned on.

Updates #540

Change-Id: Idc9a72748e74145b068d67e6dd4a4ffe3932efd0
Signed-off-by: Jenny Zhang <jz@tailscale.com>

Signed-off-by: Jenny Zhang <jz@tailscale.com>
2022-09-16 23:48:46 -04:00
Brad Fitzpatrick
65c24b6334 envknob: generalize Windows tailscaled-env.txt support
ipnserver previously had support for a Windows-only environment
variable mechanism that further only worked when Windows was running
as a service, not from a console.

But we want it to work from tailscaed too, and we want it to work on
macOS and Synology. So move it to envknob, now that envknob can change
values at runtime post-init.

A future change will wire this up for more platforms, and do something
more for CLI flags like --port, which the bug was originally about.

Updates #5114

Change-Id: I9fd69a9a91bb0f308fc264d4a6c33e0cbe352d71
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-16 15:30:19 -07:00
Brad Fitzpatrick
4bda41e701 Dockerfile: add test that build-env Alpine version matches go.mod
So things like #5660 don't happen in the future.

Change-Id: I01234f241e297d5b7bdd18da1bb3cc5420ad2225
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-16 12:19:09 -07:00
Andrew Dunham
9b71008ef2 control/controlhttp: move Dial options into options struct (#5661)
This turns 'dialParams' into something more like net.Dialer, where
configuration fields are public on the struct.

Split out of #5648

Change-Id: I0c56fd151dc5489c3c94fb40d18fd639e06473bc
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-16 15:06:25 -04:00
Luis Peralta
5623ef0271 Update Dockerfile to use golang:1.19-alpine
Tailscale @4a82b31 does not build in the container image due to using golang:1.18 image

Signed-off-by: Luis Peralta <luis.peralta@gmail.com>
2022-09-16 11:40:31 -07:00
Tyler Lee
486eecc063 Switched Secret snippet to match run.sh
Signed-off-by: Tyler Lee <tyler.lee@radius.ai>
2022-09-16 11:20:33 -07:00
Tyler Lee
b830c9975f Updated secret example in readme to match the sidecar key value
Signed-off-by: Tyler Lee <tyler.lee@radius.ai>
2022-09-16 11:20:33 -07:00
Brad Fitzpatrick
4a82b317b7 ipn/{ipnlocal,localapi}: use strs.CutPrefix, add more domain validation
The GitHub CodeQL scanner flagged the localapi's cert domain usage as a problem
because user input in the URL made it to disk stat checks.

The domain is validated against the ipnstate.Status later, and only
authenticated root/configured users can hit this, but add some
paranoia anyway.

Change-Id: I373ef23832f1d8b3a27208bc811b6588ae5a1ddd
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-16 05:52:33 -07:00
Eng Zer Jun
f0347e841f refactor: move from io/ioutil to io and os packages
The io/ioutil package has been deprecated as of Go 1.16 [1]. This commit
replaces the existing io/ioutil functions with their new definitions in
io and os packages.

Reference: https://golang.org/doc/go1.16#ioutil
Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2022-09-15 21:45:53 -07:00
Mihai Parparita
027111fb5a derp: update DERP acronym expansion
Makes the package description consistent with other documentation.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-15 16:08:12 -07:00
Mihai Parparita
1ce0e558a7 cmd/derper, control/controlhttp: disable WebSocket compression
The data that we send over WebSockets is encrypted and thus not
compressible. Additionally, Safari has a broken implementation of compression
(see nhooyr/websocket#218) that makes enabling it actively harmful.

Fixes tailscale/corp#6943

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-15 15:35:49 -07:00
Brad Fitzpatrick
74674b110d envknob: support changing envknobs post-init
Updates #5114

Change-Id: Ia423fc7486e1b3f3180a26308278be0086fae49b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-15 15:04:02 -07:00
Brad Fitzpatrick
33ee2c058e wgengine: update comments, remove redundant code in forceFullWireguardConfig
Change-Id: I464a0bce36e3a362c7d7ace0e8d2dd77fa825ee2
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-15 13:03:18 -07:00
Brad Fitzpatrick
d34dd43562 ipn/ipnlocal: remove unused envknob
Change-Id: I6d18af2c469eb660e6ca81d1dcc2af33c9e628aa
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-15 11:06:15 -07:00
Andrew Dunham
cf61070e26 net/dnscache: add better logging to bootstrap DNS path (#5640)
Change-Id: I4cde3a72e06dac18df856a0cfeac10ab7e3a9108
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-15 10:41:45 -04:00
Kristoffer Dalby
81574a5c8d portlist: normalise space delimited process names (#5634) 2022-09-15 12:17:31 +02:00
Mihai Parparita
9c6bdae556 cmd/tsconnect: use the parent window for beforeunload event listener
The SSH session may be rendered in a different window that the one that
is executing the script.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-14 11:35:13 -07:00
Mihai Parparita
82e82d9b7a net/dns/resolver: remove unused responseTimeout constant
Timeout is now enforced elsewhere, see discussion in https://github.com/tailscale/tailscale/pull/4408#discussion_r970092333.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-13 18:12:11 -07:00
nyghtowl
0f16640546 net/dns: fix fmt error on Revert print
Fixes #5619

Signed-off-by: nyghtowl <warrick@tailscale.com>
2022-09-13 16:36:15 -07:00
Joe Tsai
aa0064db4d logpolicy: add NewWithConfigPath (#5625)
The version.CmdName implementation is buggy such that it does not correctly
identify the binary name if it embeds other go binaries.
For now, add a NewWithConfigPath API that allows the caller to explicitly
specify this information.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-09-13 16:30:40 -07:00
Brad Fitzpatrick
45a3de14a6 cmd/tailscaled, tailcfg, hostinfo: add flag to disable logging + support
As noted in #5617, our documented method of blocking log.tailscale.io
DNS no longer works due to bootstrap DNS.

Instead, provide an explicit flag (--no-logs-no-support) and/or env
variable (TS_NO_LOGS_NO_SUPPORT=true) to explicitly disable logcatcher
uploads. It also sets a bit on Hostinfo to say that the node is in that
mode so we can end any support tickets from such nodes more quickly.

This does not yet provide an easy mechanism for users on some
platforms (such as Windows, macOS, Synology) to set flags/env. On
Linux you'd used /etc/default/tailscaled typically. Making it easier
to set flags for other platforms is tracked in #5114.

Fixes #5617
Fixes tailscale/corp#1475

Change-Id: I72404e1789f9e56ec47f9b7021b44c025f7a373a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-13 11:47:36 -07:00
Tom DNetto
f6da2220d3 wgengine: set fwmark masks in netfilter & ip rules
This change masks the bitspace used when setting and querying the fwmark on packets. This allows
tailscaled to play nicer with other networking software on the host, assuming the other networking
software is also using fwmarks & a different mask.

IPTables / mark module has always supported masks, so this is safe on the netfilter front.

However, busybox only gained support for parsing + setting masks in 1.33.0, so we make sure we
arent such a version before we add the "/<mask>" syntax to an ip rule command.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-09-13 09:52:26 -07:00
Mihai Parparita
b22b565947 cmd/tsconnect: allow xterm.js terminal options to be passed in
Allows clients to use a custom theme and other xterm.js customization
options.

Fixes #5610

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-12 16:39:02 -07:00
David Anderson
7c49db02a2 wgengine/magicsock: don't use BPF receive when SO_MARK doesn't work.
Fixes #5607

Signed-off-by: David Anderson <danderson@tailscale.com>
2022-09-12 15:05:44 -07:00
Mihai Parparita
c312e0d264 cmd/tsconnect: allow hostname to be specified
The auto-generated hostname is nice as a default, but there are cases
where the client has a more specific name that it can generate.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-12 14:26:50 -07:00
Mihai Parparita
11fcc3a7b0 cmd/tsconnect: fix xterm.js link opening not working when rendered into another window
The default WebLinksAddon handler uses window.open(), but that gets blocked
by the popup blocker when the event being handled is another window. We
instead need to invoke open() on the window that the event was triggered
in.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-12 13:54:27 -07:00
Will Norris
f03a63910d cmd/tailscale: add licenses link to web UI
The `tailscale web` UI is the primary interface for Synology and Home
Assistant users (and perhaps others), so is the logical place to put our
open source license notices.  I don't love adding things to what is
currently a very minimal UI, but I'm not sure of a better option.

Updates tailscale/corp#5780

Signed-off-by: Will Norris <will@tailscale.com>
2022-09-12 12:06:44 -07:00
Brad Fitzpatrick
024257ef5a net/stun: unmap IPv4 addresses in 16 byte STUN replies
Updates #5602

Change-Id: I2276ad2bfb415b9ff52f37444f2a1d74b38543b1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-12 12:03:27 -07:00
Andrew Dunham
eb5939289c cmd/derper: add /generate_204 endpoint (#5601)
For captive portal detection.

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-12 13:43:50 -04:00
Brad Fitzpatrick
16939f0d56 hostinfo: detect being run in a container in more ways
Change-Id: I038ff7705ba232e6cf8dcc9775357ef708d43762
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-12 07:02:01 -07:00
Brad Fitzpatrick
d5e7e3093d hostinfo, tailcfg: split Hostinfo.OSVersion into separate fields
Stop jamming everything into one string.

Fixes #5578

Change-Id: I7dec8d6c073bddc7dc5f653e3baf2b4bf6b68378
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-11 21:40:28 -07:00
Brad Fitzpatrick
708b7bff3d net/dns/publicdns: also support NextDNS DoH query parameters
The plan has changed. Doing query parameters rather than path +
heades. NextDNS added support for query parameters.

Updates #2452

Change-Id: I4783c0a06d6af90756d9c80a7512644ba702388c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-11 09:01:03 -07:00
Brad Fitzpatrick
81bc4992f2 net/netns: add TS_FORCE_LINUX_BIND_TO_DEVICE for Linux
For debugging a macOS-specific magicsock issue. macOS runs in
bind-to-interface mode always. This lets me force Linux into the same
mode as macOS, even if the Linux kernel supports SO_MARK, as it
usually does.

Updates #2331 etc

Change-Id: Iac9e4a7429c1781337e716ffc914443b7aa2869d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-10 18:33:30 -07:00
Brad Fitzpatrick
f3ce1e2536 util/mak: deprecate NonNil, add type-safe NonNilSliceForJSON, NonNilMapForJSON
And put the rationale in the name too to save the callers the need for a comment.

Change-Id: I090f51b749a5a0641897ee89a8fb2e2080c8b782
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-10 12:19:22 -07:00
Brad Fitzpatrick
e7376aca25 net/dns/resolver: set DNS-over-HTTPS Accept and User-Agent header on requests
Change-Id: I14b821771681e70405a507f43229c694159265ff
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-10 08:57:26 -07:00
Tom DNetto
ed2b8b3e1d wgengine/router: reduce routing rule priority for openWRT + mwan3
Fixes #3659

Signed-off-by: Tom DNetto <tom@tailscale.com>
Co-authored-by: Ian Foster <ian@vorsk.com>
2022-09-09 18:21:24 -07:00
Brad Fitzpatrick
c14361e70e net/dns/publicdns: support NextDNS DoH URLs with path parameters
Updates #2452

Change-Id: I0f1c34cc1672e87e7efd0adfe4088724dd0de3ed
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-09 14:16:23 -07:00
Mihai Parparita
b302742137 cmd/tsconnect: enable web links addon in the terminal
More user friendly, and as a side-effect we handle SSH check mode better,
since the URL that's output is now clickable.

Fixes #5247

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-09 11:05:01 -07:00
Mihai Parparita
62035d6485 cmd/tsconnect: switch back to public version of xterm npm package
xtermjs/xterm.js#4069 was merged and published (in 5.0.0-beta.58),
no need for the fork added by 01e6565e8a.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-09 10:50:43 -07:00
Brad Fitzpatrick
89fee056d3 cmd/derper: add robots.txt to disallow all
Fixes #5565

Change-Id: I5626ec2116d9be451caef651dc301b7a82e35550
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-09 10:29:46 -07:00
License Updater
3ed366ee1e licenses: update android licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-09-09 09:14:22 -07:00
Brad Fitzpatrick
2aade349fc net/dns, types/dnstypes: update some comments, tests for DoH
Clarify & verify that some DoH URLs can be sent over tailcfg
in some limited cases.

Updates #2452

Change-Id: Ibb25db77788629c315dc26285a1059a763989e24
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-08 17:16:13 -07:00
Brad Fitzpatrick
58abae1f83 net/dns/{publicdns,resolver}: add NextDNS DoH support
NextDNS is unique in that users create accounts and then get
user-specific DNS IPs & DoH URLs.

For DoH, the customer ID is in the URL path.

For IPv6, the IP address includes the customer ID in the lower bits.

For IPv4, there's a fragile "IP linking" mechanism to associate your
public IPv4 with an assigned NextDNS IPv4 and that tuple maps to your
customer ID.

We don't use the IP linking mechanism.

Instead, NextDNS is DoH-only. Which means using NextDNS necessarily
shunts all DNS traffic through 100.100.100.100 (programming the OS to
use 100.100.100.100 as the global resolver) because operating systems
can't usually do DoH themselves.

Once it's in Tailscale's DoH client, we then connect out to the known
NextDNS IPv4/IPv6 anycast addresses.

If the control plane sends the client a NextDNS IPv6 address, we then
map it to the corresponding NextDNS DoH with the same client ID, and
we dial that DoH server using the combination of v4/v6 anycast IPs.

Updates #2452

Change-Id: I3439d798d21d5fc9df5a2701839910f5bef85463
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-08 12:50:32 -07:00
Mihai Parparita
01e6565e8a cmd/tsconnect: temporarily switch to xterm.js fork that handles popup windows
Allows other work to be unblocked while xtermjs/xterm.js#4069 is worked
through.

To enable testing the popup window handling, the standalone app allows
opening of SSH sessions in new windows by holding down the alt key
while pressing the SSH button.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-08 09:30:52 -07:00
Mihai Parparita
2400ba28b1 cmd/tsconnect: handle terminal resizes before the SSH session is created
Store the requested size is a struct field, and use that when actually
creating the SSH session.

Fixes #5567

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-08 09:30:52 -07:00
Brad Fitzpatrick
2266b59446 go.toolchain.rev: bump to Go 1.19.1
See https://github.com/tailscale/go/pull/34

Change-Id: I56806358cd1be4a2b8f509883e47c93083d82bdf
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-07 22:13:01 -07:00
Brad Fitzpatrick
ad7546fb9f tailcfg: fix broken test from comment change
Fix broken build from 255c0472fb

"Oh, that's safe to commit because most tests are passing and it's
just a comment change!", I thought, forgetting I'd added a test that
parses its comments.

Change-Id: Iae93d595e06fec48831215a98adbb270f3bfda05
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-07 22:03:38 -07:00
Brad Fitzpatrick
255c0472fb tailcfg: reformat CurrentCapabilityVersion to be a bulleted list
gofmt in 1.19 is now opinionated about structured text formatting in
comments. It did not like our style and kept fighting us whenever we
changed these lines. Give up the fight and be a bulleted list for it.

See:

* https://go.dev/doc/go1.19#go-doc and
* https://go.dev/doc/comment

Updates #4872

Change-Id: Ifae431218471217168c003ab3b4e03c394ca8105
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-07 21:53:16 -07:00
License Updater
c5adc5243c licenses: update win/apple licenses
Signed-off-by: GitHub <noreply@github.com>
2022-09-07 14:28:24 -07:00
Andrew Dunham
c9961b8b95 cmd/derper: filter out useless HTTP error logs (#5563)
These errors aren't actionable and just fill up logs with useless data.
See the following Go issue for more details:
  https://golang.org/issue/26918

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-07 16:31:06 -04:00
License Updater
8fdf137571 licenses: update tailscale{,d} licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-09-06 19:19:15 -07:00
Colin Adler
9c8bbc7888 wgengine/magicsock: fix panic in http debug server
Fixes an panic in `(*magicsock.Conn).ServeHTTPDebug` when the
`recentPongs` ring buffer for an endpoint wraps around.

Signed-off-by: Colin Adler <colin1adler@gmail.com>
2022-09-06 15:02:07 -07:00
Andrew Dunham
9240f5c1e2 wgengine/netstack: only accept connection after dialing (#5503)
If we accept a forwarded TCP connection before dialing, we can
erroneously signal to a client that we support IPv6 (or IPv4) without
that actually being possible. Instead, we only complete the client's TCP
handshake after we've dialed the outbound connection; if that fails, we
respond with a RST.

Updates #5425 (maybe fixes!)

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-06 16:04:10 -04:00
Mihai Parparita
2f702b150e cmd/tsconnect: add dev-pkg command for two-sided development
Allows imports of the NPM package added by 1a093ef482
to be replaced with import("http://localhost:9090/pkg/pkg.js"), so that
changes can be made in parallel to both the module and code that uses
it (without any need for NPM publishing or even building of the package).

Updates #5415

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-09-06 12:42:58 -07:00
James Tucker
672c2c8de8 wgengine/magicsock: add filter to ignore disco to old/other ports
Incoming disco packets are now dropped unless they match one of the
current bound ports, or have a zero port*.

The BPF filter passes all packets with a disco header to the raw packet
sockets regardless of destination port (in order to avoid needing to
reconfigure BPF on rebind).

If a BPF enabled node has just rebound, due to restart or rebind, it may
receive and reply to disco ping packets destined for ports other than
those which are presently bound. If the pong is accepted, the pinging
node will now assume that it can send WireGuard traffic to the pinged
port - such traffic will not reach the node as it is not destined for a
bound port.

*The zero port is ignored, if received. This is a speculative defense
and would indicate a problem in the receive path, or the BPF filter.
This condition is allowed to pass as it may enable traffic to flow,
however it will also enable problems with the same symptoms this patch
otherwise fixes.

Fixes #5536

Signed-off-by: James Tucker <james@tailscale.com>
2022-09-06 12:25:04 -07:00
James Tucker
be140add75 wgengine/magicsock: fix regression in initial bind for js
1f959edeb0 introduced a regression for JS
where the initial bind no longer occurred at all for JS.

The condition is moved deeper in the call tree to avoid proliferation of
higher level conditions.

Updates #5537

Signed-off-by: James Tucker <james@tailscale.com>
2022-09-06 12:23:44 -07:00
James Tucker
1f959edeb0 wgengine/magicksock: remove nullability of RebindingUDPConns
Both RebindingUDPConns now always exist. the initial bind (which now
just calls rebind) now ensures that bind is called for both, such that
they both at least contain a blockForeverConn. Calling code no longer
needs to assert their state.

Signed-off-by: James Tucker <james@tailscale.com>
2022-09-06 12:08:31 -07:00
Brad Fitzpatrick
56f6fe204b go.mod, wgengine/wgint: bump wireguard-go
For b51010ba13

Change-Id: Ibf767dfad98aef7e9f0505d91c0d26f924e046d5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-06 11:34:30 -07:00
Andrew Dunham
f52a659076 net/dnsfallback: allow setting log function (#5550)
This broke a test in corp that enforces we don't use the log package.

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-09-06 11:19:50 -04:00
Andrew Dunham
b8596f2a2f net/dnsfallback: cache most recent DERP map on disk (#5545)
This is especially helpful as we launch newer DERPs over time, and older
clients have progressively out-of-date static DERP maps baked in. After
this, as long as the client has successfully connected once, it'll cache
the most recent DERP map it knows about.

Resolves an in-code comment from @bradfitz

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-09-05 14:36:30 -04:00
Maisem Ali
060ecb010f docs/k8s: make run.sh handle SIGINT
It was previously using jobcontrol to achieve this, but that apparently
doesn't work when there is no tty. This makes it so that it directly
handles SIGINT and SIGTERM and passes it on to tailscaled. I tested this
works on a Digital Ocean K8s cluster.

Fixes #5512

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-09-04 15:50:02 -07:00
Brad Fitzpatrick
02de34fb10 cmd/derper: add flag to run derper in bootstrap-dns-only mode
Change-Id: Iba128e94464afa605bc9df1f06a91d296380eed0
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-03 19:51:00 -07:00
Will Norris
3344c3b89b tsnet: add Server method to listener
Allow callers to verify that a net.Listener is a tsnet.listener by type
asserting against this Server method, as well as providing access to the
underlying Server.

This is initially being added to support the caddy integration in
caddyserver/caddy#5002.

Signed-off-by: Will Norris <will@tailscale.com>
2022-09-02 16:29:49 -07:00
Andrew Dunham
a0bae4dac8 cmd/derper: add support for unpublished bootstrap DNS entries (#5529)
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-09-02 14:48:30 -04:00
Tom DNetto
9132b31e43 tailcfg: refactor/implement wire structs for TKA
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-09-02 10:29:46 -07:00
Kris Brandow
19008a3023 net/dnscache: use net/netip
Removes usage of net.IP and net.IPAddr where possible from net/dnscache.

Fixes #5282

Signed-off-by: Kris Brandow <kris.brandow@gmail.com>
2022-09-01 17:54:19 -04:00
Brad Fitzpatrick
ba3cc08b62 cmd/tailscale/cli: add backwards compatibility 'up' processing for legacy client
Updates tailscale/corp#6781

Change-Id: I843fc810cbec0140d423d65db81e90179d6e0fa5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-09-01 14:21:48 -07:00
License Updater
d8bfb7543e licenses: update win/apple licenses
Signed-off-by: GitHub <noreply@github.com>
2022-09-01 14:10:55 -07:00
James Tucker
265b008e49 wgengine: fix race on endpoints in getStatus
Signed-off-by: James Tucker <james@tailscale.com>
2022-09-01 10:58:04 -07:00
Bertrand Lorentz
a5ad57472a cli/cert: Fix help message for --key-file
Signed-off-by: Bertrand Lorentz <bertrand.lorentz@gmail.com>
2022-09-01 10:57:00 -07:00
Xe Iaso
3564fd61b5 cmd/gitops-pusher: standardize hujson before posting to validate (#5525)
Apparently the validate route doesn't check content-types or handle
hujson with comments correctly. This patch makes gitops-pusher convert
the hujson to normal json.

Signed-off-by: Xe <xe@tailscale.com>

Signed-off-by: Xe <xe@tailscale.com>
2022-09-01 13:38:32 -04:00
nyghtowl
cfbbcf6d07 cmd/nginx-auth/nginx-auth: update auth to allow for new domains
With MagicDNS GA, we are giving every tailnet a tailnet-<hex>.ts.net name.
We will only parse out if legacy domains include beta.tailscale.net; otherwise,
set tailnet to the full domain format going forward.

Signed-off-by: nyghtowl <warrick@tailscale.com>
2022-08-31 20:18:13 -07:00
License Updater
9c66dce8e0 licenses: update win/apple licenses
Signed-off-by: GitHub <noreply@github.com>
2022-08-31 15:38:41 -07:00
Brad Fitzpatrick
e470893ba0 wgengine/magicsock: use mak in another spot
Change-Id: I0a46d6243371ae6d126005a2bd63820cb2d1db6b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-31 15:30:26 -07:00
Andrew Dunham
c72caa6672 wgengine/magicsock: use AF_PACKET socket + BPF to read disco messages
This is entirely optional (i.e. failing in this code is non-fatal) and
only enabled on Linux for now. Additionally, this new behaviour can be
disabled by setting the TS_DEBUG_DISABLE_AF_PACKET environment variable.

Updates #3824
Replaces #5474

Co-authored-by: Andrew Dunham <andrew@du.nham.ca>
Signed-off-by: David Anderson <danderson@tailscale.com>
2022-08-31 14:52:31 -07:00
Mihai Parparita
58f35261d0 cmd/tsconnect: remove debugging code
Remove test prefix added to validate the error code from 27f36f77c3.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-31 10:46:47 -07:00
Tom DNetto
be95aebabd tka: implement credential signatures (key material delegation)
This will be needed to support preauth-keys with network lock in the future,
so getting the core mechanics out of the way now.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-31 10:13:13 -07:00
License Updater
490acdefb6 licenses: update android licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-08-31 09:55:41 -07:00
License Updater
84b74825f0 licenses: update tailscale{,d} licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-08-31 08:38:55 -07:00
Brad Fitzpatrick
9bd9f37d29 go.mod: bump wireguard/windows, which moves to using net/netip
Updates #5162

Change-Id: If99a3f0000bce0c01bdf44da1d513f236fd7cdf8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-31 08:36:56 -07:00
Charlotte Brandhorst-Satzkorn
185f2e4768 words: this title should have been a pun, but I chickened out (#5506)
Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2022-08-31 07:02:49 -07:00
Denton Gentry
53e08bd7ea VERSION.txt: this is 1.31
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-31 06:48:24 -07:00
Andrew Dunham
70ed22ccf9 util/uniq: add ModifySliceFunc (#5504)
Follow-up to #5491. This was used in control... oops!

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-30 18:51:18 -04:00
Tom DNetto
7ca17b6bdb tka: validate key after UpdateKey before applying state
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-30 15:23:30 -07:00
Andrew Dunham
e945d87d76 util/uniq: use generics instead of reflect (#5491)
This takes 75% less time per operation per some benchmarks on my mac.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-08-30 17:56:51 -04:00
Andrew Dunham
1ac4a26fee ipn/localapi: send Tailscale version in ACME User-Agent (#5499)
Requested by a friend at Let's Encrypt.

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-30 16:48:59 -04:00
Brad Fitzpatrick
761163815c tailcfg: add Hostinfo.Userspace{,Router} bits
Change-Id: Iad47f904872f2df146c1f63945f79cfddeac7fe8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-30 12:39:03 -07:00
Brad Fitzpatrick
9f6c8517e0 net/dns: set OS DNS to 100.100.100.100 for route-less ExtraRecords [cap 41]
If ExtraRecords (Hosts) are specified without a corresponding split
DNS route and global DNS is specified, then program the host OS DNS to
use 100.100.100.100 so it can blend in those ExtraRecords.

Updates #1543

Change-Id: If49014a5ecc8e38978ff26e54d1f74fe8dbbb9bc
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-30 12:36:25 -07:00
Mihai Parparita
27f36f77c3 cmd/tsconnect: output errors to the JS console too
We were just outputting them to the terminal, but that's hard to debug
because we immediately tear down the terminal when getting an error.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-30 10:46:44 -07:00
Xe Iaso
122bd667dc cmd/gitops-pusher: be less paranoid about external modifications (#5488)
This makes a "modified externally" error turn into a "modified externally" warning. It means CI won't fail if someone does something manually in the admin console.

Signed-off-by: Xe <xe@tailscale.com>
2022-08-30 09:41:25 -04:00
Joe Tsai
21cd402204 logtail: do not log when backing off (#5485) 2022-08-30 06:21:03 -07:00
Denton Gentry
0ae0439668 docs/k8s: add IPv6 forwarding in proxy.yaml
Fixes https://github.com/tailscale/tailscale/issues/4999

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-30 06:03:15 -07:00
Denton Gentry
6dcc6313a6 CI: add go mod tidy workflow
Fixes https://github.com/tailscale/tailscale/issues/4567

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-30 06:02:34 -07:00
Denton Gentry
78dbb59a00 CI: make all workflows get Go version from go.mod
The next time we update the toolchain, all of the CI
Actions will automatically use it when go.mod is updated.

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-30 06:02:14 -07:00
Joe Tsai
7e40071571 util/deephash: handle slice edge-cases (#5471)
It is unclear whether the lack of checking nil-ness of slices
was an oversight or a deliberate feature.
Lacking a comment, the assumption is that this was an oversight.

Also, expand the logic to perform cycle detection for recursive slices.
We do this on a per-element basis since a slice is semantically
equivalent to a list of pointers.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-30 00:33:18 -07:00
James Tucker
90dc0e1702 wgengine: remove unused singleflight group
Signed-off-by: James Tucker <james@tailscale.com>
2022-08-29 18:16:30 -07:00
Mihai Parparita
2c18517121 cmd/tsconnect: add npm publish workflow
Adds an on-demand GitHub Action that publishes the package to the npm
registry (currently under tailscale-connect, will be moved to
@tailscale/connect once we get control of the npm org).

Makes the package.json for the NPM package be dynamically generated to
have the current Tailscale client version.

Updates #5415

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-29 18:02:51 -07:00
Andrew Dunham
d6c3588ed3 wgengine/wgcfg: only write peer headers if necessary (#5449)
On sufficiently large tailnets, even writing the peer header (~95 bytes)
can result in a large amount of data that needs to be serialized and
deserialized. Only write headers for peers that need to have their
configuration changed.

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-29 20:47:52 -04:00
James Tucker
81dba3738e wgengine: remove all peer status from open timeout diagnostics
Avoid contention from fetching status for all peers, and instead fetch
status for a single peer.

Updates tailscale/coral#72
Signed-off-by: James Tucker <james@tailscale.com>
2022-08-29 15:54:33 -07:00
James Tucker
ad1cc6cff9 wgengine: use Go API rather than UAPI for status
Signed-off-by: James Tucker <james@tailscale.com>
2022-08-29 15:38:16 -07:00
Maisem Ali
68d9d161f4 net/dns: [win] fix regression in disableDynamicUpdate
Somehow I accidentally set the wrong registry value here.
It should be DisableDynamicUpdate=1 and not EnableDNSUpdate=0.

This is a regression from 545639e.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-29 15:28:59 -07:00
Brad Fitzpatrick
c66f99fcdc tailcfg, control/controlclient, ipn/ipnlocal: add c2n (control-to-node) system
This lets the control plane can make HTTP requests to nodes.

Then we can use this for future things rather than slapping more stuff
into MapResponse, etc.

Change-Id: Ic802078c50d33653ae1f79d1e5257e7ade4408fd
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-29 15:18:40 -07:00
Brad Fitzpatrick
08b3f5f070 wgengine/wgint: add shady temporary package to get at wireguard internals
For #5451

Change-Id: I43482289e323ba9142a446d551ab7a94a467c43a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-29 10:03:51 -07:00
Nahum Shalman
66d7d2549f logger: migrate rusage syscall use to x/sys/unix
This will be helpful for illumos (#697) and should be safe
everywhere else.

Signed-off-by: Nahum Shalman <nahamu@gmail.com>
2022-08-28 08:29:41 -07:00
Nahum Shalman
d20392d413 cmd/tailscale: add emoji for illumos in status subcommand
Signed-off-by: Nahum Shalman <nahamu@gmail.com>
2022-08-28 08:29:31 -07:00
Andrew Dunham
58cc049a9f util/cstruct: add package for decoding padded C structures (#5429)
I was working on my "dump iptables rules using only syscalls" branch and
had a bunch of C structure decoding to do. Rather than manually
calculating the padding or using unsafe trickery to actually cast
variable-length structures to Go types, I'd rather use a helper package
that deals with padding for me.

Padding rules were taken from the following article:
  http://www.catb.org/esr/structure-packing/

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-08-28 11:12:09 -04:00
Andrew Dunham
9b77ac128a wgengine: print in-flight operations on watchdog trigger (#5447)
In addition to printing goroutine stacks, explicitly track all in-flight
operations and print them when the watchdog triggers (along with the
time they were started at). This should make debugging watchdog failures
easier, since we can look at the longest-running operation(s) first.

Signed-off-by: Andrew Dunham <andrew@tailscale.com>

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-27 22:06:18 -04:00
Andrew Dunham
e1738ea78e chirp: add a 10s timeout when communicating with BIRD (#5444)
Prior to this change, if BIRD stops responding wgengine.watchdogEngine
will crash tailscaled.

This happens because in wgengine.userspaceEngine, we end up blocking
forever trying to write a request to or read a response from BIRD with
wgLock held, and then future watchdog'd calls will block on acquiring
that mutex until the watchdog kills the process. With the timeout, we at
least get the chance to print an error message and decide whether we
want to crash or not.

Updates tailscale/coral#72

Signed-off-by: Andrew Dunham <andrew@tailscale.com>

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-27 20:49:31 -04:00
Joe Tsai
9bf13fc3d1 util/deephash: remove getTypeInfo (#5469)
Add a new lookupTypeHasher function that is just a cached front-end
around the makeTypeHasher function.
We do not need to worry about the recursive type cycle issue that
made getTypeInfo more complicated since makeTypeHasher
is not directly recursive. All calls to itself happen lazily
through a sync.Once upon first use.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 17:39:51 -07:00
Joe Tsai
ab7e6f3f11 util/deephash: require pointer in API (#5467)
The entry logic of Hash has extra complexity to make sure
we always have an addressable value on hand.
If not, we heap allocate the input.
For this reason we document that there are performance benefits
to always providing a pointer.
Rather than documenting this, just enforce it through generics.

Also, delete the unused HasherForType function.
It's an interesting use of generics, but not well tested.
We can resurrect it from code history if there's a need for it.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 16:08:31 -07:00
Joe Tsai
c5b1565337 util/deephash: move pointer and interface logic to separate function (#5465)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:51:34 -07:00
Joe Tsai
d2e2d8438b util/deephash: move map logic to separate function (#5464)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

There is a minor adjustment where we hash the length of the map
to be more on the cautious side.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:49:26 -07:00
Joe Tsai
23c3831ff9 util/deephash: coalesce struct logic (#5466)
Rather than having two copies []fieldInfo,
just maintain one and perform merging in the same pass.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:39:46 -07:00
Joe Tsai
296b008b9f util/deephash: move array and slice logic to separate function (#5463)
This helps pprof better identify which Go kinds take the most time
since the kind is always in the function name.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 15:37:36 -07:00
Joe Tsai
31bf3874d6 util/deephash: use unsafe.Pointer instead of reflect.Value (#5459)
Use of reflect.Value.SetXXX panics if the provided argument was
obtained from an unexported struct field.
Instead, pass an unsafe.Pointer around and convert to a
reflect.Value when necessary (i.e., for maps and interfaces).
Converting from unsafe.Pointer to reflect.Value guarantees that
none of the read-only bits will be populated.

When running in race mode, we attach type information to the pointer
so that we can type check every pointer operation.
This also type-checks that direct memory hashing is within
the valid range of a struct value.

We add test cases that previously caused deephash to panic,
but now pass.

Performance:

	name              old time/op    new time/op    delta
	Hash              14.1µs ± 1%    14.1µs ± 1%    ~     (p=0.590 n=10+9)
	HashPacketFilter  2.53µs ± 2%    2.44µs ± 1%  -3.79%  (p=0.000 n=9+10)
	TailcfgNode       1.45µs ± 1%    1.43µs ± 0%  -1.36%  (p=0.000 n=9+9)
	HashArray         318ns ± 2%     318ns ± 2%    ~      (p=0.541 n=10+10)
	HashMapAcyclic    32.9µs ± 1%    31.6µs ± 1%  -4.16%  (p=0.000 n=10+9)

There is a slight performance gain due to the use of unsafe.Pointer
over reflect.Value methods. Also, passing an unsafe.Pointer (1 word)
on the stack is cheaper than passing a reflect.Value (3 words).

Performance gains are diminishing since SHA-256 hashing now dominates the runtime.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 12:30:35 -07:00
Joe Tsai
e0c5ac1f02 util/deephash: add debug printer (#5460)
When built with "deephash_debug", print the set of HashXXX methods.

Example usage:

	$ go test -run=GetTypeHasher/string_slice -tags=deephash_debug
	U64(2)+U64(3)+S("foo")+U64(3)+S("bar")+FIN

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-27 12:14:07 -07:00
Andrew Dunham
e8f09d24c7 wgengine: use a singleflight.Group to reduce status contention (#5450)
Updates tailscale/coral#72

Signed-off-by: Andrew Dunham <andrew@tailscale.com>

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-27 12:36:07 -04:00
Joe Tsai
70f9fc8c7a util/deephash: rely on direct memory hashing for primitive kinds (#5457)
Rather than separate functions to hash each kind,
just rely on the fact that these are direct memory hashable,
thus simplifying the code.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-26 20:50:56 -07:00
Brad Fitzpatrick
f1c9812188 tailcfg: add Hostinfo.GoVersion
So next time something like #5340 happens we can identify all affected
nodes and have the control plane send them health warnings.

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-26 18:04:15 -07:00
Nahum Shalman
214242ff62 net/dns/publicdns: Add Mullvad DoH
See https://mullvad.net/en/help/dns-over-https-and-dns-over-tls/

The Mullvad DoH servers appear to only speak HTTP/2 and
the use of a non-nil DialContext in the http.Transport
means that ForceAttemptHTTP2 must be set to true to be
able to use them.

Signed-off-by: Nahum Shalman <nahamu@gmail.com>
2022-08-26 17:46:30 -07:00
Joe Tsai
531ccca648 util/deephash: delete slow path (#5423)
Every implementation of typeHasherFunc always returns true,
which implies that the slow path is no longer executed.
Delete it.

h.hashValueWithType(v, ti, ...) is deleted as it is equivalent to:
	ti.hasher()(h, v)

h.hashValue(v, ...) is deleted as it is equivalent to:
	ti := getTypeInfo(v.Type())
	ti.hasher()(h, v)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-26 17:46:22 -07:00
Will Norris
3d328b82ee .github/actions: add signoff to go-licenses commits
also set git committer, which is apparently what this action uses for
signoff rather than git author.  Remove branch-suffix, which isn't
proving useful, and add installation_id, which isn't technically
necessary in the tailscale/tailscale repo, but makes this consistent
with the workflows in other repos.

Signed-off-by: Will Norris <will@tailscale.com>
2022-08-26 16:45:36 -07:00
License Updater
d95b95038c licenses: update android licenses
Signed-off-by: License Updater <noreply@tailscale.com>
2022-08-26 16:44:33 -07:00
License Updater
ceb8c5d1e9 licenses: update win/apple licenses
Signed-off-by: GitHub <noreply@github.com>
2022-08-26 14:12:52 -07:00
Will Norris
d1dd04e327 cmd/tailscale: use platform specific license link 2022-08-26 13:40:56 -07:00
Will Norris
79cf550823 cmd/tailscale: add licenses subcommand
Signed-off-by: Will Norris <will@tailscale.com>
2022-08-26 13:40:56 -07:00
Tom DNetto
79905a1162 tka: make storage a parameter rather than an Authority struct member
Updates #5435

Based on the discussion in #5435, we can better support transactional data models
by making the underlying storage layer a parameter (which can be specialized for
the request) rather than a long-lived member of Authority.

Now that Authority is just an instantaneous snapshot of state, we can do things
like provide idempotent methods and make it cloneable, too.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-26 10:44:28 -07:00
Mihai Parparita
7d1357162e cmd/tsconnect: expose runSSHSession in npm package
Move it to lib/ so that it can be used in both the app and the package.

Updates #5415

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-26 09:24:44 -07:00
License Updater
e4b5b92b82 licenses: update android licenses 2022-08-25 15:28:25 -07:00
License Updater
ff97a97f08 licenses: update win/apple licenses 2022-08-25 14:42:58 -07:00
Tom DNetto
f580f4484f tka: move disablement logic out-of-band from AUMs
It doesn't make a ton of sense for disablement to be communicated as an AUM, because
any failure in the AUM or chain mechanism will mean disablement wont function.

Instead, tracking of the disablement secrets remains inside the state machine, but
actual disablement and communication of the disablement secret is done by the caller.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-25 14:25:34 -07:00
Mihai Parparita
7a5cf39d0d ipn/ipnlocal: fix Taildrop not returning any sharing targets
The CapabilityFileSharingTarget capability added by eb32847d85
is meant to control the ability to share with nodes not owned by the
current user, not to restrict all sharing (the coordination server is
not currently populating the capability at all)

Fixes tailscale/corp#6669

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-25 12:47:20 -07:00
Tom DNetto
f81723ceac tailcfg: implement wire format for map request/response tka comms
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-25 11:02:58 -07:00
License Updater
9ccc52cd04 licenses: update tailscale{,d} licenses 2022-08-25 10:52:46 -07:00
Will Norris
5c7e960fa8 licenses: add GitHub Action to update license notices (#5433)
This will update a licenses/tailscale.md file with all of our go
dependencies and their respective licenses. Notices for other clients
will be triggered by similar actions in other repos.

Co-authored-by: Andrew Dunham <andrew@tailscale.com>
Signed-off-by: Will Norris <will@tailscale.com>
Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-25 10:28:10 -07:00
Mihai Parparita
1a093ef482 cmd/tsconnect: extract NPM package for reusing in other projects
`src/` is broken up into several subdirectories:
- `lib/` and `types`/ for shared code and type definitions (more code
  will be moved here)
- `app/` for the existing Preact-app
- `pkg/` for the new NPM package

A new `build-pkg` esbuild-based command is added to generate the files
for the NPM package. To generate type definitions (something that esbuild
does not do), we set up `dts-bundle-generator`.

Includes additional cleanups to the Wasm type definitions (we switch to
string literals for enums, since exported const enums are hard to use
via packages).

Also allows the control URL to be set a runtime (in addition to the
current build option), so that we don't have to rebuild the package
for dev vs. prod use.

Updates #5415

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-24 17:29:52 -07:00
Tom DNetto
472529af38 tka: optimize common case of processing updates built from head
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-24 11:35:55 -07:00
Tom DNetto
039def3b50 ipn,tailcfg: transmit NodeID in tka init RPCs
Needed to identify the node. A serverside-check the machine key (used
to authenticate the noise session) is that of the specified NodeID
ensures the authenticity of the request.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-24 11:35:55 -07:00
Tom DNetto
a78f8fa701 tka: support rotating node-keys in node-key signatures
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-24 10:41:01 -07:00
Will Norris
b3cc719add cmd/nginx-auth: allow use of shared nodes
When sharing nodes, the name of the sharee node is not exposed (instead
it is hardcoded to "device-of-shared-to-user"), which means that we
can't determine the tailnet of that node.  Don't immediately fail when
that happens, since it only matters if "Expected-Tailnet" is used.

Signed-off-by: Will Norris <will@tailscale.com>
2022-08-24 09:41:56 -07:00
Joe Tsai
3fc8683585 util/deephash: expand fast-path capabilities (#5404)
Add support for maps and interfaces to the fast path.
Add cycle-detection to the pointer handling logic.
This logic is mostly copied from the slow path.

A future commit will delete the slow path once
the fast path never falls back to the slow path.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                18.5µs ± 1%    14.9µs ± 2%  -19.52%  (p=0.000 n=10+10)
	HashPacketFilter-24    2.54µs ± 1%    2.60µs ± 1%   +2.19%  (p=0.000 n=10+10)
	HashMapAcyclic-24      31.6µs ± 1%    30.5µs ± 1%   -3.42%  (p=0.000 n=9+8)
	TailcfgNode-24         1.44µs ± 2%    1.43µs ± 1%     ~     (p=0.171 n=10+10)
	HashArray-24            324ns ± 1%     324ns ± 2%     ~     (p=0.425 n=9+9)

The additional cycle detection logic doesn't incur much slow down
since it only activates if a type is recursive, which does not apply
for any of the types that we care about.

There is a notable performance boost since we switch from the fath path
to the slow path less often. Most notably, a struct with a field that
could not be handled by the fast path would previously cause
the entire struct to go through the slow path.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-24 01:31:01 -07:00
Mihai Parparita
78b90c3685 cmd/tsconnect: stop writing build artifacts into src/
We can't write to src/ when tsconnect is used a dependency in another
repo (see also b763a12331). We therefore
need to switch from writing to src/ to using esbuild plugins to handle
the requests for wasm_exec.js (the Go JS runtime for Wasm) and the
Wasm build of the Go module.

This has the benefit of allowing Go/Wasm changes to be picked up without
restarting the server when in dev mode (Go compilation is fast enough
that we can do this on every request, CSS compilation continues to be
the long pole).

Fixes #5382

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-23 15:44:59 -07:00
Tom DNetto
facafd8819 client,cmd/tailscale,ipn,tka,types: implement tka initialization flow
This PR implements the client-side of initializing network-lock with the
Coordination server.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-22 11:35:16 -07:00
Tom DNetto
18edd79421 control/controlclient,tailcfg: [capver 40] create KeySignature field in tailcfg.Node
We calve out a space to put the node-key signature (used on tailnets where network lock is enabled).

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-22 11:25:41 -07:00
Kris Brandow
5d559141d5 wgengine/magicsock: remove mention of Start
The Start method was removed in 4c27e2fa22, but the comment on NewConn
still mentioned it doesn't do anything until this method is called.

Signed-off-by: Kris Brandow <kris.brandow@gmail.com>
2022-08-22 11:26:41 -04:00
Kamal Nasser
f983962fc6 fix typo in incomplete default routes error message
Signed-off-by: Kamal Nasser <hello@kamal.io>
2022-08-20 14:02:17 -07:00
Aaron Klotz
b997304bf6 tailcfg: add CapabilityDataPlaneAuditLogs.
We're going to want to enable audit logging on a per-Tailnet basis. When this
happens, we want control to inform the Tailnet's clients of this capability.

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-08-19 14:21:17 -06:00
Maisem Ali
9197dd14cc net/dns: [win] add MagicDNS entries to etc/hosts
This works around the 2.3s delay in short name lookups when SNR is
enabled.
C:\Windows\System32\drivers\etc\hosts file. We only add known hosts that
match the search domains, and we populate the list in order of
Search Domains so that our matching algorithm mimics what Windows would
otherwise do itself if SNR was off.

Updates #1659

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-19 12:38:11 -05:00
Denton Gentry
3c8d257b3e cmd/tailscale: set /dev/net perms in configure-host
Several customers have had issues due to the permissions
on /dev/net. Set permissions to 0755.

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

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-19 06:55:34 -07:00
Joe Tsai
d32700c7b2 util/deephash: specialize for netip.Addr and drop AppendTo support (#5402)
There are 5 types that we care about that implement AppendTo:

	key.DiscoPublic
	key.NodePublic
	netip.Prefix
	netipx.IPRange
	netip.Addr

The key types are thin wrappers around [32]byte and are memory hashable.
The netip.Prefix and netipx.IPRange types are thin wrappers over netip.Addr
and are hashable by default if netip.Addr is hashable.
The netip.Addr type is the only one with a complex structure where
the default behavior of deephash does not hash it correctly due to the presence
of the intern.Value type.

Drop support for AppendTo and instead add specialized hashing for netip.Addr
that would be semantically equivalent to == on the netip.Addr values.

The AppendTo support was already broken prior to this change.
It was fully removed (intentionally or not) in #4870.
It was partially restored in #4858 for the fast path,
but still broken in the slow path.
Just drop support for it altogether.

This does mean we lack any ability for types to self-hash themselves.
In the future we can add support for types that implement:

	interface { DeepHash() Sum }

Test and fuzz cases were added for the relevant types that
used to rely on the AppendTo method.
FuzzAddr has been executed on 1 billion samples without issues.

Signed-off-by: Joe Tsai joetsai@digital-static.net
2022-08-18 22:54:56 -07:00
Brad Fitzpatrick
0de66386d4 cmd/viewer: add flag to support Clone generation without Views
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-17 15:15:27 -07:00
Kris Brandow
9ae1161e85 net/dnscache: fix v4addrs to return only v4 addrs
Update the v4addrs function to filter out IPv6 addresses.

Fixes regression from 8725b14056.

Signed-off-by: Kris Brandow <kris.brandow@gmail.com>
2022-08-17 14:19:37 -04:00
Joe Tsai
03f7e4e577 util/hashx: move from sha256x (#5388) 2022-08-16 13:15:33 -07:00
Joe Tsai
f061d20c9d util/sha256x: rename Hash as Block512 (#5351)
Rename Hash as Block512 to indicate that this is a general-purpose
hash.Hash for any algorithm that operates on 512-bit block sizes.

While we rename the package as hashx in this commit,
a subsequent commit will move the sha256x package to hashx.
This is done separately to avoid confusing git.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-16 09:49:48 -07:00
Joe Tsai
44d62b65d0 util/deephash: move typeIsRecursive and canMemHash to types.go (#5386)
Also, rename canMemHash to typeIsMemHashable to be consistent.
There are zero changes to the semantics.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-16 09:31:19 -07:00
Joe Tsai
d53eb6fa11 util/deephash: simplify typeIsRecursive (#5385)
Any type that is memory hashable must not be recursive since
there are definitely no pointers involved to make a cycle.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 23:29:06 -07:00
Joe Tsai
23ec3c104a util/deephash: remove unused stack slice in typeIsRecursive (#5363)
No operation ever reads from this variable.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 22:36:08 -07:00
Joe Tsai
c200229f9e util/deephash: simplify canMemHash (#5384)
Put the t.Size() == 0 check first since this is applicable in all cases.
Drop the last struct field conditional since this is covered by the
sumFieldSize check at the end.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 22:22:18 -07:00
Brad Fitzpatrick
766ea96adf cmd/tailscaled: enable hybrid netstack mode on openbsd too
Apparently OpenBSD can forward packets with manual configuration,

https://github.com/tailscale/tailscale/issues/2498#issuecomment-1114216999

But this makes it work by default. People doing things by hand can
set TS_DEBUG_WRAP_NETSTACK=0 in the environment.

Change-Id: Iee5f32252f83af2baa0ebbe3f20ce9fec5f29e96
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-15 14:48:15 -07:00
Juan Font
ffc67806ef tailcfg: bump capver for clients to talk Noise over any HTTPS port [capver 39]
Signed-off-by: Juan Font Alonso <juanfontalonso@gmail.com>
2022-08-15 12:18:14 -07:00
Joe Tsai
32a1a3d1c0 util/deephash: avoid variadic argument for Update (#5372)
Hashing []any is slow since hashing of interfaces is slow.
Hashing of interfaces is slow since we pessimistically assume
that cycles can occur through them and start cycle tracking.

Drop the variadic signature of Update and fix callers to pass in
an anonymous struct so that we are hashing concrete types
near the root of the value tree.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-15 11:22:28 -07:00
Denton Gentry
1c0286e98a scripts/installer.sh: add -y for unattended install
Fixes https://github.com/tailscale/tailscale/issues/5377

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-15 10:28:55 -07:00
Kris Brandow
8f38afbf8e net/stun: convert to use net/netip.AddrPort
Convert ParseResponse and Response to use netip.AddrPort instead of
net.IP and separate port.

Fixes #5281

Signed-off-by: Kris Brandow <kris.brandow@gmail.com>
2022-08-15 12:46:01 -04:00
Maisem Ali
c3270af52b Makefile: add target for wasm and make it part of check
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-15 09:43:35 -07:00
Tom DNetto
06eac9bbff tka: Use strict decoding settings, implement Unserialize()
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-15 09:20:53 -07:00
Maisem Ali
dbcc34981a cmd/tailscale/cli: fix build break
Accidental break from 64d482ff48.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-15 07:31:03 -07:00
Maisem Ali
d4916a8be3 .github/workflows: cancel previous CI runs on PR update
Currently scheduled runs are not canceled when we update PRs, those
results are unused and the resources are just wasted. Instead of doing
that, this PR makes it so that when a PR is updated (force-pushed or new
commit added) the previous runs are canceled.

https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-a-fallback-value

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-15 07:27:24 -07:00
Juan Font Alonso
64d482ff48 Allow any port for HTTPS when using Noise over TLS
Signed-off-by: Juan Font Alonso <juanfontalonso@gmail.com>
2022-08-15 06:43:43 -07:00
Maisem Ali
25865f81ee net/dns: disable NetBIOS on Tailscale interfaces
Like LLMNR, NetBIOS also adds resolution delays and we don't support it
anyway so just disable it on the interface.

Updates #1659

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-14 22:55:08 -07:00
Maisem Ali
545639ee44 util/winutil: consolidate interface specific registry keys
Code movement to allow reuse in a follow up PR.

Updates #1659

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-14 22:34:59 -07:00
Maisem Ali
23f37b05a3 atomicfile: update docs to clarify behavior of argument
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-13 21:31:40 -07:00
Maisem Ali
1cff719015 net/dns: [win] respond with SERVFAIL queries when no resolvers
Currently we forward unmatched queries to the default resolver on
Windows. This results in duplicate queries being issued to the same
resolver which is just wasted.

Updates #1659

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-12 17:09:30 -07:00
Joe Tsai
548fa63e49 util/deephash: use binary encoding of time.Time (#5352)
Formatting a time.Time as RFC3339 is slow.
See https://go.dev/issue/54093

Now that we have efficient hashing of fixed-width integers,
just hash the time.Time as a binary value.

Performance:

	Hash-24                19.0µs ± 1%    18.6µs ± 1%   -2.03%  (p=0.000 n=10+9)
	TailcfgNode-24         1.79µs ± 1%    1.40µs ± 1%  -21.74%  (p=0.000 n=10+9)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-12 14:42:51 -07:00
Brad Fitzpatrick
0476c8ebc6 .github/workflows: delete flaky windows-race.yml
It flakes more often than it runs. It provides no value and builds
failure blindness, making people get used to submitting on red.

Bye.

Change-Id: If5491c70737b4c9851c103733b1855af2a90a9e9
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-12 13:20:00 -07:00
Joe Tsai
1f7479466e util/deephash: use sha256x (#5339)
Switch deephash to use sha256x.Hash.

We add sha256x.HashString to efficiently hash a string.
It uses unsafe under the hood to convert a string to a []byte.
We also modify sha256x.Hash to export the underlying hash.Hash
for testing purposes so that we can intercept all hash.Hash calls.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                19.8µs ± 1%    19.2µs ± 1%  -3.01%  (p=0.000 n=10+10)
	HashPacketFilter-24    2.61µs ± 0%    2.53µs ± 1%  -3.01%  (p=0.000 n=8+10)
	HashMapAcyclic-24      31.3µs ± 1%    29.8µs ± 0%  -4.80%  (p=0.000 n=10+9)
	TailcfgNode-24         1.83µs ± 1%    1.82µs ± 2%    ~     (p=0.305 n=10+10)
	HashArray-24            344ns ± 2%     323ns ± 1%  -6.02%  (p=0.000 n=9+10)

The performance gains is not as dramatic as sha256x over sha256 due to:
1. most of the hashing already occurring through the direct memory hashing logic, and
2. what does not go through direct memory hashing is slowed down by reflect.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-11 17:44:09 -07:00
Andrew Dunham
d942a2ff56 net/dnscache: try IPv6 addresses first (#5349)
Signed-off-by: Andrew Dunham <andrew@tailscale.com>

Signed-off-by: Andrew Dunham <andrew@tailscale.com>
2022-08-11 19:00:39 -04:00
Brad Fitzpatrick
90555c5cb2 tailcfg, control/controlclient: add PingRequest.URLIsNoise [capver 38]
Change-Id: I19bb63b6d99e96b2f9fd2c440afcc31d38137ded
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-11 09:41:06 -07:00
Brad Fitzpatrick
b33c337baa tailcfg: actually bump capver to 37, add test
I documented capver 37 in 4ee64681a but forgot to bump the actual
constant. I've done this previously too, so add a test to prevent
it from happening again.

Change-Id: I6f7659db1243d30672121a384beb386d9f9f5b98
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-11 09:28:54 -07:00
Joe Tsai
77a92f326d util/deephash: avoid using sync.Pool for reflect.MapIter (#5333)
In Go 1.19, the reflect.Value.MapRange method uses "function outlining"
so that the allocation of reflect.MapIter is inlinable by the caller.
If the iterator doesn't escape the caller, it can be stack allocated.
See https://go.dev/cl/400675

Performance:

	name               old time/op    new time/op    delta
	HashMapAcyclic-24    31.9µs ± 2%    32.1µs ± 1%   ~     (p=0.075 n=10+10)

	name               old alloc/op   new alloc/op   delta
	HashMapAcyclic-24     0.00B          0.00B        ~     (all equal)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-11 00:33:40 -07:00
Denton Gentry
5d731ca13f installer.sh: add manjaro-arm & EndeavourOS.
Fixes https://github.com/tailscale/tailscale/issues/5192
Fixes https://github.com/tailscale/tailscale/issues/5284

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-08-10 20:10:48 -07:00
Joe Tsai
1c3c6b5382 util/sha256x: make Hash.Sum non-escaping (#5338)
Since Hash is a concrete type, we can make it such that
Sum never escapes the input.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-10 17:31:44 -07:00
Joe Tsai
76b0e578c5 util/sha256x: new package (#5337)
The hash.Hash provided by sha256.New is much more efficient
if we always provide it with data a multiple of the block size.
This avoids double-copying of data into the internal block
of sha256.digest.x. Effectively, we are managing a block ourselves
to ensure we only ever call hash.Hash.Write with full blocks.

Performance:

	name    old time/op    new time/op    delta
	Hash    33.5µs ± 1%    20.6µs ± 1%  -38.40%  (p=0.000 n=10+9)

The logic has gone through CPU-hours of fuzzing.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-10 15:49:36 -07:00
Brad Fitzpatrick
090033ede5 cmd/derper: fix data race & server panic in manual cert mode
(Thanks for debugging, Roland!)

Fixes #4082

Change-Id: I400a64001c3c58899bb570b759b08e745abc0be1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-10 15:14:09 -07:00
dependabot[bot]
9996d94b3c build(deps): bump github.com/u-root/u-root from 0.8.0 to 0.9.0
Bumps [github.com/u-root/u-root](https://github.com/u-root/u-root) from 0.8.0 to 0.9.0.
- [Release notes](https://github.com/u-root/u-root/releases)
- [Changelog](https://github.com/u-root/u-root/blob/main/RELEASES)
- [Commits](https://github.com/u-root/u-root/compare/v0.8.0...v0.9.0)

---
updated-dependencies:
- dependency-name: github.com/u-root/u-root
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-10 09:52:57 -07:00
Brad Fitzpatrick
c8dd39fcbc .github/workflows: use a matrix for staticcheck GOOS/GOARCHes
Change-Id: Ic4eda169729117afc4031bbefbe265195b38c19b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-10 09:48:44 -07:00
Joe Tsai
539c5e44c5 util/deephash: always keep values addressable (#5328)
The logic of deephash is both simpler and easier to reason about
if values are always addressable.

In Go, the composite kinds are slices, arrays, maps, structs,
interfaces, pointers, channels, and functions,
where we define "composite" as a Go value that encapsulates
some other Go value (e.g., a map is a collection of key-value entries).

In the cases of pointers and slices, the sub-values are always addressable.

In the cases of arrays and structs, the sub-values are always addressable
if and only if the parent value is addressable.

In the case of maps and interfaces, the sub-values are never addressable.
To make them addressable, we need to copy them onto the heap.

For the purposes of deephash, we do not care about channels and functions.

For all non-composite kinds (e.g., strings and ints), they are only addressable
if obtained from one of the composite kinds that produce addressable values
(i.e., pointers, slices, addressable arrays, and addressable structs).
A non-addressible, non-composite kind can be made addressable by
allocating it on the heap, obtaining a pointer to it, and dereferencing it.

Thus, if we can ensure that values are addressable at the entry points,
and shallow copy sub-values whenever we encounter an interface or map,
then we can ensure that all values are always addressable and
assume such property throughout all the logic.

Performance:

	name                 old time/op    new time/op    delta
	Hash-24                21.5µs ± 1%    19.7µs ± 1%  -8.29%  (p=0.000 n=9+9)
	HashPacketFilter-24    2.61µs ± 1%    2.62µs ± 0%  +0.29%  (p=0.037 n=10+9)
	HashMapAcyclic-24      30.8µs ± 1%    30.9µs ± 1%    ~     (p=0.400 n=9+10)
	TailcfgNode-24         1.84µs ± 1%    1.84µs ± 2%    ~     (p=0.928 n=10+10)
	HashArray-24            324ns ± 2%     332ns ± 2%  +2.45%  (p=0.000 n=10+10)

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-09 22:00:02 -07:00
Brad Fitzpatrick
4ee64681ad tailcfg, control/controlclient: make Debug settings sticky in a map session [capver 37]
Fixes #4843

Change-Id: I3accfd91be474ac745cb47f5d6e866c37d5c5d2d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-09 14:36:09 -07:00
Brad Fitzpatrick
8e821d7aa8 types/opt: support an explicit "unset" value for Bool
Updates #4843

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-09 13:34:56 -07:00
Maisem Ali
3bb57504af net/dns/resolver: add comments clarifying nil error returns
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-09 13:32:11 -07:00
Maisem Ali
4497bb0b81 net/dns/resolver: return SERVFAIL when no upstream resolvers set
Otherwise we just keep looping over the same thing again and again.

```
dns udp query: upstream nameservers not set
dns udp query: upstream nameservers not set
dns udp query: upstream nameservers not set
```

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-09 13:28:03 -07:00
Charlotte Brandhorst-Satzkorn
0f12ead567 tsconnect: pass in authkey in dev mode (#5320)
This change allows for an auth key to be specified as a url query param
for use in development mode. If an auth key is specified and valid, it
will authorize the client for use immediately.

Updates #5144

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2022-08-09 13:07:01 -07:00
Mihai Parparita
ab159f748b cmd/tsconnect: switch UI to Preact
Reduces the amount of boilerplate to render the UI and makes it easier to
respond to state changes (e.g. machine getting authorized, netmap changing,
etc.)

Preact adds ~13K to our bundle size (5K after Brotli) thus is a neglibible
size contribution. We mitigate the delay in rendering the UI by having a static
placeholder in the HTML.

Required bumping the esbuild version to pick up evanw/esbuild#2349, which
makes it easier to support Preact's JSX code generation.

Fixes #5137
Fixes #5273

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-08 21:54:06 -07:00
David Crawshaw
15b8665787 tka: stable text representation of AUMHash
This makes debugging easier, you can pass an AUMHash to a printf and get
a string that is easy to debug.

Also rearrange how directories/files work in the FS store: use the first
two characters of the string representation as the prefix directory, and
use the entire AUMHash string as the file name. This is again to aid
debugging: you can `ls` a directory and line up what prints out easily
with what you get from a printf in debug code.

Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2022-08-08 11:10:56 -07:00
Maisem Ali
40ec8617ac util/must: rename Do->Get, add Do
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-06 09:30:10 -07:00
Brad Fitzpatrick
ec9d13bce5 hostinfo, net/netcheck: use CutPrefix
Updates #5309

Change-Id: I37e594cfd245784bf810c493de68a66d3ff20677
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-05 15:17:44 -07:00
Brad Fitzpatrick
01e8ef7293 util/strs: add new package for string utility funcs
Updates #5309

Change-Id: I677cc6e01050b6e10d8d6907d961b11c7a787a05
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-05 15:06:08 -07:00
Joe Tsai
622d80c007 util/must: new package (#5307)
The Do function assists in calling functions that must succeed.
It only interacts well with functions that return (T, err).
Signatures with more return arguments are not supported.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-05 12:20:50 -07:00
Maisem Ali
486cc9393c ipn/ipnlocal: fix log about local IPs when using exit nodes
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-05 11:45:22 -07:00
David Crawshaw
93324cc7b3 cmd/derper: add depaware.txt
Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
2022-08-05 11:38:10 -07:00
Brad Fitzpatrick
18109c63b0 net/socks5: use new Go 1.19 binary.AppendByteOrder.AppendUintX
Updates #4872

Change-Id: I43db63d2ba237324bc1cd87d4261197f14cb1088
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-05 08:30:48 -07:00
Joe Tsai
b1fff4499f tsnet: cleanup resources upon start failure (#5301)
In a partially initialized state, we should cleanup
all prior resources when an error occurs.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-04 16:20:48 -07:00
Andrew Dunham
f0d6f173c9 net/netcheck: try ICMP if UDP is blocked (#5056)
Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
2022-08-04 17:10:13 -04:00
Joe Tsai
ddebd30917 tsnet: fix closing of filch buffer (#5299)
It should be safe to initialize multiple Server instances
without any resource leaks what-so-ever.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-04 13:22:56 -07:00
Tom DNetto
f50043f6cb tka,types/key: remove dependency for tailcfg & types/ packages on tka
Following the pattern elsewhere, we create a new tka-specific types package for the types
that need to couple between the serialized structure types, and tka.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-04 12:51:58 -07:00
Maisem Ali
a9f6cd41fd all: use syncs.AtomicValue
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-04 11:52:16 -07:00
Maisem Ali
b75f81ec00 syncs: add generic AtomicValue
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-04 11:52:16 -07:00
Charlotte Brandhorst-Satzkorn
5055e00cf1 tsconnect: add flag to specify control server (#5294)
To improve the local development experience, this change allows a
control url to be passed in with the `--dev-control=` flag.

If the flag is passed in when not specifying dev, an error is returned.

If no flag is passed, the default remains the Tailscale controlled
control server set by `ipn.DefaultControlURL`.

Co-authored-by: Maisem Ali <maisem@tailscale.com>
Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
2022-08-04 10:37:19 -07:00
Mihai Parparita
f371a1afd9 cmd/tsconnect: make logtail uploading work
Initialize logtail and provide an uploader that works in the
browser (we make a no-cors cross-origin request to avoid having to
open up the logcatcher servers to CORS).

Fixes #5147

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-04 09:10:20 -07:00
Brad Fitzpatrick
4950fe60bd syncs, all: move to using Go's new atomic types instead of ours
Fixes #5185

Change-Id: I850dd532559af78c3895e2924f8237ccc328449d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-04 07:47:59 -07:00
Maisem Ali
9bb5a038e5 all: use atomic.Pointer
Also add some missing docs.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-03 21:42:52 -07:00
Brad Fitzpatrick
5381437664 logtail, net/portmapper, wgengine/magicsock: use fmt.Appendf
Fixes #5206

Change-Id: I490bb92e774ce7c044040537e2cd864fcf1dbe5a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-03 21:35:51 -07:00
Mihai Parparita
4aa88bc2c0 cmd/tsconnect,util/precompress: move precompression to its own package
We have very similar code in corp, moving it to util/precompress allows
it to be reused.

Updates #5133

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-03 17:44:57 -07:00
Mihai Parparita
dfcef3382e cmd/tsconnect: add README with instructions
Outlines basic development, build and serving workflows.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-03 17:17:42 -07:00
Brad Fitzpatrick
9e5954c598 control/controlclient: fix crash in tests elsewhere when GetNLPublicKey is nil
4001d0bf25 caused tests in another repo to fail with a crash, calling
a nil func. This might not be the right fix, but fixes the build.

Change-Id: I67263f883c298f307abdd22bc2a30b3393f062e6
Co-authored-by: Maisem Ali <maisem@tailscale.com>
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-03 16:39:06 -07:00
Tom DNetto
8cfd775885 tka,types/key: implement direct node-key signatures
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-03 15:42:27 -07:00
Tom DNetto
c13fab2a67 tka: add attack-scenario unit tests, defensive checks, resolve TODOs
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-03 15:42:09 -07:00
Tom DNetto
4001d0bf25 assorted: plumb tka initialization & network-lock key into tailscaled
- A network-lock key is generated if it doesn't already exist, and stored in the StateStore. The public component is communicated to control during registration.
 - If TKA state exists on the filesystem, a tailnet key authority is initialized (but nothing is done with it for now).

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-03 14:51:47 -07:00
Tom DNetto
8d45d7e312 types/key: make NLPublic complement to NLPrivate
Forgot that I would need that in control. Oops.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-08-03 14:51:47 -07:00
Maisem Ali
be5eadbecc tsnet: log out ephemeral nodes on Close()
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-03 14:27:04 -07:00
Maisem Ali
95d43c54bf cmd/{cloner,viewer}: add support for map values with pointers
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-03 13:02:17 -07:00
Maisem Ali
26f103473c cmd/viewer: add support for map of structs without pointers
This adds support for fields like `map[string]netaddr.IPPrefix`.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-03 13:02:17 -07:00
Mihai Parparita
adc5ffea99 cmd/tsconnect: make PeerAPI work
JS -> native nodes worked already, tested by exposing a fetch() method
to JS (it's Promise-based to be consistent with the native fetch() API).

Native nodes -> JS almost worked, we just needed to set the LocalBackend
on the userspace netstack.

Fixes #5130

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-03 10:39:47 -07:00
Brad Fitzpatrick
5f6abcfa6f all: migrate code from netaddr.FromStdAddr to Go 1.18
With caveat https://github.com/golang/go/issues/53607#issuecomment-1203466984
that then requires a new wrapper. But a simpler one at least.

Updates #5162

Change-Id: I0a5265065bfcd7f21e8dd65b2bd74cae90d76090
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 22:25:07 -07:00
Brad Fitzpatrick
7c7e23d87a control/controlclient, tailcfg: add 6 more patchable Node fields [capver 36]
Change-Id: Iae997a9a98a5dd841bc41fa91227d5a7dd476a25
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 21:14:00 -07:00
Mihai Parparita
52d769d35c cmd/tsconnect: prefetch main.wasm when serving
Avoids waterfalling of requests from the file (its load is triggered
from JavaScript).

Also has other cleanups to index.html, adding a <title> and moving the
<script> to being loaded sooner (but still not delaying page rendering
by using the defer attribute).

Fixes #5141
Fixes #5135

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-02 16:52:06 -07:00
Mihai Parparita
f04bc31820 cmd/tsconnect: add -fast-compression option
Changes Gzip and Brotli to optimize for speed instead of size. This
signficantly speeds up Brotli, and is useful when iterating locally
or running the build during a CI job (where we just care that it
can successfully build).

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-02 16:52:06 -07:00
Mihai Parparita
9a2171e4ea cmd/tsconnect: make terminal resizable
Makes the terminal container DOM node as large as the window (except for
the header) via flexbox. The xterm.js terminal is then sized to fit via
xterm-addon-fit. Once we have a computed rows/columns size, and we can
tell the SSH session of the computed size.

Required introducing an IPNSSHSession type to allow the JS to control
the SSH session once opened. That alse allows us to programatically
close it, which we do when the user closes the window with the session
still active.

I initially wanted to open the terminal in a new window instead (so that
it could be resizable independently of the main window), but xterm.js
does not appear to work well in that mode (possibly because it adds an
IntersectionObserver to pause rendering when the window is not visible,
and it ends up doing that when the parent window is hidden -- see
xtermjs/xterm.js@87dca56dee)

Fixes #5150

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-02 15:30:40 -07:00
Brad Fitzpatrick
8725b14056 all: migrate more code code to net/netip directly
Instead of going through the tailscale.com/net/netaddr transitional
wrappers.

Updates #5162

Change-Id: I3dafd1c2effa1a6caa9b7151ecf6edd1a3fda3dd
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 13:59:57 -07:00
Maisem Ali
eb32847d85 tailcfg: add CapabilityFileSharingTarget to identify FileTargets
This adds the inverse to CapabilityFileSharingSend so that senders can
identify who they can Taildrop to.

Updates #2101

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-02 13:52:10 -07:00
Mihai Parparita
9dfcdbf478 .github/workflows: put back CLI in cross-wasm GitHub action
Since we're keeping the JS support in the CLI (see https://github.com/tailscale/tailscale/pull/5265#issuecomment-1203078243),
make sure it keeps building.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-02 12:39:35 -07:00
Mihai Parparita
e846481731 cmd/tailscale/cli: use printf and outln consistently
Fix some fmt.Println and fmt.Printf calls that crept in since
5df7ac70d6.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-08-02 12:37:45 -07:00
Maisem Ali
02a765743e ssh/tailssh: fix deadlock in expandDelegateURL
Also rename it to expandDelegateURLLocked, previously it was trying
to acquire the mutex while holding the mutex.

Fixes #5235

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-02 12:25:09 -07:00
Brad Fitzpatrick
e1309e1323 all: require Go 1.19
Updates #5210

Change-Id: I2e950b4776636b4ea89b6566b60e4a87596a3a43
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 11:49:01 -07:00
Brad Fitzpatrick
fb82299f5a wgengine/magicsock: avoid RebindingUDPConn mutex in common read/write case
Change-Id: I209fac567326f2e926bace2582dbc67a8bc94c78
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 11:27:10 -07:00
Brad Fitzpatrick
116f55ff66 all: gofmt for Go 1.19
Updates #5210

Change-Id: Ib02cd5e43d0a8db60c1f09755a8ac7b140b670be
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-02 10:08:05 -07:00
Maisem Ali
a029989aff types/dnstype: use viewer instead of cloner
This was missed when I did the initial viewer work.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-08-02 09:58:53 -07:00
Joe Tsai
57275a4912 tsweb: add HTTPError.Header (#5251)
The Header field allows the server to specify specific headers to set.
Example use case: server returns 429 with the "Retry-After" header set.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 22:57:04 -07:00
Joe Tsai
a794963e2f tsweb: mark AccessLogRecord fields as omitempty (#5250)
If the field is the zero value, then avoid serializing the field.
This reduces verbosity in server logs.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 21:17:38 -07:00
Brad Fitzpatrick
5d0e3d379c go.mod: bump gvisor
For https://github.com/google/gvisor/pull/7850 to quiet macOS warnings.

Updates #5240

Change-Id: Iaa7abab20485c8ff40e5c9b16013aef4fd297a64
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-08-01 16:48:27 -07:00
Joe Tsai
b905db7a56 cmd/derper: remove support for logtail logging (#5248)
There aren't really any useful logs produced by derper.

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2022-08-01 14:36:55 -07:00
Brad Fitzpatrick
357fd85ecf go.toolchain.rev: bump tailscale.go1.19 commit
for mutex slow path metrics: https://github.com/tailscale/go/pull/33

(a maybe temporary experiment)

Change-Id: Idba1ef866e2e1764728bdd869c25becccc1051b0
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-31 22:10:31 -07:00
Mihai Parparita
c06758c83b cmd/tsconnect: allow SSH username to be specified
Redoes the UI to be a form, with a username field and a host drop-down.

Fixes #5139

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-29 15:54:06 -07:00
Tom DNetto
47f91dd732 cmd/tailscale{,d}: update depaware
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-07-29 12:16:32 -07:00
Tom DNetto
023d4e2216 tka,types/key: implement NLPrivate glue for tailnet key authority keys
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-07-29 12:16:32 -07:00
Tom DNetto
7a74466998 shell.nix: update Go toolchain to 1.19
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-07-29 11:25:47 -07:00
Tom DNetto
44a9b0170b tka: support processing non-primary forks, scenario-driven tests
Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-07-29 11:18:06 -07:00
Maisem Ali
8fd5d3eaf3 tstest: do not error if the there are fewer goroutines than at start
This fixes test failures like:
```
    resource.go:34: goroutine count: expected 3, got 2
```

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-29 09:53:01 -07:00
Tom DNetto
5e61d52f91 tka: implement API surface for generating updates
Based on the builder pattern.

Signed-off-by: Tom DNetto <tom@tailscale.com>
2022-07-29 09:36:32 -07:00
Brad Fitzpatrick
acc3b7f259 go.mod: bump inet.af/wf, tidy
This removes inet.af/netaddr from go.{mod,sum}.

Updates #5162

Change-Id: I7121e9fbb96d036cf188c51f0b53731570252d69
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-28 14:50:50 -07:00
Brad Fitzpatrick
f541e00db2 go.toolchain.rev: bump for VERSION file
Updates #5210

Change-Id: Ib6db8b010a6a9369a3eda8a86a49e538e376aff6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-28 14:29:15 -07:00
Brad Fitzpatrick
eae003e56f ipn/ipnlocal: blend existing host SSH keys + newly-generated types as needed
If the host only has RSA, use its RSA + generate ecdsa + ed25519, etc.

Perhaps fixes https://twitter.com/colek42c/status/1550554439299244032 and
something else that was reported.

Change-Id: I88dc475c8e3d95b6f25288ff7664b8e72655fd16
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-28 11:42:58 -07:00
Brad Fitzpatrick
e5176f572e go.toolchain.rev: switch to Go 1.19rc2+
Switch to Go 1.19rc2 in prep for the Go 1.19 GA release on Tuesday.

(We won't be using any Go 1.19 features until then.)

Updates #5210

Change-Id: I94fa0ae8f5645fb7579429668f3970c18d1796d8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-28 11:28:21 -07:00
Brad Fitzpatrick
48e73e147a logtail,logpolicy: tweak minor cosmetic things
Just reading the code again in prep for some alloc reductions.

Change-Id: I065226ea794b7ec7144c2b15942d35131c9313a8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-27 21:13:46 -07:00
Mihai Parparita
ab60f28227 cmd/tsconnect: fix xterm CSS not being imported
@import rules need to come first, they are (silently) ignored otherwise.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-27 16:16:13 -07:00
Mihai Parparita
7c3f480767 cmd/tsconnect: lint during build step
Ensures that TypeScript checks pass before we deploy.

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-27 16:12:22 -07:00
James Tucker
d5fb852718 build_dist.sh: add --box and --extra-small flag to produce smaller and boxed binaries
- `--box` when ./cmd/tailscaled is built with this flag, it builds a
  "toybox" style binary that includes tailscale and tailscaled.
- `--extra-small` strip the output binary and omit some dependencies
  (currently AWS integration).

Signed-off-by: James Tucker <james@tailscale.com>
2022-07-27 16:08:52 -07:00
Mihai Parparita
a3d74c4548 cmd/tsconnect: add basic panic handling
The go wasm process exiting is a sign of an unhandled panic. Also
add a explicit recover() call in the notify callback, that's where most
logic bugs are likely to happen (and they may not be fatal).

Also fixes the one panic that was encountered (nill pointer dereference
when generating the JS view of the netmap).

Fixes #5132

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-27 15:42:58 -07:00
Aaron Klotz
4dbdb19c26 net/tshttpproxy: fix incorrect type in Windows implementation, switch to mkwinsyscall, fix memory leak
The definition of winHTTPProxyInfo was using the wrong type (uint16 vs uint32)
for its first field. I fixed that type.

Furthermore, any UTF16 strings returned in that structure must be explicitly
freed. I added code to do this.

Finally, since this is the second time I've seen type safety errors in this code,
I switched the native API calls over to use wrappers generated by mkwinsyscall.
I know that would not have helped prevent the previous two problems, but every
bit helps IMHO.

Updates https://github.com/tailscale/tailscale/issues/4811

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-07-27 16:33:57 -06:00
Aaron Klotz
446d03e108 scripts: update check_license_headers.sh to skip zsyscall_windows.go
Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-07-27 16:19:39 -06:00
Brad Fitzpatrick
97b8c4fa1b ipn/store/awsstore: add "ts_omit_aws" build tag to reduce binary size
Drops tailscaled from 23M to 21M.

Change-Id: I731c542d03113ac94abb695e3c8fcacbc5542712
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-27 14:19:33 -07:00
nyghtowl
e6e1976c3a net/dns: remove systemd-resolved ping
Ping only needed to ensure system awak otherwise utilizing resolvConf to set dns mode.

Signed-off-by: nyghtowl <warrick@tailscale.com>
2022-07-27 14:15:22 -07:00
Mihai Parparita
617a2ec7cc cmd/tsconnect: add Tailwind CSS support and switch to using it
Integrates Tailwind CSS as an esbuild plugin that invokes the CLI
to process the input. It takes ~400ms, so it seems like the easiest
option (vs running a separate process for dev mode).

Existing minimal look and feel is replicated with Tailwind classes,
mostly to prove that the entire system works, including unused
class removal.

Also fixes yarn warnings about package.json not having a license
(which were showing up when invoking any scripts).

Fixes #5136
Fixes #5129

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-27 14:05:45 -07:00
Mihai Parparita
389629258b cmd/tsconnect: switch to TypeScript
Continues to use esbuild for development mode and building. Also
includes a `yarn lint` script that uses tsc to do full type checking.

Fixes #5138

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-27 13:50:34 -07:00
Brad Fitzpatrick
0a6aa75a2d cmd/tailscaled: add opt-in support for linking CLI into daemon
Doesn't help much, though.

    $ go install --tags=ts_include_cli ./cmd/tailscaled/
    $ ls -lh ~/go/bin/tailscaled
    -rwxr-xr-x 2 bradfitz bradfitz 34M Jul 27 11:00 /home/bradfitz/go/bin/tailscaled
    $ go install --tags= ./cmd/tailscaled/
    $ ls -lh ~/go/bin/tailscaled
    -rwxr-xr-x 1 bradfitz bradfitz 23M Jul 27 11:00 /home/bradfitz/go/bin/tailscaled
    $ ls -lh ~/go/bin/tailscale
    -rwxr-xr-x 1 bradfitz bradfitz 13M Jul 25 21:30 /home/bradfitz/go/bin/tailscale

Fixes #2233

Change-Id: I46bae91bb38eb47a76251c1b5c1e9e455fc234b6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-27 11:15:52 -07:00
Brad Fitzpatrick
04cf46a762 util/deephash: fix unexported time.Time hashing
Updates tailscale/corp#6311

Change-Id: I33cd7e4040966261c2f2eb3d32f29936aeb7f632
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-27 09:28:23 -07:00
Maisem Ali
51c3d74095 types/views: add BenchmarkSliceIteration
```
goos: darwin
goarch: arm64
pkg: tailscale.com/types/views
BenchmarkSliceIteration/Len-10            340093              3212 ns/op               0 B/op          0 allocs/op
BenchmarkSliceIteration/Cached-Len-10     366727              3211 ns/op               0 B/op          0 allocs/op
BenchmarkSliceIteration/direct-10         361561              3290 ns/op               0 B/op          0 allocs/op
PASS
ok      tailscale.com/types/views       3.662s
```

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-26 21:25:26 -07:00
Maisem Ali
fa2fbaf3aa tailcfg: add views for SSHRule and SSHPrincipal
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-26 20:57:33 -07:00
Brad Fitzpatrick
7c671b0220 .github/workflows: add gofmt (goimports) check
Change-Id: Iceb3182827b9c65f28f0351e0e254abe4a95e4de
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 09:46:06 -07:00
Brad Fitzpatrick
dd3e91b678 go.mod: tidy, remove inet.af/netaddr
It was actually unused earlier, but I had a test program
in my git workdir, keeping go mod tidy from cleaning it.
(more CI needed, perhaps)

Updates #5162

Change-Id: I9047a9aaa6fde7736d6ef516dc3bb652d06fe921
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 22:08:20 -07:00
Brad Fitzpatrick
a12aad6b47 all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
    perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
    perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
    perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
    perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
    perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
    goimports -w .

Then delete some stuff from the net/netaddr shim package which is no
longer neeed.

Updates #5162

Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 21:53:49 -07:00
Brad Fitzpatrick
6a396731eb all: use various net/netip parse funcs directly
Mechanical change with perl+goimports.

Changed {Must,}Parse{IP,IPPrefix,IPPort} to their netip variants, then
goimports -d .

Finally, removed the net/netaddr wrappers, to prevent future use.

Updates #5162

Change-Id: I59c0e38b5fbca5a935d701645789cddf3d7863ad
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 21:12:28 -07:00
Brad Fitzpatrick
730ca4203c cmd/tsshd: add a package line to appease gofmt
Change-Id: I2fbbe983186169ddf1995d2f51c7b5a6164a0904
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 20:52:04 -07:00
Mihai Parparita
7e4883b261 .github/workflows: add tsconnect static build to wasm GitHub action
Technically not the same as the wasm cross-compilation, but it's
closely connected to it.

Also includes some fixes to tool/yasm to make it actually work on
non-ARM platforms.

Fixes #5134

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-25 18:02:22 -07:00
Brad Fitzpatrick
7eaf5e509f net/netaddr: start migrating to net/netip via new netaddr adapter package
Updates #5162

Change-Id: Id7bdec303b25471f69d542f8ce43805328d56c12
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-25 16:20:43 -07:00
James Tucker
7b1a91dfd3 tool/go: accept a marker file with no line terminator
Somewhere my local configuration or program versions are producing
marker files earlier in the process that lack a line terminator. This
doesn't need to cause an exit via set -e, we can just continue the
process. $extracted matches $REV anyway, so the process works.

Signed-off-by: James Tucker <james@tailscale.com>
2022-07-25 16:06:52 -07:00
Walter Poupore
df9f3edea3 docs/k8s: add prefix to (#5167)
Signed-off-by: Walter Poupore <walterp@tailscale.com>
2022-07-25 15:10:07 -07:00
Denton Gentry
7fd03ad4b4 logpolicy: put QNAP logs buffer in /tmp
Ongoing log writing keeps the spinning disks from hibernating.
Extends earlier implementation for Synology to also handle QNAP.

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-07-25 09:45:04 -07:00
Denton Gentry
f85bb60eba ipn/ipnlocal: prevent attempting to run SSH on QNAP for now
tailscaled runs as a non-root user, SSH is not immediately working.

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-07-25 09:45:04 -07:00
Xe Iaso
904723691b cmd/gitops-pusher: things i forgot to push last PR (#5128)
Signed-off-by: Xe <xe@tailscale.com>
2022-07-25 09:09:32 -04:00
Denton Gentry
4dd799ec43 hostinfo: determine QNAP QTS version
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-07-22 19:43:47 -07:00
Denton Gentry
d17849461c ipn/{ipnserver,ipnlocal}: support incoming Taildrop on QNAP
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-07-22 19:43:47 -07:00
Aaron Klotz
1cae618b03 net/dns: add Windows group policy notifications to the NRPT rule manager
As discussed in previous PRs, we can register for notifications when group
policies are updated and act accordingly.

This patch changes nrptRuleDatabase to receive notifications that group policy
has changed and automatically move our NRPT rules between the local and
group policy subkeys as needed.

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-07-22 14:24:39 -06:00
Melanie Warrick
f17873e0f4 net/dns: handle D-Bus restarts in resolved manager (#5026)
When dbus restarts it can cause the tailscaled to crash because the nil
signal was not handled in resolved.Fixing so the nil signal leads to a
connection reset and tailscaled stays connected to systemd when dbus restarted.

Fixes #4645

Co-authored-by: James Tucker <james@tailscale.com>
Signed-off-by: nyghtowl <warrick@tailscale.com>

Co-authored-by: James Tucker <james@tailscale.com>
2022-07-22 12:49:18 -07:00
Xe Iaso
898695e312 cmd/gitops-pusher: add etag cache file for the three version problem (#5124)
This allows gitops-pusher to detect external ACL changes. I'm not
sure what to call this problem, so I've been calling it the "three
version problem" in my notes. The basic problem is that at any given
time we only have two versions of the ACL file at any given point:
the version in CONTROL and the one in the git repo. In order to
check if there has been tampering of the ACL files in the admin
panel, we need to have a _third_ version to compare against.

In this case I am not storing the old ACL entirely (though that could
be a reasonable thing to add in the future), but only its sha256sum.
This allows us to detect if the shasum in control matches the shasum
we expect, and if that expectation fails, then we can react
accordingly.

This will require additional configuration in CI, but I'm sure that
can be done.

Signed-off-by: Xe <xe@tailscale.com>
2022-07-22 15:07:38 -04:00
Brad Fitzpatrick
2024008667 types/key: add MachinePrecomputedSharedKey.Open
Follow-up to cfdb862673

Updates tailscale/corp#1709

Change-Id: I7af931a2cb55f9006e1029381663ac21d1794242
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-22 12:05:29 -07:00
Mihai Parparita
be8a0859a9 cmd/tsconnect: pin yarn and node
Adds a tool/yarn helper script that uses specific versions of yarn and
node, downloading them if necessary.

Modeled after tool/go (and the yarn and node Redo scripts from the
corp repo).

Also allows the path to yarn to be overidden (in case the user does not
want to use this script) and always pipes yarn output (to make debugging
and viewing of process easier).

Signed-off-by: Mihai Parparita <mihai@tailscale.com>
2022-07-22 11:36:49 -07:00
Xe Iaso
92357a54ec cmd/gitops-pusher: fix minor bug with ACL tests (#5123)
Signed-off-by: Xe <xe@tailscale.com>
2022-07-22 13:53:42 -04:00
Brad Fitzpatrick
ba91f57ddd ipn/ipnlocal: ignore empty SSH host key files
Change-Id: I332b0d7d01386111d0af4adf98c96c04d3d12fbb
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-22 10:52:04 -07:00
Brad Fitzpatrick
227c6b2a53 ipn/ipnlocal: flesh out error on ssh host key parse error
Change-Id: Iedd2d3898befa536181036b9e9dea59bc777a440
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-22 08:09:59 -07:00
Xe Iaso
90ccba6730 cmd/gitops-pusher: port to use ffcli (#5113)
Signed-off-by: Xe <xe@tailscale.com>
2022-07-22 09:39:24 -04:00
Logan Saso
f7a36dfeb1 api.md: added missing quote to POST DNS Searchpaths request body example
Signed-off-by: Logan Saso <logansaso@gmail.com>
2022-07-21 17:44:51 -07:00
Maisem Ali
9514ed33d2 go.mod: bump gvisor.dev/gvisor
Pick up https://github.com/google/gvisor/pull/7787

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-21 16:41:18 -07:00
Maisem Ali
1d33157ab9 docs/k8s: use job control in run.sh
This has the benefit of propagating SIGINT to tailscaled, which in turn
can react to the event and logout in case of an ephemeral node.

Also fix missing run.sh in Dockerfile.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-21 15:43:40 -07:00
Maisem Ali
3e06b9ea7a ssh/tailssh: add "ssh" to conn logs
Fixes #5089

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-21 12:42:31 -07:00
Maisem Ali
480fd6c797 ssh/tailssh: handle not-authenticated-yet connections in matchRule
Also make more fields in conn.info thread safe, there was previously a
data race here.

Fixes #5110

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-07-21 12:42:31 -07:00
Xe Iaso
41e60dae80 cmd/gitops-pusher: use fmt.Println for errors (#5112)
Signed-off-by: Xe <xe@tailscale.com>
2022-07-21 13:02:14 -04:00
487 changed files with 20686 additions and 7205 deletions

17
.github/licenses.tmpl vendored Normal file
View File

@@ -0,0 +1,17 @@
# Tailscale CLI and daemon dependencies
The following open source dependencies are used to build the [tailscale][] and
[tailscaled][] commands. These are primarily used on Linux and BSD variants as
well as an [option for macOS][].
[tailscale]: https://pkg.go.dev/tailscale.com/cmd/tailscale
[tailscaled]: https://pkg.go.dev/tailscale.com/cmd/tailscaled
[option for macOS]: https://tailscale.com/kb/1065/macos-variants/
## Go Packages
Some packages may only be included on certain architectures or operating systems.
{{ range . }}
- [{{.Name}}](https://pkg.go.dev/{{.Name}}) ([{{.LicenseName}}]({{.LicenseURL}}))
{{- end }}

View File

@@ -1,8 +1,15 @@
name: CIFuzz
on: [pull_request]
on:
push:
branches: [ main, release-branch/* ]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
Fuzzing:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
steps:
- name: Build Fuzzers
id: build
@@ -15,7 +22,7 @@ jobs:
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'tailscale'
fuzz-seconds: 300
fuzz-seconds: 900
dry-run: false
language: go
- name: Upload Crash

View File

@@ -20,10 +20,14 @@ on:
schedule:
- cron: '31 14 * * 5'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
permissions:
actions: read
contents: read

View File

@@ -1,4 +1,4 @@
name: staticcheck
name: Android-Cross
on:
push:
@@ -8,51 +8,35 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code
uses: actions/checkout@v3
- name: Run go vet
run: go vet ./...
- name: Install staticcheck
run: "GOBIN=~/.local/bin go install honnef.co/go/tools/cmd/staticcheck"
- name: Print staticcheck version
run: "staticcheck -version"
- name: Run staticcheck (linux/amd64)
- name: Android smoke build
# Super minimal Android build that doesn't even use CGO and doesn't build everything that's needed
# and is only arm64. But it's a smoke build: it's not meant to catch everything. But it'll catch
# some Android breakages early.
# TODO(bradfitz): better; see https://github.com/tailscale/tailscale/issues/4482
env:
GOOS: linux
GOARCH: amd64
run: "staticcheck -- $(go list ./... | grep -v tempfork)"
- name: Run staticcheck (darwin/amd64)
env:
GOOS: darwin
GOARCH: amd64
run: "staticcheck -- $(go list ./... | grep -v tempfork)"
- name: Run staticcheck (windows/amd64)
env:
GOOS: windows
GOARCH: amd64
run: "staticcheck -- $(go list ./... | grep -v tempfork)"
- name: Run staticcheck (windows/386)
env:
GOOS: windows
GOARCH: "386"
run: "staticcheck -- $(go list ./... | grep -v tempfork)"
GOOS: android
GOARCH: arm64
run: go install ./net/netns ./ipn/ipnlocal ./wgengine/magicsock/ ./wgengine/ ./wgengine/router/ ./wgengine/netstack ./util/dnsname/ ./ipn/ ./net/interfaces ./wgengine/router/ ./tailcfg/ ./types/logger/ ./net/dns ./hostinfo ./version
- uses: k0kubun/action-slack@v2.0.0
with:

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: macOS build cmd
env:
GOOS: darwin

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: FreeBSD build cmd
env:
GOOS: freebsd

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: OpenBSD build cmd
env:
GOOS: openbsd

View File

@@ -8,28 +8,38 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Wasm client build
env:
GOOS: js
GOARCH: wasm
run: go build ./cmd/tsconnect/wasm
run: go build ./cmd/tsconnect/wasm ./cmd/tailscale/cli
- name: tsconnect static build
# Use our custom Go toolchain, we set build tags (to control binary size)
# that depend on it.
run: |
./tool/go run ./cmd/tsconnect --fast-compression build
./tool/go run ./cmd/tsconnect build-pkg
- uses: k0kubun/action-slack@v2.0.0
with:

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Windows build cmd
env:
GOOS: windows

View File

@@ -8,21 +8,25 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Check out code
uses: actions/checkout@v3
- name: depaware tailscaled
run: go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
- name: depaware tailscale
run: go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
- name: depaware
run: go run github.com/tailscale/depaware --check
tailscale.com/cmd/tailscaled
tailscale.com/cmd/tailscale
tailscale.com/cmd/derper

64
.github/workflows/go-licenses.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: go-licenses
on:
# run action when a change lands in the main branch which updates go.mod or
# our license template file. Also allow manual triggering.
push:
branches:
- main
paths:
- go.mod
- .github/licenses.tmpl
- .github/workflows/go-licenses.yml
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
tailscale:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
- name: Install go-licenses
run: |
go install github.com/google/go-licenses@v1.2.2-0.20220825154955-5eedde1c6584
- name: Run go-licenses
env:
# include all build tags to include platform-specific dependencies
GOFLAGS: "-tags=android,cgo,darwin,freebsd,ios,js,linux,openbsd,wasm,windows"
run: |
[ -d licenses ] || mkdir licenses
go-licenses report tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled > licenses/tailscale.md --template .github/licenses.tmpl
- name: Get access token
uses: tibdex/github-app-token@f717b5ecd4534d3c4df4ce9b5c1c2214f0f7cd06 # v1.6.0
id: generate-token
with:
app_id: ${{ secrets.LICENSING_APP_ID }}
installation_id: ${{ secrets.LICENSING_APP_INSTALLATION_ID }}
private_key: ${{ secrets.LICENSING_APP_PRIVATE_KEY }}
- name: Send pull request
uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d #v4.1.1
with:
token: ${{ steps.generate-token.outputs.token }}
author: License Updater <noreply@tailscale.com>
committer: License Updater <noreply@tailscale.com>
branch: licenses/cli
commit-message: "licenses: update tailscale{,d} licenses"
title: "licenses: update tailscale{,d} licenses"
body: Triggered by ${{ github.repository }}@${{ github.sha }}
signoff: true
delete-branch: true
team-reviewers: opensource-license-reviewers

View File

@@ -9,21 +9,25 @@ on:
branches:
- "*"
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
- name: check 'go generate' is clean
run: |
if [[ "${{github.ref}}" == release-branch/* ]]

35
.github/workflows/go_mod_tidy.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: go mod tidy
on:
push:
branches:
- main
pull_request:
branches:
- "*"
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
- name: check 'go mod tidy' is clean
run: |
go mod tidy
echo
echo
git diff --name-only --exit-code || (echo "Please run 'go mod tidy'."; exit 1)

View File

@@ -8,18 +8,22 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Check out code
uses: actions/checkout@v3
go-version-file: go.mod
- name: Run license checker
run: ./scripts/check_license_headers.sh .

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Basic build
run: go build ./cmd/...

View File

@@ -8,32 +8,36 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Basic build
run: go build ./cmd/...
- name: Build variants
run: |
go install --tags=ts_include_cli ./cmd/tailscaled
go install --tags=ts_omit_aws ./cmd/tailscaled
- name: Get QEMU
run: |
# The qemu in Ubuntu 20.04 (Focal) is too old; we need 5.x something
# to run Go binaries. 5.2.0 (Debian bullseye) empirically works, and
# use this PPA which brings in a modern qemu.
sudo add-apt-repository -y ppa:jacob/virtualisation
sudo apt-get -y update
sudo apt-get -y install qemu-user

View File

@@ -8,23 +8,26 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
runs-on: github-4vcpu-ubuntu-2204
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
go-version-file: go.mod
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v3
- name: Basic build
run: GOARCH=386 go build ./cmd/...

112
.github/workflows/static-analysis.yml vendored Normal file
View File

@@ -0,0 +1,112 @@
name: static-analysis
on:
push:
branches:
- main
pull_request:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
gofmt:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
- name: Run gofmt (goimports)
run: go run golang.org/x/tools/cmd/goimports -d --format-only .
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |
{
"attachments": [{
"text": "${{ job.status }}: ${{ github.workflow }} <https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks|${{ env.COMMIT_DATE }} #${{ env.COMMIT_NUMBER_OF_DAY }}> " +
"(<https://github.com/${{ github.repository }}/commit/${{ github.sha }}|" + "${{ github.sha }}".substring(0, 10) + ">) " +
"of ${{ github.repository }}@" + "${{ github.ref }}".split('/').reverse()[0] + " by ${{ github.event.head_commit.committer.name }}",
"color": "danger"
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure() && github.event_name == 'push'
vet:
runs-on: github-4vcpu-ubuntu-2204
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Check out code
uses: actions/checkout@v3
- name: Run go vet
run: go vet ./...
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |
{
"attachments": [{
"text": "${{ job.status }}: ${{ github.workflow }} <https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks|${{ env.COMMIT_DATE }} #${{ env.COMMIT_NUMBER_OF_DAY }}> " +
"(<https://github.com/${{ github.repository }}/commit/${{ github.sha }}|" + "${{ github.sha }}".substring(0, 10) + ">) " +
"of ${{ github.repository }}@" + "${{ github.ref }}".split('/').reverse()[0] + " by ${{ github.event.head_commit.committer.name }}",
"color": "danger"
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure() && github.event_name == 'push'
staticcheck:
runs-on: github-4vcpu-ubuntu-2204
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: [amd64]
include:
- goos: windows
goarch: 386
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Check out code
uses: actions/checkout@v3
- name: Install staticcheck
run: "GOBIN=~/.local/bin go install honnef.co/go/tools/cmd/staticcheck"
- name: Print staticcheck version
run: "staticcheck -version"
- name: "Run staticcheck (${{ matrix.goos }}/${{ matrix.goarch }})"
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: "staticcheck -- $(go list ./... | grep -v tempfork)"
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |
{
"attachments": [{
"text": "${{ job.status }}: ${{ github.workflow }} <https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks|${{ env.COMMIT_DATE }} #${{ env.COMMIT_NUMBER_OF_DAY }}> " +
"(<https://github.com/${{ github.repository }}/commit/${{ github.sha }}|" + "${{ github.sha }}".substring(0, 10) + ">) " +
"of ${{ github.repository }}@" + "${{ github.ref }}".split('/').reverse()[0] + " by ${{ github.event.head_commit.committer.name }}",
"color": "danger"
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure() && github.event_name == 'push'

View File

@@ -0,0 +1,30 @@
name: "@tailscale/connect npm publish"
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up node
uses: actions/setup-node@v3
with:
node-version: "16.x"
registry-url: "https://registry.npmjs.org"
- name: Build package
# Build with build_dist.sh to ensure that version information is embedded.
# GOROOT is specified so that the Go/Wasm that is trigged by build-pk
# also picks up our custom Go toolchain.
run: |
./build_dist.sh tailscale.com/cmd/tsconnect
GOROOT="${HOME}/.cache/tailscale-go" ./tsconnect build-pkg
- name: Publish
env:
NODE_AUTH_TOKEN: ${{ secrets.TSCONNECT_NPM_PUBLISH_AUTH_TOKEN }}
run: ./tool/yarn --cwd ./cmd/tsconnect/pkg publish --access public

View File

@@ -5,6 +5,10 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
ubuntu2004-LTS-cloud-base:
runs-on: [ self-hosted, linux, vm ]
@@ -15,13 +19,13 @@ jobs:
- name: Set GOPATH
run: echo "GOPATH=$HOME/go" >> $GITHUB_ENV
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Checkout Code
uses: actions/checkout@v3
go-version-file: go.mod
- name: Run VM tests
run: go test ./tstest/integration/vms -v -no-s3 -run-vm-tests -run=TestRunUbuntu2004

View File

@@ -1,77 +0,0 @@
name: Windows race
on:
push:
branches:
- main
pull_request:
branches:
- '*'
jobs:
test:
runs-on: windows-latest
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
- name: Checkout code
uses: actions/checkout@v3
- name: Restore Cache
uses: actions/cache@v3
with:
# Note: unlike some other setups, this is only grabbing the mod download
# cache, rather than the whole mod directory, as the download cache
# contains zips that can be unpacked in parallel faster than they can be
# fetched and extracted by tar
path: |
~/go/pkg/mod/cache
~\AppData\Local\go-build
# The -2- here should be incremented when the scheme of data to be
# cached changes (e.g. path above changes).
# The -race- here ensures that non-race builds and race builds do not
# overwrite each others cache, as while they share some files, they
# differ in most by volume (build cache).
# TODO(raggi): add a go version here.
key: ${{ runner.os }}-go-2-race-${{ hashFiles('**/go.sum') }}
- name: Print toolchain details
run: gcc -v
# There is currently an issue in the race detector in Go on Windows when
# used with a newer version of GCC.
# See https://github.com/tailscale/tailscale/issues/4926.
- name: Downgrade MinGW
shell: bash
run: |
choco install mingw --version 10.2.0 --allow-downgrade
- name: Test with -race flag
# Don't use -bench=. -benchtime=1x.
# Somewhere in the layers (powershell?)
# the equals signs cause great confusion.
run: go test -race -bench . -benchtime 1x ./...
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |
{
"attachments": [{
"text": "${{ job.status }}: ${{ github.workflow }} <https://github.com/${{ github.repository }}/commit/${{ github.sha }}/checks|${{ env.COMMIT_DATE }} #${{ env.COMMIT_NUMBER_OF_DAY }}> " +
"(<https://github.com/${{ github.repository }}/commit/${{ github.sha }}|" + "${{ github.sha }}".substring(0, 10) + ">) " +
"of ${{ github.repository }}@" + "${{ github.ref }}".split('/').reverse()[0] + " by ${{ github.event.head_commit.committer.name }}",
"color": "danger"
}]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: failure() && github.event_name == 'push'

View File

@@ -8,21 +8,24 @@ on:
branches:
- '*'
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
runs-on: windows-latest
runs-on: windows-8vcpu
if: "!contains(github.event.head_commit.message, '[ci skip]')"
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Go
uses: actions/setup-go@v3
with:
go-version: 1.18.x
- name: Checkout code
uses: actions/checkout@v3
go-version-file: go.mod
- name: Restore Cache
uses: actions/cache@v3

View File

@@ -32,7 +32,7 @@
# $ docker exec tailscaled tailscale status
FROM golang:1.18-alpine AS build-env
FROM golang:1.19-alpine AS build-env
WORKDIR /go/src/tailscale
@@ -72,3 +72,4 @@ FROM alpine:3.16
RUN apk add --no-cache ca-certificates iptables iproute2 ip6tables
COPY --from=build-env /go/bin/* /usr/local/bin/
COPY --from=build-env /go/src/tailscale/docs/k8s/run.sh /usr/local/bin/

View File

@@ -9,15 +9,19 @@ vet:
./tool/go vet ./...
tidy:
./tool/go mod tidy -compat=1.17
./tool/go mod tidy
updatedeps:
./tool/go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscaled
./tool/go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscale
./tool/go run github.com/tailscale/depaware --update \
tailscale.com/cmd/tailscaled \
tailscale.com/cmd/tailscale \
tailscale.com/cmd/derper
depaware:
./tool/go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
./tool/go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
./tool/go run github.com/tailscale/depaware --check \
tailscale.com/cmd/tailscaled \
tailscale.com/cmd/tailscale \
tailscale.com/cmd/derper
buildwindows:
GOOS=windows GOARCH=amd64 ./tool/go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
@@ -28,10 +32,13 @@ build386:
buildlinuxarm:
GOOS=linux GOARCH=arm ./tool/go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
buildwasm:
GOOS=js GOARCH=wasm ./tool/go install ./cmd/tsconnect/wasm ./cmd/tailscale/cli
buildmultiarchimage:
./build_docker.sh
check: staticcheck vet depaware buildwindows build386 buildlinuxarm
check: staticcheck vet depaware buildwindows build386 buildlinuxarm buildwasm
staticcheck:
./tool/go run honnef.co/go/tools/cmd/staticcheck -- $$(./tool/go list ./... | grep -v tempfork)

View File

@@ -43,10 +43,7 @@ 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 only guarantee to support the latest Go release and any Go beta or
release candidate builds (currently Go 1.18) in module mode. It might
work in earlier Go versions or in GOPATH mode, but we're making no
effort to keep those working.
We require the latest Go release, currently Go 1.19.
## Bugs

View File

@@ -1 +1 @@
1.29.0
1.31.0

2
api.md
View File

@@ -1120,7 +1120,7 @@ Replaces the list of searchpaths with the list supplied by the user and returns
`searchPaths` - A list of searchpaths in JSON.
```
{
"searchPaths: ["user1.example.com", "user2.example.com"]
"searchPaths": ["user1.example.com", "user2.example.com"]
}
```

View File

@@ -9,16 +9,15 @@
package atomicfile // import "tailscale.com/atomicfile"
import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
)
// WriteFile writes data to filename+some suffix, then renames it
// into filename.
// into filename. The perm argument is ignored on Windows.
func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+".tmp")
f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename)+".tmp")
if err != nil {
return err
}

View File

@@ -45,4 +45,25 @@ EOF
exit 0
fi
exec ./tool/go build -ldflags "-X tailscale.com/version.Long=${LONG} -X tailscale.com/version.Short=${SHORT} -X tailscale.com/version.GitCommit=${GIT_HASH}" "$@"
tags=""
ldflags="-X tailscale.com/version.Long=${LONG} -X tailscale.com/version.Short=${SHORT} -X tailscale.com/version.GitCommit=${GIT_HASH}"
# build_dist.sh arguments must precede go build arguments.
while [ "$#" -gt 1 ]; do
case "$1" in
--extra-small)
shift
ldflags="$ldflags -w -s"
tags="${tags:+$tags,}ts_omit_aws"
;;
--box)
shift
tags="${tags:+$tags,}ts_include_cli"
;;
*)
break
;;
esac
done
exec ./tool/go build ${tags:+-tags=$tags} -ldflags "$ldflags" "$@"

View File

@@ -11,15 +11,31 @@ import (
"fmt"
"net"
"strings"
"time"
)
const (
// Maximum amount of time we should wait when reading a response from BIRD.
responseTimeout = 10 * time.Second
)
// New creates a BIRDClient.
func New(socket string) (*BIRDClient, error) {
return newWithTimeout(socket, responseTimeout)
}
func newWithTimeout(socket string, timeout time.Duration) (*BIRDClient, error) {
conn, err := net.Dial("unix", socket)
if err != nil {
return nil, fmt.Errorf("failed to connect to BIRD: %w", err)
}
b := &BIRDClient{socket: socket, conn: conn, scanner: bufio.NewScanner(conn)}
b := &BIRDClient{
socket: socket,
conn: conn,
scanner: bufio.NewScanner(conn),
timeNow: time.Now,
timeout: timeout,
}
// Read and discard the first line as that is the welcome message.
if _, err := b.readResponse(); err != nil {
return nil, err
@@ -32,6 +48,8 @@ type BIRDClient struct {
socket string
conn net.Conn
scanner *bufio.Scanner
timeNow func() time.Time
timeout time.Duration
}
// Close closes the underlying connection to BIRD.
@@ -81,10 +99,15 @@ func (b *BIRDClient) EnableProtocol(protocol string) error {
// 1 means table entry, 8 runtime error and 9 syntax error.
func (b *BIRDClient) exec(cmd string, args ...any) (string, error) {
if err := b.conn.SetWriteDeadline(b.timeNow().Add(b.timeout)); err != nil {
return "", err
}
if _, err := fmt.Fprintf(b.conn, cmd, args...); err != nil {
return "", err
}
fmt.Fprintln(b.conn)
if _, err := fmt.Fprintln(b.conn); err != nil {
return "", err
}
return b.readResponse()
}
@@ -105,14 +128,20 @@ func hasResponseCode(s []byte) bool {
}
func (b *BIRDClient) readResponse() (string, error) {
// Set the read timeout before we start reading anything.
if err := b.conn.SetReadDeadline(b.timeNow().Add(b.timeout)); err != nil {
return "", err
}
var resp strings.Builder
var done bool
for !done {
if !b.scanner.Scan() {
return "", fmt.Errorf("reading response from bird failed: %q", resp.String())
}
if err := b.scanner.Err(); err != nil {
return "", err
if err := b.scanner.Err(); err != nil {
return "", err
}
return "", fmt.Errorf("reading response from bird failed (EOF): %q", resp.String())
}
out := b.scanner.Bytes()
if _, err := resp.Write(out); err != nil {

View File

@@ -8,9 +8,12 @@ import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
)
type fakeBIRD struct {
@@ -109,3 +112,82 @@ func TestChirp(t *testing.T) {
t.Fatalf("disabling %q succeded", "rando")
}
}
type hangingListener struct {
net.Listener
t *testing.T
done chan struct{}
wg sync.WaitGroup
sock string
}
func newHangingListener(t *testing.T) *hangingListener {
sock := filepath.Join(t.TempDir(), "sock")
l, err := net.Listen("unix", sock)
if err != nil {
t.Fatal(err)
}
return &hangingListener{
Listener: l,
t: t,
done: make(chan struct{}),
sock: sock,
}
}
func (hl *hangingListener) Stop() {
hl.Close()
close(hl.done)
hl.wg.Wait()
}
func (hl *hangingListener) listen() error {
for {
c, err := hl.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
return nil
}
return err
}
hl.wg.Add(1)
go hl.handle(c)
}
}
func (hl *hangingListener) handle(c net.Conn) {
defer hl.wg.Done()
// Write our fake first line of response so that we get into the read loop
fmt.Fprintln(c, "0001 BIRD 2.0.8 ready.")
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
hl.t.Logf("connection still hanging")
case <-hl.done:
return
}
}
}
func TestChirpTimeout(t *testing.T) {
fb := newHangingListener(t)
defer fb.Stop()
go fb.listen()
c, err := newWithTimeout(fb.sock, 500*time.Millisecond)
if err != nil {
t.Fatal(err)
}
err = c.EnableProtocol("tailscale")
if err == nil {
t.Fatal("got err=nil, want timeout")
}
if !os.IsTimeout(err) {
t.Fatalf("got err=%v, want os.IsTimeout(err)=true", err)
}
}

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale
@@ -13,8 +13,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"inet.af/netaddr"
"net/netip"
)
// ACLRow defines a rule that grants access by a set of users or groups to a set
@@ -354,7 +353,7 @@ func (c *Client) PreviewACLForUser(ctx context.Context, acl ACL, user string) (r
// Returns ACLPreview on success with matches in a slice. If there are no matches,
// the call is still successful but Matches will be an empty slice.
// Returns error if the provided ACL is invalid.
func (c *Client) PreviewACLForIPPort(ctx context.Context, acl ACL, ipport netaddr.IPPort) (res *ACLPreview, err error) {
func (c *Client) PreviewACLForIPPort(ctx context.Context, acl ACL, ipport netip.AddrPort) (res *ACLPreview, err error) {
// Format return errors to be descriptive.
defer func() {
if err != nil {

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale
@@ -15,10 +15,10 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptrace"
"net/netip"
"net/url"
"os/exec"
"runtime"
@@ -28,7 +28,6 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
@@ -36,6 +35,7 @@ import (
"tailscale.com/paths"
"tailscale.com/safesocket"
"tailscale.com/tailcfg"
"tailscale.com/tka"
)
// defaultLocalClient is the default LocalClient when using the legacy
@@ -136,7 +136,7 @@ func (lc *LocalClient) doLocalRequestNiceError(req *http.Request) (*http.Respons
onVersionMismatch(ipn.IPCVersion(), server)
}
if res.StatusCode == 403 {
all, _ := ioutil.ReadAll(res.Body)
all, _ := io.ReadAll(res.Body)
return nil, &AccessDeniedError{errors.New(errorMessageFromBody(all))}
}
return res, nil
@@ -206,7 +206,7 @@ func (lc *LocalClient) send(ctx context.Context, method, path string, wantStatus
return nil, err
}
defer res.Body.Close()
slurp, err := ioutil.ReadAll(res.Body)
slurp, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
@@ -364,7 +364,7 @@ func (lc *LocalClient) GetWaitingFile(ctx context.Context, baseName string) (rc
return nil, 0, fmt.Errorf("unexpected chunking")
}
if res.StatusCode != 200 {
body, _ := ioutil.ReadAll(res.Body)
body, _ := io.ReadAll(res.Body)
res.Body.Close()
return nil, 0, fmt.Errorf("HTTP %s: %s", res.Status, body)
}
@@ -665,7 +665,7 @@ func (lc *LocalClient) ExpandSNIName(ctx context.Context, name string) (fqdn str
// Ping sends a ping of the provided type to the provided IP and waits
// for its response.
func (lc *LocalClient) Ping(ctx context.Context, ip netaddr.IP, pingtype tailcfg.PingType) (*ipnstate.PingResult, error) {
func (lc *LocalClient) Ping(ctx context.Context, ip netip.Addr, pingtype tailcfg.PingType) (*ipnstate.PingResult, error) {
v := url.Values{}
v.Set("ip", ip.String())
v.Set("type", string(pingtype))
@@ -680,6 +680,42 @@ func (lc *LocalClient) Ping(ctx context.Context, ip netaddr.IP, pingtype tailcfg
return pr, nil
}
// NetworkLockStatus fetches information about the tailnet key authority, if one is configured.
func (lc *LocalClient) NetworkLockStatus(ctx context.Context) (*ipnstate.NetworkLockStatus, error) {
body, err := lc.send(ctx, "GET", "/localapi/v0/tka/status", 200, nil)
if err != nil {
return nil, fmt.Errorf("error: %w", err)
}
pr := new(ipnstate.NetworkLockStatus)
if err := json.Unmarshal(body, pr); err != nil {
return nil, err
}
return pr, nil
}
// NetworkLockInit initializes the tailnet key authority.
func (lc *LocalClient) NetworkLockInit(ctx context.Context, keys []tka.Key) (*ipnstate.NetworkLockStatus, error) {
var b bytes.Buffer
type initRequest struct {
Keys []tka.Key
}
if err := json.NewEncoder(&b).Encode(initRequest{Keys: keys}); err != nil {
return nil, err
}
body, err := lc.send(ctx, "POST", "/localapi/v0/tka/init", 200, &b)
if err != nil {
return nil, fmt.Errorf("error: %w", err)
}
pr := new(ipnstate.NetworkLockStatus)
if err := json.Unmarshal(body, pr); err != nil {
return nil, err
}
return pr, nil
}
// tailscaledConnectHint gives a little thing about why tailscaled (or
// platform equivalent) is not answering localapi connections.
//

View File

@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
//go:build !go1.19
// +build !go1.19
package tailscale
func init() {
you_need_Go_1_18_to_compile_Tailscale()
you_need_Go_1_19_to_compile_Tailscale()
}

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale
@@ -13,15 +13,14 @@ import (
"encoding/json"
"fmt"
"net/http"
"inet.af/netaddr"
"net/netip"
)
// Routes contains the lists of subnet routes that are currently advertised by a device,
// as well as the subnets that are enabled to be routed by the device.
type Routes struct {
AdvertisedRoutes []netaddr.IPPrefix `json:"advertisedRoutes"`
EnabledRoutes []netaddr.IPPrefix `json:"enabledRoutes"`
AdvertisedRoutes []netip.Prefix `json:"advertisedRoutes"`
EnabledRoutes []netip.Prefix `json:"enabledRoutes"`
}
// Routes retrieves the list of subnet routes that have been enabled for a device.
@@ -56,14 +55,14 @@ func (c *Client) Routes(ctx context.Context, deviceID string) (routes *Routes, e
}
type postRoutesParams struct {
Routes []netaddr.IPPrefix `json:"routes"`
Routes []netip.Prefix `json:"routes"`
}
// SetRoutes updates the list of subnets that are enabled for a device.
// Subnets must be parsable by inet.af/netaddr.ParseIPPrefix.
// Subnets must be parsable by net/netip.ParsePrefix.
// Subnets do not have to be currently advertised by a device, they may be pre-enabled.
// Returns the updated list of enabled and advertised subnet routes in a *Routes object.
func (c *Client) SetRoutes(ctx context.Context, deviceID string, subnets []netaddr.IPPrefix) (routes *Routes, err error) {
func (c *Client) SetRoutes(ctx context.Context, deviceID string, subnets []netip.Prefix) (routes *Routes, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("tailscale.SetRoutes: %w", err)

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package tailscale

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
// Package tailscale contains Go clients for the Tailscale Local API and
// Tailscale control plane API.
@@ -17,7 +17,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
)
@@ -131,7 +130,7 @@ func (c *Client) sendRequest(req *http.Request) ([]byte, *http.Response, error)
// Read response. Limit the response to 10MB.
body := io.LimitReader(resp.Body, maxReadSize+1)
b, err := ioutil.ReadAll(body)
b, err := io.ReadAll(body)
if len(b) > maxReadSize {
err = errors.New("API response too large")
}

View File

@@ -153,18 +153,25 @@ func gen(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named) {
}
writef("}")
case *types.Map:
elem := ft.Elem()
writef("if dst.%s != nil {", fname)
writef("\tdst.%s = map[%s]%s{}", fname, it.QualifiedName(ft.Key()), it.QualifiedName(ft.Elem()))
if sliceType, isSlice := ft.Elem().(*types.Slice); isSlice {
writef("\tdst.%s = map[%s]%s{}", fname, it.QualifiedName(ft.Key()), it.QualifiedName(elem))
if sliceType, isSlice := elem.(*types.Slice); isSlice {
n := it.QualifiedName(sliceType.Elem())
writef("\tfor k := range src.%s {", fname)
// use zero-length slice instead of nil to ensure
// the key is always copied.
writef("\t\tdst.%s[k] = append([]%s{}, src.%s[k]...)", fname, n, fname)
writef("\t}")
} else if codegen.ContainsPointers(ft.Elem()) {
} else if codegen.ContainsPointers(elem) {
writef("\tfor k, v := range src.%s {", fname)
writef("\t\tdst.%s[k] = v.Clone()", fname)
switch elem.(type) {
case *types.Pointer:
writef("\t\tdst.%s[k] = v.Clone()", fname)
default:
writef("\t\tv2 := v.Clone()")
writef("\t\tdst.%s[k] = *v2", fname)
}
writef("\t}")
} else {
writef("\tfor k, v := range src.%s {", fname)

View File

@@ -12,20 +12,36 @@ import (
"net"
"net/http"
"strings"
"sync/atomic"
"time"
"tailscale.com/syncs"
)
var dnsCache atomic.Value // of []byte
const refreshTimeout = time.Minute
var bootstrapDNSRequests = expvar.NewInt("counter_bootstrap_dns_requests")
type dnsEntryMap map[string][]net.IP
var (
dnsCache syncs.AtomicValue[dnsEntryMap]
dnsCacheBytes syncs.AtomicValue[[]byte] // of JSON
unpublishedDNSCache syncs.AtomicValue[dnsEntryMap]
)
var (
bootstrapDNSRequests = expvar.NewInt("counter_bootstrap_dns_requests")
publishedDNSHits = expvar.NewInt("counter_bootstrap_dns_published_hits")
publishedDNSMisses = expvar.NewInt("counter_bootstrap_dns_published_misses")
unpublishedDNSHits = expvar.NewInt("counter_bootstrap_dns_unpublished_hits")
unpublishedDNSMisses = expvar.NewInt("counter_bootstrap_dns_unpublished_misses")
)
func refreshBootstrapDNSLoop() {
if *bootstrapDNS == "" {
if *bootstrapDNS == "" && *unpublishedDNS == "" {
return
}
for {
refreshBootstrapDNS()
refreshUnpublishedDNS()
time.Sleep(10 * time.Minute)
}
}
@@ -34,10 +50,34 @@ func refreshBootstrapDNS() {
if *bootstrapDNS == "" {
return
}
dnsEntries := make(map[string][]net.IP)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), refreshTimeout)
defer cancel()
names := strings.Split(*bootstrapDNS, ",")
dnsEntries := resolveList(ctx, strings.Split(*bootstrapDNS, ","))
j, err := json.MarshalIndent(dnsEntries, "", "\t")
if err != nil {
// leave the old values in place
return
}
dnsCache.Store(dnsEntries)
dnsCacheBytes.Store(j)
}
func refreshUnpublishedDNS() {
if *unpublishedDNS == "" {
return
}
ctx, cancel := context.WithTimeout(context.Background(), refreshTimeout)
defer cancel()
dnsEntries := resolveList(ctx, strings.Split(*unpublishedDNS, ","))
unpublishedDNSCache.Store(dnsEntries)
}
func resolveList(ctx context.Context, names []string) dnsEntryMap {
dnsEntries := make(dnsEntryMap)
var r net.Resolver
for _, name := range names {
addrs, err := r.LookupIP(ctx, "ip", name)
@@ -47,21 +87,47 @@ func refreshBootstrapDNS() {
}
dnsEntries[name] = addrs
}
j, err := json.MarshalIndent(dnsEntries, "", "\t")
if err != nil {
// leave the old values in place
return
}
dnsCache.Store(j)
return dnsEntries
}
func handleBootstrapDNS(w http.ResponseWriter, r *http.Request) {
bootstrapDNSRequests.Add(1)
w.Header().Set("Content-Type", "application/json")
j, _ := dnsCache.Load().([]byte)
// Bootstrap DNS requests occur cross-regions,
// and are randomized per request,
// so keeping a connection open is pointlessly expensive.
// Bootstrap DNS requests occur cross-regions, and are randomized per
// request, so keeping a connection open is pointlessly expensive.
w.Header().Set("Connection", "close")
// Try answering a query from our hidden map first
if q := r.URL.Query().Get("q"); q != "" {
if ips, ok := unpublishedDNSCache.Load()[q]; ok && len(ips) > 0 {
unpublishedDNSHits.Add(1)
// Only return the specific query, not everything.
m := dnsEntryMap{q: ips}
j, err := json.MarshalIndent(m, "", "\t")
if err == nil {
w.Write(j)
return
}
}
// If we have a "q" query for a name in the published cache
// list, then track whether that's a hit/miss.
if m, ok := dnsCache.Load()[q]; ok {
if len(m) > 0 {
publishedDNSHits.Add(1)
} else {
publishedDNSMisses.Add(1)
}
} else {
// If it wasn't in either cache, treat this as a query
// for the unpublished cache, and thus a cache miss.
unpublishedDNSMisses.Add(1)
}
}
// Fall back to returning the public set of cached DNS names
j := dnsCacheBytes.Load()
w.Write(j)
}

View File

@@ -5,7 +5,12 @@
package main
import (
"encoding/json"
"net"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"
)
@@ -17,11 +22,12 @@ func BenchmarkHandleBootstrapDNS(b *testing.B) {
}()
refreshBootstrapDNS()
w := new(bitbucketResponseWriter)
req, _ := http.NewRequest("GET", "https://localhost/bootstrap-dns?q="+url.QueryEscape("log.tailscale.io"), nil)
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(b *testing.PB) {
for b.Next() {
handleBootstrapDNS(w, nil)
handleBootstrapDNS(w, req)
}
})
}
@@ -33,3 +39,116 @@ func (b *bitbucketResponseWriter) Header() http.Header { return make(http.Header
func (b *bitbucketResponseWriter) Write(p []byte) (int, error) { return len(p), nil }
func (b *bitbucketResponseWriter) WriteHeader(statusCode int) {}
func getBootstrapDNS(t *testing.T, q string) dnsEntryMap {
t.Helper()
req, _ := http.NewRequest("GET", "https://localhost/bootstrap-dns?q="+url.QueryEscape(q), nil)
w := httptest.NewRecorder()
handleBootstrapDNS(w, req)
res := w.Result()
if res.StatusCode != 200 {
t.Fatalf("got status=%d; want %d", res.StatusCode, 200)
}
var ips dnsEntryMap
if err := json.NewDecoder(res.Body).Decode(&ips); err != nil {
t.Fatalf("error decoding response body: %v", err)
}
return ips
}
func TestUnpublishedDNS(t *testing.T) {
const published = "login.tailscale.com"
const unpublished = "log.tailscale.io"
prev1, prev2 := *bootstrapDNS, *unpublishedDNS
*bootstrapDNS = published
*unpublishedDNS = unpublished
t.Cleanup(func() {
*bootstrapDNS = prev1
*unpublishedDNS = prev2
})
refreshBootstrapDNS()
refreshUnpublishedDNS()
hasResponse := func(q string) bool {
_, found := getBootstrapDNS(t, q)[q]
return found
}
if !hasResponse(published) {
t.Errorf("expected response for: %s", published)
}
if !hasResponse(unpublished) {
t.Errorf("expected response for: %s", unpublished)
}
// Verify that querying for a random query or a real query does not
// leak our unpublished domain
m1 := getBootstrapDNS(t, published)
if _, found := m1[unpublished]; found {
t.Errorf("found unpublished domain %s: %+v", unpublished, m1)
}
m2 := getBootstrapDNS(t, "random.example.com")
if _, found := m2[unpublished]; found {
t.Errorf("found unpublished domain %s: %+v", unpublished, m2)
}
}
func resetMetrics() {
publishedDNSHits.Set(0)
publishedDNSMisses.Set(0)
unpublishedDNSHits.Set(0)
unpublishedDNSMisses.Set(0)
}
// Verify that we don't count an empty list in the unpublishedDNSCache as a
// cache hit in our metrics.
func TestUnpublishedDNSEmptyList(t *testing.T) {
pub := dnsEntryMap{
"tailscale.com": {net.IPv4(10, 10, 10, 10)},
}
dnsCache.Store(pub)
dnsCacheBytes.Store([]byte(`{"tailscale.com":["10.10.10.10"]}`))
unpublishedDNSCache.Store(dnsEntryMap{
"log.tailscale.io": {},
"controlplane.tailscale.com": {net.IPv4(1, 2, 3, 4)},
})
t.Run("CacheMiss", func(t *testing.T) {
// One domain in map but empty, one not in map at all
for _, q := range []string{"log.tailscale.io", "login.tailscale.com"} {
resetMetrics()
ips := getBootstrapDNS(t, q)
// Expected our public map to be returned on a cache miss
if !reflect.DeepEqual(ips, pub) {
t.Errorf("got ips=%+v; want %+v", ips, pub)
}
if v := unpublishedDNSHits.Value(); v != 0 {
t.Errorf("got hits=%d; want 0", v)
}
if v := unpublishedDNSMisses.Value(); v != 1 {
t.Errorf("got misses=%d; want 1", v)
}
}
})
// Verify that we do get a valid response and metric.
t.Run("CacheHit", func(t *testing.T) {
resetMetrics()
ips := getBootstrapDNS(t, "controlplane.tailscale.com")
want := dnsEntryMap{"controlplane.tailscale.com": {net.IPv4(1, 2, 3, 4)}}
if !reflect.DeepEqual(ips, want) {
t.Errorf("got ips=%+v; want %+v", ips, want)
}
if v := unpublishedDNSHits.Value(); v != 1 {
t.Errorf("got hits=%d; want 1", v)
}
if v := unpublishedDNSMisses.Value(); v != 0 {
t.Errorf("got misses=%d; want 0", v)
}
})
}

View File

@@ -20,6 +20,11 @@ var unsafeHostnameCharacters = regexp.MustCompile(`[^a-zA-Z0-9-\.]`)
type certProvider interface {
// TLSConfig creates a new TLS config suitable for net/http.Server servers.
//
// The returned Config must have a GetCertificate function set and that
// function must return a unique *tls.Certificate for each call. The
// returned *tls.Certificate will be mutated by the caller to append to the
// (*tls.Certificate).Certificate field.
TLSConfig() *tls.Config
// HTTPHandler handle ACME related request, if any.
HTTPHandler(fallback http.Handler) http.Handler
@@ -87,7 +92,13 @@ func (m *manualCertManager) getCertificate(hi *tls.ClientHelloInfo) (*tls.Certif
if hi.ServerName != m.hostname {
return nil, fmt.Errorf("cert mismatch with hostname: %q", hi.ServerName)
}
return m.cert, nil
// Return a shallow copy of the cert so the caller can append to its
// Certificate field.
certCopy := new(tls.Certificate)
*certCopy = *m.cert
certCopy.Certificate = certCopy.Certificate[:len(certCopy.Certificate):len(certCopy.Certificate)]
return certCopy, nil
}
func (m *manualCertManager) HTTPHandler(fallback http.Handler) http.Handler {

197
cmd/derper/depaware.txt Normal file
View File

@@ -0,0 +1,197 @@
tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
github.com/klauspost/compress/flate from nhooyr.io/websocket
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/socket from github.com/mdlayher/netlink
💣 github.com/mitchellh/go-ps from tailscale.com/safesocket
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/client/tailscale+
go4.org/netipx from tailscale.com/wgengine/filter
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
nhooyr.io/websocket from tailscale.com/cmd/derper+
nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
tailscale.com from tailscale.com/version
tailscale.com/atomicfile from tailscale.com/cmd/derper+
tailscale.com/client/tailscale from tailscale.com/derp
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale
tailscale.com/derp from tailscale.com/cmd/derper+
tailscale.com/derp/derphttp from tailscale.com/cmd/derper
tailscale.com/disco from tailscale.com/derp
tailscale.com/envknob from tailscale.com/derp+
tailscale.com/hostinfo from tailscale.com/net/interfaces+
tailscale.com/ipn from tailscale.com/client/tailscale
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
💣 tailscale.com/metrics from tailscale.com/cmd/derper+
tailscale.com/net/dnscache from tailscale.com/derp/derphttp
tailscale.com/net/flowtrack from tailscale.com/net/packet+
💣 tailscale.com/net/interfaces from tailscale.com/net/netns+
tailscale.com/net/netaddr from tailscale.com/ipn+
tailscale.com/net/netknob from tailscale.com/net/netns
tailscale.com/net/netns from tailscale.com/derp/derphttp
tailscale.com/net/netutil from tailscale.com/client/tailscale
tailscale.com/net/packet from tailscale.com/wgengine/filter
tailscale.com/net/stun from tailscale.com/cmd/derper
tailscale.com/net/tlsdial from tailscale.com/derp/derphttp
tailscale.com/net/tsaddr from tailscale.com/ipn+
💣 tailscale.com/net/tshttpproxy from tailscale.com/derp/derphttp+
tailscale.com/paths from tailscale.com/client/tailscale
tailscale.com/safesocket from tailscale.com/client/tailscale
tailscale.com/syncs from tailscale.com/cmd/derper+
tailscale.com/tailcfg from tailscale.com/client/tailscale+
tailscale.com/tka from tailscale.com/client/tailscale+
W tailscale.com/tsconst from tailscale.com/net/interfaces
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
tailscale.com/tstime/rate from tailscale.com/wgengine/filter
tailscale.com/tsweb from tailscale.com/cmd/derper
tailscale.com/types/dnstype from tailscale.com/tailcfg
tailscale.com/types/empty from tailscale.com/ipn
tailscale.com/types/ipproto from tailscale.com/net/flowtrack+
tailscale.com/types/key from tailscale.com/cmd/derper+
tailscale.com/types/logger from tailscale.com/cmd/derper+
tailscale.com/types/netmap from tailscale.com/ipn
tailscale.com/types/opt from tailscale.com/client/tailscale+
tailscale.com/types/pad32 from tailscale.com/derp
tailscale.com/types/persist from tailscale.com/ipn
tailscale.com/types/preftype from tailscale.com/ipn
tailscale.com/types/structs from tailscale.com/ipn+
tailscale.com/types/tkatype from tailscale.com/types/key+
tailscale.com/types/views from tailscale.com/ipn/ipnstate+
tailscale.com/util/cloudenv from tailscale.com/hostinfo+
W tailscale.com/util/cmpver from tailscale.com/net/tshttpproxy
tailscale.com/util/dnsname from tailscale.com/hostinfo+
W tailscale.com/util/endian from tailscale.com/net/netns
tailscale.com/util/lineread from tailscale.com/hostinfo+
tailscale.com/util/singleflight from tailscale.com/net/dnscache
L tailscale.com/util/strs from tailscale.com/hostinfo
W 💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
tailscale.com/version from tailscale.com/derp+
tailscale.com/version/distro from tailscale.com/hostinfo+
tailscale.com/wgengine/filter from tailscale.com/types/netmap
golang.org/x/crypto/acme from golang.org/x/crypto/acme/autocert
golang.org/x/crypto/acme/autocert from tailscale.com/cmd/derper
golang.org/x/crypto/argon2 from tailscale.com/tka
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box+
golang.org/x/crypto/blake2s from tailscale.com/tka
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
golang.org/x/crypto/curve25519 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/secretbox from golang.org/x/crypto/nacl/box
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
L golang.org/x/net/bpf from github.com/mdlayher/netlink+
golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from net/http
golang.org/x/net/http/httpproxy from net/http
golang.org/x/net/http2/hpack from net/http
golang.org/x/net/idna from golang.org/x/crypto/acme/autocert+
golang.org/x/net/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net+
golang.org/x/sync/errgroup from github.com/mdlayher/socket+
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
LD golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+
W golang.org/x/sys/windows from golang.org/x/sys/windows/registry+
W golang.org/x/sys/windows/registry from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg+
golang.org/x/text/secure/bidirule from golang.org/x/net/idna
golang.org/x/text/transform from golang.org/x/text/secure/bidirule+
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
golang.org/x/text/unicode/norm from golang.org/x/net/idna
golang.org/x/time/rate from tailscale.com/cmd/derper+
bufio from compress/flate+
bytes from bufio+
compress/flate from compress/gzip+
compress/gzip from internal/profile+
container/list from crypto/tls+
context from crypto/tls+
crypto from crypto/ecdsa+
crypto/aes from crypto/ecdsa+
crypto/cipher from crypto/aes+
crypto/des from crypto/tls+
crypto/dsa from crypto/x509
crypto/ecdsa from crypto/tls+
crypto/ed25519 from crypto/tls+
crypto/elliptic from crypto/ecdsa+
crypto/hmac from crypto/tls+
crypto/md5 from crypto/tls+
crypto/rand from crypto/ed25519+
crypto/rc4 from crypto/tls
crypto/rsa from crypto/tls+
crypto/sha1 from crypto/tls+
crypto/sha256 from crypto/tls+
crypto/sha512 from crypto/ecdsa+
crypto/subtle from crypto/aes+
crypto/tls from golang.org/x/crypto/acme+
crypto/x509 from crypto/tls+
crypto/x509/pkix from crypto/x509+
embed from crypto/internal/nistec+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from tailscale.com/tka
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/hex from crypto/x509+
encoding/json from expvar+
encoding/pem from crypto/tls+
errors from bufio+
expvar from tailscale.com/cmd/derper+
flag from tailscale.com/cmd/derper
fmt from compress/flate+
hash from crypto+
hash/crc32 from compress/gzip+
hash/maphash from go4.org/mem
html from net/http/pprof+
io from bufio+
io/fs from crypto/x509+
io/ioutil from github.com/mitchellh/go-ps+
log from expvar+
math from compress/flate+
math/big from crypto/dsa+
math/bits from compress/flate+
math/rand from github.com/mdlayher/netlink+
mime from mime/multipart+
mime/multipart from net/http
mime/quotedprintable from mime/multipart
net from crypto/tls+
net/http from expvar+
net/http/httptrace from net/http+
net/http/internal from net/http
net/http/pprof from tailscale.com/tsweb
net/netip from go4.org/netipx+
net/textproto from golang.org/x/net/http/httpguts+
net/url from crypto/x509+
os from crypto/rand+
os/exec from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg+
path from golang.org/x/crypto/acme/autocert+
path/filepath from crypto/x509+
reflect from crypto/x509+
regexp from internal/profile+
regexp/syntax from regexp
runtime/debug from golang.org/x/crypto/acme+
runtime/pprof from net/http/pprof
runtime/trace from net/http/pprof
sort from compress/flate+
strconv from compress/flate+
strings from bufio+
sync from compress/flate+
sync/atomic from context+
syscall from crypto/rand+
text/tabwriter from runtime/pprof
time from compress/gzip+
unicode from bytes+
unicode/utf16 from crypto/x509+
unicode/utf8 from bufio+

View File

@@ -14,22 +14,22 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"net"
"net/http"
"net/netip"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"go4.org/mem"
"golang.org/x/time/rate"
"tailscale.com/atomicfile"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/logpolicy"
"tailscale.com/metrics"
"tailscale.com/net/stun"
"tailscale.com/tsweb"
@@ -37,21 +37,22 @@ import (
)
var (
dev = flag.Bool("dev", false, "run in localhost development mode")
addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.")
httpPort = flag.Int("http-port", 80, "The port on which to serve HTTP. Set to -1 to disable. The listener is bound to the same IP (if any) as specified in the -a flag.")
stunPort = flag.Int("stun-port", 3478, "The UDP port on which to serve STUN. The listener is bound to the same IP (if any) as specified in the -a flag.")
configPath = flag.String("c", "", "config file path")
certMode = flag.String("certmode", "letsencrypt", "mode for getting a cert. possible options: manual, letsencrypt")
certDir = flag.String("certdir", tsweb.DefaultCertDir("derper-certs"), "directory to store LetsEncrypt certs, if addr's port is :443")
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to")
runSTUN = flag.Bool("stun", true, "whether to run a STUN server. It will bind to the same IP (if any) as the --addr flag value.")
dev = flag.Bool("dev", false, "run in localhost development mode")
addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.")
httpPort = flag.Int("http-port", 80, "The port on which to serve HTTP. Set to -1 to disable. The listener is bound to the same IP (if any) as specified in the -a flag.")
stunPort = flag.Int("stun-port", 3478, "The UDP port on which to serve STUN. The listener is bound to the same IP (if any) as specified in the -a flag.")
configPath = flag.String("c", "", "config file path")
certMode = flag.String("certmode", "letsencrypt", "mode for getting a cert. possible options: manual, letsencrypt")
certDir = flag.String("certdir", tsweb.DefaultCertDir("derper-certs"), "directory to store LetsEncrypt certs, if addr's port is :443")
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
runSTUN = flag.Bool("stun", true, "whether to run a STUN server. It will bind to the same IP (if any) as the --addr flag value.")
runDERP = flag.Bool("derp", true, "whether to run a DERP server. The only reason to set this false is if you're decommissioning a server but want to keep its bootstrap DNS functionality still running.")
meshPSKFile = flag.String("mesh-psk-file", defaultMeshPSKFile(), "if non-empty, path to file containing the mesh pre-shared key file. It should contain some hex string; whitespace is trimmed.")
meshWith = flag.String("mesh-with", "", "optional comma-separated list of hostnames to mesh with; the server's own hostname can be in the list")
bootstrapDNS = flag.String("bootstrap-dns-names", "", "optional comma-separated list of hostnames to make available at /bootstrap-dns")
verifyClients = flag.Bool("verify-clients", false, "verify clients to this DERP server through a local tailscaled instance.")
meshPSKFile = flag.String("mesh-psk-file", defaultMeshPSKFile(), "if non-empty, path to file containing the mesh pre-shared key file. It should contain some hex string; whitespace is trimmed.")
meshWith = flag.String("mesh-with", "", "optional comma-separated list of hostnames to mesh with; the server's own hostname can be in the list")
bootstrapDNS = flag.String("bootstrap-dns-names", "", "optional comma-separated list of hostnames to make available at /bootstrap-dns")
unpublishedDNS = flag.String("unpublished-bootstrap-dns-names", "", "optional comma-separated list of hostnames to make available at /bootstrap-dns and not publish in the list")
verifyClients = flag.Bool("verify-clients", false, "verify clients to this DERP server through a local tailscaled instance.")
acceptConnLimit = flag.Float64("accept-connection-limit", math.Inf(+1), "rate limit for accepting new connection")
acceptConnBurst = flag.Int("accept-connection-burst", math.MaxInt, "burst limit for accepting new connection")
@@ -97,7 +98,7 @@ func loadConfig() config {
}
log.Printf("no config path specified; using %s", *configPath)
}
b, err := ioutil.ReadFile(*configPath)
b, err := os.ReadFile(*configPath)
switch {
case errors.Is(err, os.ErrNotExist):
return writeNewConfig()
@@ -135,7 +136,6 @@ func main() {
flag.Parse()
if *dev {
*logCollection = ""
*addr = ":3340" // above the keys DERP
log.Printf("Running in dev mode.")
tsweb.DevMode = true
@@ -146,12 +146,6 @@ func main() {
log.Fatalf("invalid server address: %v", err)
}
var logPol *logpolicy.Policy
if *logCollection != "" {
logPol = logpolicy.New(*logCollection)
log.SetOutput(logPol.Logtail)
}
cfg := loadConfig()
serveTLS := tsweb.IsProd443(*addr) || *certMode == "manual"
@@ -160,7 +154,7 @@ func main() {
s.SetVerifyClient(*verifyClients)
if *meshPSKFile != "" {
b, err := ioutil.ReadFile(*meshPSKFile)
b, err := os.ReadFile(*meshPSKFile)
if err != nil {
log.Fatal(err)
}
@@ -177,9 +171,15 @@ func main() {
expvar.Publish("derp", s.ExpVar())
mux := http.NewServeMux()
derpHandler := derphttp.Handler(s)
derpHandler = addWebSocketSupport(s, derpHandler)
mux.Handle("/derp", derpHandler)
if *runDERP {
derpHandler := derphttp.Handler(s)
derpHandler = addWebSocketSupport(s, derpHandler)
mux.Handle("/derp", derpHandler)
} else {
mux.Handle("/derp", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "derp server disabled", http.StatusNotFound)
}))
}
mux.HandleFunc("/derp/probe", probeHandler)
go refreshBootstrapDNSLoop()
mux.HandleFunc("/bootstrap-dns", handleBootstrapDNS)
@@ -195,10 +195,17 @@ func main() {
server.
</p>
`)
if !*runDERP {
io.WriteString(w, `<p>Status: <b>disabled</b></p>`)
}
if tsweb.AllowDebugAccess(r) {
io.WriteString(w, "<p>Debug info at <a href='/debug/'>/debug/</a>.</p>\n")
}
}))
mux.Handle("/robots.txt", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "User-agent: *\nDisallow: /\n")
}))
mux.Handle("/generate_204", http.HandlerFunc(serveNoContent))
debug := tsweb.Debugger(mux)
debug.KV("TLS hostname", *hostname)
debug.KV("Mesh key", s.HasMeshKey())
@@ -216,9 +223,11 @@ func main() {
go serveSTUN(listenHost, *stunPort)
}
quietLogger := log.New(logFilter{}, "", 0)
httpsrv := &http.Server{
Addr: *addr,
Handler: mux,
Addr: *addr,
Handler: mux,
ErrorLog: quietLogger,
// Set read/write timeout. For derper, this basically
// only affects TLS setup, as read/write deadlines are
@@ -284,9 +293,13 @@ func main() {
})
if *httpPort > -1 {
go func() {
port80mux := http.NewServeMux()
port80mux.HandleFunc("/generate_204", serveNoContent)
port80mux.Handle("/", certManager.HTTPHandler(tsweb.Port80Handler{Main: mux}))
port80srv := &http.Server{
Addr: net.JoinHostPort(listenHost, fmt.Sprintf("%d", *httpPort)),
Handler: certManager.HTTPHandler(tsweb.Port80Handler{Main: mux}),
Handler: port80mux,
ErrorLog: quietLogger,
ReadTimeout: 30 * time.Second,
// Crank up WriteTimeout a bit more than usually
// necessary just so we can do long CPU profiles
@@ -312,6 +325,11 @@ func main() {
}
}
// For captive portal detection
func serveNoContent(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
// probeHandler is the endpoint that js/wasm clients hit to measure
// DERP latency, since they can't do UDP STUN queries.
func probeHandler(w http.ResponseWriter, r *http.Request) {
@@ -365,7 +383,8 @@ func serverSTUNListener(ctx context.Context, pc *net.UDPConn) {
} else {
stunIPv6.Add(1)
}
res := stun.Response(txid, ua.IP, uint16(ua.Port))
addr, _ := netip.AddrFromSlice(ua.IP)
res := stun.Response(txid, netip.AddrPortFrom(addr, uint16(ua.Port)))
_, err = pc.WriteTo(res, ua)
if err != nil {
stunWriteError.Add(1)
@@ -456,3 +475,22 @@ func (l *rateLimitedListener) Accept() (net.Conn, error) {
l.numAccepts.Add(1)
return cn, nil
}
// logFilter is used to filter out useless error logs that are logged to
// the net/http.Server.ErrorLog logger.
type logFilter struct{}
func (logFilter) Write(p []byte) (int, error) {
b := mem.B(p)
if mem.HasSuffix(b, mem.S(": EOF\n")) ||
mem.HasSuffix(b, mem.S(": i/o timeout\n")) ||
mem.HasSuffix(b, mem.S(": read: connection reset by peer\n")) ||
mem.HasSuffix(b, mem.S(": remote error: tls: bad certificate\n")) ||
mem.HasSuffix(b, mem.S(": tls: first record does not look like a TLS handshake\n")) {
// Skip this log message, but say that we processed it
return len(p), nil
}
log.Printf("%s", p)
return len(p), nil
}

View File

@@ -33,6 +33,12 @@ func addWebSocketSupport(s *derp.Server, base http.Handler) http.Handler {
c, err := websocket.Accept(w, r, &websocket.AcceptOptions{
Subprotocols: []string{"derp"},
OriginPatterns: []string{"*"},
// Disable compression because we transmit WireGuard messages that
// are not compressible.
// Additionally, Safari has a broken implementation of compression
// (see https://github.com/nhooyr/websocket/issues/218) that makes
// enabling it actively harmful.
CompressionMode: websocket.CompressionDisabled,
})
if err != nil {
log.Printf("websocket.Accept: %v", err)

View File

@@ -360,7 +360,7 @@ func probeUDP(ctx context.Context, dm *tailcfg.DERPMap, n *tailcfg.DERPNode) (la
time.Sleep(100 * time.Millisecond)
continue
}
txBack, _, _, err := stun.ParseResponse(buf[:n])
txBack, _, err := stun.ParseResponse(buf[:n])
if err != nil {
return 0, fmt.Errorf("parsing STUN response from %v: %v", ip, err)
}

1
cmd/gitops-pusher/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
version-cache.json

View File

@@ -0,0 +1,67 @@
// 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 main
import (
"encoding/json"
"os"
)
// Cache contains cached information about the last time this tool was run.
//
// This is serialized to a JSON file that should NOT be checked into git.
// It should be managed with either CI cache tools or stored locally somehow. The
// exact mechanism is irrelevant as long as it is consistent.
//
// This allows gitops-pusher to detect external ACL changes. I'm not sure what to
// call this problem, so I've been calling it the "three version problem" in my
// notes. The basic problem is that at any given time we only have two versions
// of the ACL file at any given point. In order to check if there has been
// tampering of the ACL files in the admin panel, we need to have a _third_ version
// to compare against.
//
// In this case I am not storing the old ACL entirely (though that could be a
// reasonable thing to add in the future), but only its sha256sum. This allows
// us to detect if the shasum in control matches the shasum we expect, and if that
// expectation fails, then we can react accordingly.
type Cache struct {
PrevETag string // Stores the previous ETag of the ACL to allow
}
// Save persists the cache to a given file.
func (c *Cache) Save(fname string) error {
os.Remove(fname)
fout, err := os.Create(fname)
if err != nil {
return err
}
defer fout.Close()
return json.NewEncoder(fout).Encode(c)
}
// LoadCache loads the cache from a given file.
func LoadCache(fname string) (*Cache, error) {
var result Cache
fin, err := os.Open(fname)
if err != nil {
return nil, err
}
defer fin.Close()
err = json.NewDecoder(fin).Decode(&result)
if err != nil {
return nil, err
}
return &result, nil
}
// Shuck removes the first and last character of a string, analogous to
// shucking off the husk of an ear of corn.
func Shuck(s string) string {
return s[1 : len(s)-1]
}

View File

@@ -8,6 +8,7 @@
package main
import (
"bytes"
"context"
"crypto/sha256"
"encoding/json"
@@ -15,7 +16,6 @@ import (
"fmt"
"log"
"net/http"
"net/http/httputil"
"os"
"regexp"
"strings"
@@ -28,11 +28,20 @@ import (
var (
rootFlagSet = flag.NewFlagSet("gitops-pusher", flag.ExitOnError)
policyFname = rootFlagSet.String("policy-file", "./policy.hujson", "filename for policy file")
cacheFname = rootFlagSet.String("cache-file", "./version-cache.json", "filename for the previous known version hash")
timeout = rootFlagSet.Duration("timeout", 5*time.Minute, "timeout for the entire CI run")
githubSyntax = rootFlagSet.Bool("github-syntax", true, "use GitHub Action error syntax (https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message)")
)
func apply(tailnet, apiKey string) func(context.Context, []string) error {
func modifiedExternallyError() {
if *githubSyntax {
fmt.Printf("::warning file=%s,line=1,col=1,title=Policy File Modified Externally::The policy file was modified externally in the admin console.\n", *policyFname)
} else {
fmt.Printf("The policy file was modified externally in the admin console.\n")
}
}
func apply(cache *Cache, tailnet, apiKey string) func(context.Context, []string) error {
return func(ctx context.Context, args []string) error {
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
if err != nil {
@@ -44,10 +53,21 @@ func apply(tailnet, apiKey string) func(context.Context, []string) error {
return err
}
if cache.PrevETag == "" {
log.Println("no previous etag found, assuming local file is correct and recording that")
cache.PrevETag = localEtag
}
log.Printf("control: %s", controlEtag)
log.Printf("local: %s", localEtag)
log.Printf("cache: %s", cache.PrevETag)
if cache.PrevETag != controlEtag {
modifiedExternallyError()
}
if controlEtag == localEtag {
cache.PrevETag = localEtag
log.Println("no update needed, doing nothing")
return nil
}
@@ -56,11 +76,13 @@ func apply(tailnet, apiKey string) func(context.Context, []string) error {
return err
}
cache.PrevETag = localEtag
return nil
}
}
func test(tailnet, apiKey string) func(context.Context, []string) error {
func test(cache *Cache, tailnet, apiKey string) func(context.Context, []string) error {
return func(ctx context.Context, args []string) error {
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
if err != nil {
@@ -72,8 +94,18 @@ func test(tailnet, apiKey string) func(context.Context, []string) error {
return err
}
if cache.PrevETag == "" {
log.Println("no previous etag found, assuming local file is correct and recording that")
cache.PrevETag = localEtag
}
log.Printf("control: %s", controlEtag)
log.Printf("local: %s", localEtag)
log.Printf("cache: %s", cache.PrevETag)
if cache.PrevETag != controlEtag {
modifiedExternallyError()
}
if controlEtag == localEtag {
log.Println("no updates found, doing nothing")
@@ -87,7 +119,7 @@ func test(tailnet, apiKey string) func(context.Context, []string) error {
}
}
func getChecksums(tailnet, apiKey string) func(context.Context, []string) error {
func getChecksums(cache *Cache, tailnet, apiKey string) func(context.Context, []string) error {
return func(ctx context.Context, args []string) error {
controlEtag, err := getACLETag(ctx, tailnet, apiKey)
if err != nil {
@@ -99,8 +131,14 @@ func getChecksums(tailnet, apiKey string) func(context.Context, []string) error
return err
}
if cache.PrevETag == "" {
log.Println("no previous etag found, assuming local file is correct and recording that")
cache.PrevETag = Shuck(localEtag)
}
log.Printf("control: %s", controlEtag)
log.Printf("local: %s", localEtag)
log.Printf("cache: %s", cache.PrevETag)
return nil
}
@@ -115,13 +153,22 @@ func main() {
if !ok {
log.Fatal("set envvar TS_API_KEY to your Tailscale API key")
}
cache, err := LoadCache(*cacheFname)
if err != nil {
if os.IsNotExist(err) {
cache = &Cache{}
} else {
log.Fatalf("error loading cache: %v", err)
}
}
defer cache.Save(*cacheFname)
applyCmd := &ffcli.Command{
Name: "apply",
ShortUsage: "gitops-pusher [options] apply",
ShortHelp: "Pushes changes to CONTROL",
LongHelp: `Pushes changes to CONTROL`,
Exec: apply(tailnet, apiKey),
Exec: apply(cache, tailnet, apiKey),
}
testCmd := &ffcli.Command{
@@ -129,7 +176,7 @@ func main() {
ShortUsage: "gitops-pusher [options] test",
ShortHelp: "Tests ACL changes",
LongHelp: "Tests ACL changes",
Exec: test(tailnet, apiKey),
Exec: test(cache, tailnet, apiKey),
}
cksumCmd := &ffcli.Command{
@@ -137,7 +184,7 @@ func main() {
ShortUsage: "Shows checksums of ACL files",
ShortHelp: "Fetch checksum of CONTROL's ACL and the local ACL for comparison",
LongHelp: "Fetch checksum of CONTROL's ACL and the local ACL for comparison",
Exec: getChecksums(tailnet, apiKey),
Exec: getChecksums(cache, tailnet, apiKey),
}
root := &ffcli.Command{
@@ -177,7 +224,7 @@ func sumFile(fname string) (string, error) {
return "", err
}
return fmt.Sprintf("\"%x\"", h.Sum(nil)), nil
return fmt.Sprintf("%x", h.Sum(nil)), nil
}
func applyNewACL(ctx context.Context, tailnet, apiKey, policyFname, oldEtag string) error {
@@ -194,7 +241,7 @@ func applyNewACL(ctx context.Context, tailnet, apiKey, policyFname, oldEtag stri
req.SetBasicAuth(apiKey, "")
req.Header.Set("Content-Type", "application/hujson")
req.Header.Set("If-Match", oldEtag)
req.Header.Set("If-Match", `"`+oldEtag+`"`)
resp, err := http.DefaultClient.Do(req)
if err != nil {
@@ -218,13 +265,16 @@ func applyNewACL(ctx context.Context, tailnet, apiKey, policyFname, oldEtag stri
}
func testNewACLs(ctx context.Context, tailnet, apiKey, policyFname string) error {
fin, err := os.Open(policyFname)
data, err := os.ReadFile(policyFname)
if err != nil {
return err
}
data, err = hujson.Standardize(data)
if err != nil {
return err
}
defer fin.Close()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("https://api.tailscale.com/api/v2/tailnet/%s/acl/validate", tailnet), fin)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("https://api.tailscale.com/api/v2/tailnet/%s/acl/validate", tailnet), bytes.NewBuffer(data))
if err != nil {
return err
}
@@ -251,15 +301,13 @@ func testNewACLs(ctx context.Context, tailnet, apiKey, policyFname string) error
got := resp.StatusCode
want := http.StatusOK
if got != want {
data, _ := httputil.DumpResponse(resp, true)
os.Stderr.Write(data)
return fmt.Errorf("wanted HTTP status code %d but got %d", want, got)
}
return nil
}
var lineColMessageSplit = regexp.MustCompile(`^line ([0-9]+), column ([0-9]+): (.*)$`)
var lineColMessageSplit = regexp.MustCompile(`line ([0-9]+), column ([0-9]+): (.*)$`)
type ACLTestError struct {
Message string `json:"message"`
@@ -318,5 +366,5 @@ func getACLETag(ctx context.Context, tailnet, apiKey string) (string, error) {
return "", fmt.Errorf("wanted HTTP status code %d but got %d", want, got)
}
return resp.Header.Get("ETag"), nil
return Shuck(resp.Header.Get("ETag")), nil
}

View File

@@ -13,7 +13,6 @@ import (
"errors"
"flag"
"html/template"
"io/ioutil"
"log"
"net/http"
"os"
@@ -106,7 +105,7 @@ func devMode() bool { return *httpsAddr == "" && *httpAddr != "" }
func getTmpl() (*template.Template, error) {
if devMode() {
tmplData, err := ioutil.ReadFile("hello.tmpl.html")
tmplData, err := os.ReadFile("hello.tmpl.html")
if os.IsNotExist(err) {
log.Printf("using baked-in template in dev mode; can't find hello.tmpl.html in current directory")
return tmpl, nil
@@ -135,13 +134,13 @@ func tailscaleIP(who *apitype.WhoIsResponse) string {
return ""
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.IP().Is4() && nodeIP.IsSingleIP() {
return nodeIP.IP().String()
if nodeIP.Addr().Is4() && nodeIP.IsSingleIP() {
return nodeIP.Addr().String()
}
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.IsSingleIP() {
return nodeIP.IP().String()
return nodeIP.Addr().String()
}
}
return ""

View File

@@ -63,17 +63,19 @@ func main() {
return
}
_, tailnet, ok := strings.Cut(info.Node.Name, info.Node.ComputedName+".")
if !ok {
w.WriteHeader(http.StatusUnauthorized)
log.Printf("can't extract tailnet name from hostname %q", info.Node.Name)
return
}
tailnet, _, ok = strings.Cut(tailnet, ".beta.tailscale.net")
if !ok {
w.WriteHeader(http.StatusUnauthorized)
log.Printf("can't extract tailnet name from hostname %q", info.Node.Name)
return
// tailnet of connected node. When accessing shared nodes, this
// will be empty because the tailnet of the sharee is not exposed.
var tailnet string
if !info.Node.Hostinfo.ShareeNode() {
var ok bool
_, tailnet, ok = strings.Cut(info.Node.Name, info.Node.ComputedName+".")
if !ok {
w.WriteHeader(http.StatusUnauthorized)
log.Printf("can't extract tailnet name from hostname %q", info.Node.Name)
return
}
tailnet = strings.TrimSuffix(tailnet, ".beta.tailscale.net")
}
if expectedTailnet := r.Header.Get("Expected-Tailnet"); expectedTailnet != "" && expectedTailnet != tailnet {

View File

@@ -14,14 +14,14 @@
//
// Use this Grafana configuration to enable the auth proxy:
//
// [auth.proxy]
// enabled = true
// header_name = X-WEBAUTH-USER
// header_property = username
// auto_sign_up = true
// whitelist = 127.0.0.1
// headers = Name:X-WEBAUTH-NAME
// enable_login_token = true
// [auth.proxy]
// enabled = true
// header_name = X-WEBAUTH-USER
// header_property = username
// auto_sign_up = true
// whitelist = 127.0.0.1
// headers = Name:X-WEBAUTH-NAME
// enable_login_token = true
package main
import (

View File

@@ -110,11 +110,12 @@ func runSpeedtest(ctx context.Context, args []string) error {
w := tabwriter.NewWriter(os.Stdout, 12, 0, 0, ' ', tabwriter.TabIndent)
fmt.Println("Results:")
fmt.Fprintln(w, "Interval\t\tTransfer\t\tBandwidth\t\t")
startTime := results[0].IntervalStart
for _, r := range results {
if r.Total {
fmt.Fprintln(w, "-------------------------------------------------------------------------")
}
fmt.Fprintf(w, "%.2f-%.2f\tsec\t%.4f\tMBits\t%.4f\tMbits/sec\t\n", r.IntervalStart.Seconds(), r.IntervalEnd.Seconds(), r.MegaBits(), r.MBitsPerSecond())
fmt.Fprintf(w, "%.2f-%.2f\tsec\t%.4f\tMBits\t%.4f\tMbits/sec\t\n", r.IntervalStart.Sub(startTime).Seconds(), r.IntervalEnd.Sub(startTime).Seconds(), r.MegaBits(), r.MBitsPerSecond())
}
w.Flush()
return nil

View File

@@ -29,7 +29,7 @@ var certCmd = &ffcli.Command{
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("cert")
fs.StringVar(&certArgs.certFile, "cert-file", "", "output cert file or \"-\" for stdout; defaults to DOMAIN.crt if --cert-file and --key-file are both unset")
fs.StringVar(&certArgs.keyFile, "key-file", "", "output cert file or \"-\" for stdout; defaults to DOMAIN.key if --cert-file and --key-file are both unset")
fs.StringVar(&certArgs.keyFile, "key-file", "", "output key file or \"-\" for stdout; defaults to DOMAIN.key if --cert-file and --key-file are both unset")
fs.BoolVar(&certArgs.serve, "serve-demo", false, "if true, serve on port :443 using the cert as a demo, instead of writing out the files to disk")
return fs
})(),

View File

@@ -29,7 +29,6 @@ import (
"tailscale.com/ipn"
"tailscale.com/paths"
"tailscale.com/safesocket"
"tailscale.com/syncs"
"tailscale.com/version/distro"
)
@@ -170,6 +169,8 @@ change in the future.
fileCmd,
bugReportCmd,
certCmd,
netlockCmd,
licensesCmd,
},
FlagSet: rootfs,
Exec: func(context.Context, []string) error { return flag.ErrHelp },
@@ -230,8 +231,6 @@ var rootArgs struct {
socket string
}
var gotSignal syncs.AtomicBool
func connect(ctx context.Context) (net.Conn, *ipn.BackendClient, context.Context, context.CancelFunc) {
s := safesocket.DefaultConnectionStrategy(rootArgs.socket)
c, err := safesocket.Connect(s)
@@ -257,7 +256,6 @@ func connect(ctx context.Context) (net.Conn, *ipn.BackendClient, context.Context
signal.Reset(syscall.SIGINT, syscall.SIGTERM)
return
}
gotSignal.Set(true)
c.Close()
cancel()
}()

View File

@@ -9,13 +9,13 @@ import (
"encoding/json"
"flag"
"fmt"
"net/netip"
"reflect"
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/google/go-cmp/cmp"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tstest"
@@ -56,7 +56,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags []string // argv to be parsed by FlagSet
curPrefs *ipn.Prefs
curExitNodeIP netaddr.IP
curExitNodeIP netip.Addr
curUser string // os.Getenv("USER") on the client side
goos string // empty means "linux"
distro distro.Distro
@@ -152,10 +152,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
},
},
want: accidentalUpPrefix + " --advertise-routes=10.0.42.0/24 --advertise-exit-node",
@@ -168,10 +168,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
},
},
want: "",
@@ -184,10 +184,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
},
},
want: "",
@@ -212,8 +212,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("1.2.0.0/16"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -226,10 +226,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -255,16 +255,16 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL,
RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netaddr.MustParseIP("100.64.5.6"),
ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
CorpDNS: false,
ShieldsUp: true,
AdvertiseTags: []string{"tag:foo", "tag:bar"},
Hostname: "myhostname",
ForceDaemon: true,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.0.0/16"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/16"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
},
NetfilterMode: preftype.NetfilterNoDivert,
OperatorUser: "alice",
@@ -280,14 +280,14 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL,
RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netaddr.MustParseIP("100.64.5.6"),
ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
CorpDNS: false,
ShieldsUp: true,
AdvertiseTags: []string{"tag:foo", "tag:bar"},
Hostname: "myhostname",
ForceDaemon: true,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.0.0/16"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/16"),
},
NetfilterMode: preftype.NetfilterNoDivert,
OperatorUser: "alice",
@@ -344,10 +344,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --operator=expbits --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -360,10 +360,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-routes=1.2.0.0/16 --operator=expbits --advertise-exit-node",
@@ -391,14 +391,14 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
ExitNodeIP: netaddr.MustParseIP("100.64.5.4"),
ExitNodeIP: netip.MustParseAddr("100.64.5.4"),
},
want: accidentalUpPrefix + " --hostname=foo --exit-node=100.64.5.4",
},
{
name: "error_exit_node_omit_with_id_pref",
flags: []string{"--hostname=foo"},
curExitNodeIP: netaddr.MustParseIP("100.64.5.7"),
curExitNodeIP: netip.MustParseAddr("100.64.5.7"),
curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
@@ -412,7 +412,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
{
name: "error_exit_node_and_allow_lan_omit_with_id_pref", // Isue 3480
flags: []string{"--hostname=foo"},
curExitNodeIP: netaddr.MustParseIP("100.2.3.4"),
curExitNodeIP: netip.MustParseAddr("100.2.3.4"),
curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
@@ -562,9 +562,9 @@ func TestPrefsFromUpArgs(t *testing.T) {
WantRunning: true,
AllowSingleHosts: true,
CorpDNS: true,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
},
NetfilterMode: preftype.NetfilterOn,
},
@@ -631,7 +631,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
exitNodeIP: "100.105.106.107",
},
st: &ipnstate.Status{
TailscaleIPs: []netaddr.IP{netaddr.MustParseIP("100.105.106.107")},
TailscaleIPs: []netip.Addr{netip.MustParseAddr("100.105.106.107")},
},
wantErr: `cannot use 100.105.106.107 as an exit node as it is a local IP address to this machine; did you mean --advertise-exit-node?`,
},
@@ -671,8 +671,8 @@ func TestPrefsFromUpArgs(t *testing.T) {
want: &ipn.Prefs{
WantRunning: true,
NoSNAT: true,
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
},
},
},
@@ -762,6 +762,9 @@ func TestPrefFlagMapping(t *testing.T) {
case "NotepadURLs":
// TODO(bradfitz): https://github.com/tailscale/tailscale/issues/1830
continue
case "Egg":
// Not applicable.
continue
}
t.Errorf("unexpected new ipn.Pref field %q is not handled by up.go (see addPrefFlagMapping and checkForAccidentalSettingReverts)", prefName)
}
@@ -786,6 +789,10 @@ func TestUpdatePrefs(t *testing.T) {
curPrefs *ipn.Prefs
env upCheckEnv // empty goos means "linux"
// sshOverTailscale specifies if the cmd being run over SSH over Tailscale.
// It is used to test the --accept-risks flag.
sshOverTailscale bool
// checkUpdatePrefsMutations, if non-nil, is run with the new prefs after
// updatePrefs might've mutated them (from applyImplicitPrefs).
checkUpdatePrefsMutations func(t *testing.T, newPrefs *ipn.Prefs)
@@ -913,15 +920,159 @@ func TestUpdatePrefs(t *testing.T) {
}
},
},
{
name: "enable_ssh",
flags: []string{"--ssh"},
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if !newPrefs.RunSSH {
t.Errorf("RunSSH not set to true")
}
},
env: upCheckEnv{backendState: "Running"},
},
{
name: "disable_ssh",
flags: []string{"--ssh=false"},
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
RunSSH: true,
NetfilterMode: preftype.NetfilterOn,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if newPrefs.RunSSH {
t.Errorf("RunSSH not set to false")
}
},
env: upCheckEnv{backendState: "Running", upArgs: upArgsT{
runSSH: true,
}},
},
{
name: "disable_ssh_over_ssh_no_risk",
flags: []string{"--ssh=false"},
sshOverTailscale: true,
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
RunSSH: true,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if !newPrefs.RunSSH {
t.Errorf("RunSSH not set to true")
}
},
env: upCheckEnv{backendState: "Running"},
wantErrSubtr: "aborted, no changes made",
},
{
name: "enable_ssh_over_ssh_no_risk",
flags: []string{"--ssh=true"},
sshOverTailscale: true,
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if !newPrefs.RunSSH {
t.Errorf("RunSSH not set to true")
}
},
env: upCheckEnv{backendState: "Running"},
wantErrSubtr: "aborted, no changes made",
},
{
name: "enable_ssh_over_ssh",
flags: []string{"--ssh=true", "--accept-risk=lose-ssh"},
sshOverTailscale: true,
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if !newPrefs.RunSSH {
t.Errorf("RunSSH not set to true")
}
},
env: upCheckEnv{backendState: "Running"},
},
{
name: "disable_ssh_over_ssh",
flags: []string{"--ssh=false", "--accept-risk=lose-ssh"},
sshOverTailscale: true,
curPrefs: &ipn.Prefs{
ControlURL: "https://login.tailscale.com",
Persist: &persist.Persist{LoginName: "crawshaw.github"},
AllowSingleHosts: true,
CorpDNS: true,
RunSSH: true,
NetfilterMode: preftype.NetfilterOn,
},
wantJustEditMP: &ipn.MaskedPrefs{
RunSSHSet: true,
WantRunningSet: true,
},
checkUpdatePrefsMutations: func(t *testing.T, newPrefs *ipn.Prefs) {
if newPrefs.RunSSH {
t.Errorf("RunSSH not set to false")
}
},
env: upCheckEnv{backendState: "Running"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.sshOverTailscale {
old := getSSHClientEnvVar
getSSHClientEnvVar = func() string { return "100.100.100.100 1 1" }
t.Cleanup(func() { getSSHClientEnvVar = old })
}
if tt.env.goos == "" {
tt.env.goos = "linux"
}
tt.env.flagSet = newUpFlagSet(tt.env.goos, &tt.env.upArgs)
flags := CleanUpArgs(tt.flags)
tt.env.flagSet.Parse(flags)
if err := tt.env.flagSet.Parse(flags); err != nil {
t.Fatal(err)
}
newPrefs, err := prefsFromUpArgs(tt.env.upArgs, t.Logf, new(ipnstate.Status), tt.env.goos)
if err != nil {
@@ -936,6 +1087,8 @@ func TestUpdatePrefs(t *testing.T) {
return
}
t.Fatal(err)
} else if tt.wantErrSubtr != "" {
t.Fatalf("want error %q, got nil", tt.wantErrSubtr)
}
if tt.checkUpdatePrefsMutations != nil {
tt.checkUpdatePrefsMutations(t, newPrefs)
@@ -949,14 +1102,19 @@ func TestUpdatePrefs(t *testing.T) {
justEditMP.Prefs = ipn.Prefs{} // uninteresting
}
if !reflect.DeepEqual(justEditMP, tt.wantJustEditMP) {
t.Logf("justEditMP != wantJustEditMP; following diff omits the Prefs field, which was %+v", oldEditPrefs)
t.Logf("justEditMP != wantJustEditMP; following diff omits the Prefs field, which was \n%v", asJSON(oldEditPrefs))
t.Fatalf("justEditMP: %v\n\n: ", cmp.Diff(justEditMP, tt.wantJustEditMP, cmpIP))
}
})
}
}
var cmpIP = cmp.Comparer(func(a, b netaddr.IP) bool {
func asJSON(v any) string {
b, _ := json.MarshalIndent(v, "", "\t")
return string(b)
}
var cmpIP = cmp.Comparer(func(a, b netip.Addr) bool {
return a == b
})

View File

@@ -48,11 +48,11 @@ func runConfigureHost(ctx context.Context, args []string) error {
if uid := os.Getuid(); uid != 0 {
return fmt.Errorf("must be run as root, not %q (%v)", os.Getenv("USER"), uid)
}
osVer := hostinfo.GetOSVersion()
isDSM6 := strings.HasPrefix(osVer, "Synology 6")
isDSM7 := strings.HasPrefix(osVer, "Synology 7")
hi:= hostinfo.New()
isDSM6 := strings.HasPrefix(hi.DistroVersion, "6.")
isDSM7 := strings.HasPrefix(hi.DistroVersion, "7.")
if !isDSM6 && !isDSM7 {
return fmt.Errorf("unsupported DSM version %q", osVer)
return fmt.Errorf("unsupported DSM version %q", hi.DistroVersion)
}
if _, err := os.Stat("/dev/net/tun"); os.IsNotExist(err) {
if err := os.MkdirAll("/dev/net", 0755); err != nil {
@@ -62,11 +62,14 @@ func runConfigureHost(ctx context.Context, args []string) error {
return fmt.Errorf("creating /dev/net/tun: %v, %s", err, out)
}
}
if err := os.Chmod("/dev/net", 0755); err != nil {
return err
}
if err := os.Chmod("/dev/net/tun", 0666); err != nil {
return err
}
if isDSM6 {
fmt.Printf("/dev/net/tun exists and has permissions 0666. Skipping setcap on DSM6.\n")
printf("/dev/net/tun exists and has permissions 0666. Skipping setcap on DSM6.\n")
return nil
}
@@ -80,6 +83,6 @@ func runConfigureHost(ctx context.Context, args []string) error {
if out, err := exec.Command("/bin/setcap", "cap_net_admin,cap_net_raw+eip", daemonBin).CombinedOutput(); err != nil {
return fmt.Errorf("setcap: %v, %s", err, out)
}
fmt.Printf("Done. To restart Tailscale to use the new permissions, run:\n\n sudo synosystemctl restart pkgctl-Tailscale.service\n\n")
printf("Done. To restart Tailscale to use the new permissions, run:\n\n sudo synosystemctl restart pkgctl-Tailscale.service\n\n")
return nil
}

View File

@@ -17,6 +17,7 @@ import (
"log"
"net"
"net/http"
"net/netip"
"os"
"runtime"
"strconv"
@@ -24,7 +25,6 @@ import (
"time"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/control/controlhttp"
"tailscale.com/hostinfo"
"tailscale.com/ipn"
@@ -308,18 +308,18 @@ func runStat(ctx context.Context, args []string) error {
for _, a := range args {
fi, err := os.Lstat(a)
if err != nil {
fmt.Printf("%s: %v\n", a, err)
printf("%s: %v\n", a, err)
continue
}
fmt.Printf("%s: %v, %v\n", a, fi.Mode(), fi.Size())
printf("%s: %v, %v\n", a, fi.Mode(), fi.Size())
if fi.IsDir() {
ents, _ := os.ReadDir(a)
for i, ent := range ents {
if i == 25 {
fmt.Printf(" ...\n")
printf(" ...\n")
break
}
fmt.Printf(" - %s\n", ent.Name())
printf(" - %s\n", ent.Name())
}
}
}
@@ -404,23 +404,23 @@ func runVia(ctx context.Context, args []string) error {
default:
return errors.New("expect either <site-id> <v4-cidr> or <v6-route>")
case 1:
ipp, err := netaddr.ParseIPPrefix(args[0])
ipp, err := netip.ParsePrefix(args[0])
if err != nil {
return err
}
if !ipp.IP().Is6() {
if !ipp.Addr().Is6() {
return errors.New("with one argument, expect an IPv6 CIDR")
}
if !tsaddr.TailscaleViaRange().Contains(ipp.IP()) {
if !tsaddr.TailscaleViaRange().Contains(ipp.Addr()) {
return errors.New("not a via route")
}
if ipp.Bits() < 96 {
return errors.New("short length, want /96 or more")
}
v4 := tsaddr.UnmapVia(ipp.IP())
a := ipp.IP().As16()
v4 := tsaddr.UnmapVia(ipp.Addr())
a := ipp.Addr().As16()
siteID := binary.BigEndian.Uint32(a[8:12])
fmt.Printf("site %v (0x%x), %v\n", siteID, siteID, netaddr.IPPrefixFrom(v4, ipp.Bits()-96))
printf("site %v (0x%x), %v\n", siteID, siteID, netip.PrefixFrom(v4, ipp.Bits()-96))
case 2:
siteID, err := strconv.ParseUint(args[0], 0, 32)
if err != nil {
@@ -429,7 +429,7 @@ func runVia(ctx context.Context, args []string) error {
if siteID > 0xff {
return fmt.Errorf("site-id values over 255 are currently reserved")
}
ipp, err := netaddr.ParseIPPrefix(args[1])
ipp, err := netip.ParsePrefix(args[1])
if err != nil {
return err
}
@@ -437,7 +437,7 @@ func runVia(ctx context.Context, args []string) error {
if err != nil {
return err
}
fmt.Println(via)
outln(via)
}
return nil
}
@@ -489,7 +489,15 @@ func runTS2021(ctx context.Context, args []string) error {
return c, err
}
conn, err := controlhttp.Dial(ctx, net.JoinHostPort(ts2021Args.host, "80"), machinePrivate, keys.PublicKey, uint16(ts2021Args.version), dialFunc)
conn, err := (&controlhttp.Dialer{
Hostname: ts2021Args.host,
HTTPPort: "80",
HTTPSPort: "443",
MachineKey: machinePrivate,
ControlKey: keys.PublicKey,
ProtocolVersion: uint16(ts2021Args.version),
Dialer: dialFunc,
}).Dial(ctx)
log.Printf("controlhttp.Dial = %p, %v", conn, err)
if err != nil {
return err

View File

@@ -22,9 +22,13 @@ var downCmd = &ffcli.Command{
FlagSet: newDownFlagSet(),
}
var downArgs struct {
acceptedRisks string
}
func newDownFlagSet() *flag.FlagSet {
downf := newFlagSet("down")
registerAcceptRiskFlag(downf)
registerAcceptRiskFlag(downf, &downArgs.acceptedRisks)
return downf
}
@@ -34,7 +38,7 @@ func runDown(ctx context.Context, args []string) error {
}
if isSSHOverTailscale() {
if err := presentRiskToUser(riskLoseSSH, `You are connected over Tailscale; this action will disable Tailscale and result in your session disconnecting.`); err != nil {
if err := presentRiskToUser(riskLoseSSH, `You are connected over Tailscale; this action will disable Tailscale and result in your session disconnecting.`, downArgs.acceptedRisks); err != nil {
return err
}
}

View File

@@ -14,6 +14,7 @@ import (
"log"
"mime"
"net/http"
"net/netip"
"os"
"path"
"path/filepath"
@@ -23,7 +24,6 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"
"golang.org/x/time/rate"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/envknob"
"tailscale.com/ipn"
@@ -85,7 +85,7 @@ func runCp(ctx context.Context, args []string) error {
hadBrackets = true
target = strings.TrimSuffix(strings.TrimPrefix(target, "["), "]")
}
if ip, err := netaddr.ParseIP(target); err == nil && ip.Is6() && !hadBrackets {
if ip, err := netip.ParseAddr(target); err == nil && ip.Is6() && !hadBrackets {
return fmt.Errorf("an IPv6 literal must be written as [%s]", ip)
} else if hadBrackets && (err != nil || !ip.Is6()) {
return errors.New("unexpected brackets around target")
@@ -168,7 +168,7 @@ func runCp(ctx context.Context, args []string) error {
}
func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNodeID, isOffline bool, err error) {
ip, err := netaddr.ParseIP(ipStr)
ip, err := netip.ParseAddr(ipStr)
if err != nil {
return "", false, err
}
@@ -179,7 +179,7 @@ func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNode
for _, ft := range fts {
n := ft.Node
for _, a := range n.Addresses {
if a.IP() != ip {
if a.Addr() != ip {
continue
}
isOffline = n.Online != nil && !*n.Online
@@ -191,7 +191,7 @@ func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNode
// fileTargetErrorDetail returns a non-nil error saying why ip is an
// invalid file sharing target.
func fileTargetErrorDetail(ctx context.Context, ip netaddr.IP) error {
func fileTargetErrorDetail(ctx context.Context, ip netip.Addr) error {
found := false
if st, err := localClient.Status(ctx); err == nil && st.Self != nil {
for _, peer := range st.Peer {
@@ -281,7 +281,7 @@ func runCpTargets(ctx context.Context, args []string) error {
if detail != "" {
detail = "\t" + detail
}
printf("%s\t%s%s\n", n.Addresses[0].IP(), n.ComputedName, detail)
printf("%s\t%s%s\n", n.Addresses[0].Addr(), n.ComputedName, detail)
}
return nil
}

View File

@@ -7,7 +7,6 @@ package cli
import (
"context"
"errors"
"fmt"
"github.com/peterbourgon/ff/v3/ffcli"
)
@@ -29,6 +28,6 @@ func runIDToken(ctx context.Context, args []string) error {
return err
}
fmt.Println(tr.IDToken)
outln(tr.IDToken)
return nil
}

View File

@@ -9,9 +9,9 @@ import (
"errors"
"flag"
"fmt"
"net/netip"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
)
@@ -100,7 +100,7 @@ func runIP(ctx context.Context, args []string) error {
}
func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus, ok bool) {
ip, err := netaddr.ParseIP(ipStr)
ip, err := netip.ParseAddr(ipStr)
if err != nil {
return
}

View File

@@ -0,0 +1,45 @@
// 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 cli
import (
"context"
"runtime"
"github.com/peterbourgon/ff/v3/ffcli"
)
var licensesCmd = &ffcli.Command{
Name: "licenses",
ShortUsage: "licenses",
ShortHelp: "Get open source license information",
LongHelp: "Get open source license information",
Exec: runLicenses,
}
// licensesURL returns the absolute URL containing open source license information for the current platform.
func licensesURL() string {
switch runtime.GOOS {
case "android":
return "https://tailscale.com/licenses/android"
case "darwin", "ios":
return "https://tailscale.com/licenses/apple"
case "windows":
return "https://tailscale.com/licenses/windows"
default:
return "https://tailscale.com/licenses/tailscale"
}
}
func runLicenses(ctx context.Context, args []string) error {
licenses := licensesURL()
outln(`
Tailscale wouldn't be possible without the contributions of thousands of open
source developers. To see the open source packages included in Tailscale and
their respective license information, visit:
` + licenses)
return nil
}

View File

@@ -10,7 +10,6 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"sort"
@@ -134,6 +133,9 @@ func printReport(dm *tailcfg.DERPMap, report *netcheck.Report) error {
printf("\t* MappingVariesByDestIP: %v\n", report.MappingVariesByDestIP)
printf("\t* HairPinning: %v\n", report.HairPinning)
printf("\t* PortMapping: %v\n", portMapping(report))
if report.CaptivePortal != "" {
printf("\t* CaptivePortal: %v\n", report.CaptivePortal)
}
// When DERP latency checking failed,
// magicsock will try to pick the DERP server that
@@ -202,7 +204,7 @@ func prodDERPMap(ctx context.Context, httpc *http.Client) (*tailcfg.DERPMap, err
return nil, fmt.Errorf("fetch prodDERPMap failed: %w", err)
}
defer res.Body.Close()
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
b, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
if err != nil {
return nil, fmt.Errorf("fetch prodDERPMap failed: %w", err)
}

View File

@@ -0,0 +1,101 @@
// 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 cli
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/tka"
"tailscale.com/types/key"
)
var netlockCmd = &ffcli.Command{
Name: "lock",
ShortUsage: "lock <sub-command> <arguments>",
ShortHelp: "Manipulate the tailnet key authority",
Subcommands: []*ffcli.Command{nlInitCmd, nlStatusCmd},
Exec: runNetworkLockStatus,
}
var nlInitCmd = &ffcli.Command{
Name: "init",
ShortUsage: "init <public-key>...",
ShortHelp: "Initialize the tailnet key authority",
Exec: runNetworkLockInit,
}
func runNetworkLockInit(ctx context.Context, args []string) error {
st, err := localClient.NetworkLockStatus(ctx)
if err != nil {
return fixTailscaledConnectError(err)
}
if st.Enabled {
return errors.New("network-lock is already enabled")
}
// Parse the set of initially-trusted keys.
// Keys are specified using their key.NLPublic.MarshalText representation,
// with an optional '?<votes>' suffix.
var keys []tka.Key
for i, a := range args {
var key key.NLPublic
spl := strings.SplitN(a, "?", 2)
if err := key.UnmarshalText([]byte(spl[0])); err != nil {
return fmt.Errorf("parsing key %d: %v", i+1, err)
}
k := tka.Key{
Kind: tka.Key25519,
Public: key.Verifier(),
Votes: 1,
}
if len(spl) > 1 {
votes, err := strconv.Atoi(spl[1])
if err != nil {
return fmt.Errorf("parsing key %d votes: %v", i+1, err)
}
k.Votes = uint(votes)
}
keys = append(keys, k)
}
status, err := localClient.NetworkLockInit(ctx, keys)
if err != nil {
return err
}
fmt.Printf("Status: %+v\n\n", status)
return nil
}
var nlStatusCmd = &ffcli.Command{
Name: "status",
ShortUsage: "status",
ShortHelp: "Outputs the state of network lock",
Exec: runNetworkLockStatus,
}
func runNetworkLockStatus(ctx context.Context, args []string) error {
st, err := localClient.NetworkLockStatus(ctx)
if err != nil {
return fixTailscaledConnectError(err)
}
if st.Enabled {
fmt.Println("Network-lock is ENABLED.")
} else {
fmt.Println("Network-lock is NOT enabled.")
}
p, err := st.PublicKey.MarshalText()
if err != nil {
return err
}
fmt.Printf("our public-key: %s\n", p)
return nil
}

View File

@@ -11,12 +11,12 @@ import (
"fmt"
"log"
"net"
"net/netip"
"os"
"strings"
"time"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
)
@@ -116,7 +116,7 @@ func runPing(ctx context.Context, args []string) error {
for {
n++
ctx, cancel := context.WithTimeout(ctx, pingArgs.timeout)
pr, err := localClient.Ping(ctx, netaddr.MustParseIP(ip), pingType())
pr, err := localClient.Ping(ctx, netip.MustParseAddr(ip), pingType())
cancel()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {

View File

@@ -16,9 +16,8 @@ import (
)
var (
riskTypes []string
acceptedRisks string
riskLoseSSH = registerRiskType("lose-ssh")
riskTypes []string
riskLoseSSH = registerRiskType("lose-ssh")
)
func registerRiskType(riskType string) string {
@@ -28,12 +27,13 @@ func registerRiskType(riskType string) string {
// registerAcceptRiskFlag registers the --accept-risk flag. Accepted risks are accounted for
// in presentRiskToUser.
func registerAcceptRiskFlag(f *flag.FlagSet) {
f.StringVar(&acceptedRisks, "accept-risk", "", "accept risk and skip confirmation for risk types: "+strings.Join(riskTypes, ","))
func registerAcceptRiskFlag(f *flag.FlagSet, acceptedRisks *string) {
f.StringVar(acceptedRisks, "accept-risk", "", "accept risk and skip confirmation for risk types: "+strings.Join(riskTypes, ","))
}
// riskAccepted reports whether riskType is in acceptedRisks.
func riskAccepted(riskType string) bool {
// isRiskAccepted reports whether riskType is in the comma-separated list of
// risks in acceptedRisks.
func isRiskAccepted(riskType, acceptedRisks string) bool {
for _, r := range strings.Split(acceptedRisks, ",") {
if r == riskType {
return true
@@ -49,14 +49,18 @@ var errAborted = errors.New("aborted, no changes made")
// It is used by the presentRiskToUser function below.
const riskAbortTimeSeconds = 5
// presentRiskToUser displays the risk message and waits for the user to
// cancel. It returns errorAborted if the user aborts.
func presentRiskToUser(riskType, riskMessage string) error {
if riskAccepted(riskType) {
// presentRiskToUser displays the risk message and waits for the user to cancel.
// It returns errorAborted if the user aborts. In tests it returns errAborted
// immediately unless the risk has been explicitly accepted.
func presentRiskToUser(riskType, riskMessage, acceptedRisks string) error {
if isRiskAccepted(riskType, acceptedRisks) {
return nil
}
fmt.Println(riskMessage)
fmt.Printf("To skip this warning, use --accept-risk=%s\n", riskType)
if inTest() {
return errAborted
}
outln(riskMessage)
printf("To skip this warning, use --accept-risk=%s\n", riskType)
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, syscall.SIGINT)
@@ -64,15 +68,15 @@ func presentRiskToUser(riskType, riskMessage string) error {
for left := riskAbortTimeSeconds; left > 0; left-- {
msg := fmt.Sprintf("\rContinuing in %d seconds...", left)
msgLen = len(msg)
fmt.Print(msg)
printf(msg)
select {
case <-interrupt:
fmt.Printf("\r%s\r", strings.Repeat(" ", msgLen+1))
printf("\r%s\r", strings.Repeat("x", msgLen+1))
return errAborted
case <-time.After(time.Second):
continue
}
}
fmt.Printf("\r%s\r", strings.Repeat(" ", msgLen))
printf("\r%s\r", strings.Repeat(" ", msgLen))
return errAborted
}

View File

@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"log"
"net/netip"
"os"
"os/user"
"path/filepath"
@@ -17,7 +18,6 @@ import (
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
@@ -163,10 +163,10 @@ func nodeDNSNameFromArg(st *ipnstate.Status, arg string) (dnsName string, ok boo
if arg == "" {
return
}
argIP, _ := netaddr.ParseIP(arg)
argIP, _ := netip.ParseAddr(arg)
for _, ps := range st.Peer {
dnsName = ps.DNSName
if !argIP.IsZero() {
if argIP.IsValid() {
for _, ip := range ps.TailscaleIPs {
if ip == argIP {
return dnsName, true
@@ -202,7 +202,7 @@ func isSSHOverTailscale() bool {
if !ok {
return false
}
ip, err := netaddr.ParseIP(ipStr)
ip, err := netip.ParseAddr(ipStr)
if err != nil {
return false
}

View File

@@ -13,12 +13,12 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"os"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/toqueteos/webbrowser"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
@@ -260,7 +260,7 @@ func ownerLogin(st *ipnstate.Status, ps *ipnstate.PeerStatus) string {
return u.LoginName
}
func firstIPString(v []netaddr.IP) string {
func firstIPString(v []netip.Addr) string {
if len(v) == 0 {
return ""
}

View File

@@ -13,6 +13,7 @@ import (
"flag"
"fmt"
"log"
"net/netip"
"os"
"reflect"
"runtime"
@@ -24,7 +25,6 @@ import (
shellquote "github.com/kballard/go-shellquote"
"github.com/peterbourgon/ff/v3/ffcli"
qrcode "github.com/skip2/go-qrcode"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
@@ -116,7 +116,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
upf.BoolVar(&upArgs.forceDaemon, "unattended", false, "run in \"Unattended Mode\" where Tailscale keeps running even after the current GUI user logs out (Windows-only)")
}
upf.DurationVar(&upArgs.timeout, "timeout", 0, "maximum amount of time to wait for tailscaled to enter a Running state; default (0s) blocks forever")
registerAcceptRiskFlag(upf)
registerAcceptRiskFlag(upf, &upArgs.acceptedRisks)
return upf
}
@@ -150,6 +150,7 @@ type upArgsT struct {
opUser string
json bool
timeout time.Duration
acceptedRisks string
}
func (a upArgsT) getAuthKey() (string, error) {
@@ -178,15 +179,16 @@ var upArgs upArgsT
// JSON block will be output. The AuthURL and QR fields will not be present, the
// BackendState and Error fields will give the result of the authentication.
// Ex:
// {
// "AuthURL": "https://login.tailscale.com/a/0123456789abcdef",
// "QR": "data:image/png;base64,0123...cdef"
// "BackendState": "NeedsLogin"
// }
// {
// "BackendState": "Running"
// }
//
// {
// "AuthURL": "https://login.tailscale.com/a/0123456789abcdef",
// "QR": "data:image/png;base64,0123...cdef"
// "BackendState": "NeedsLogin"
// }
//
// {
// "BackendState": "Running"
// }
type upOutputJSON struct {
AuthURL string `json:",omitempty"` // Authentication URL of the form https://login.tailscale.com/a/0123456789
QR string `json:",omitempty"` // a DataURL (base64) PNG of a QR code AuthURL
@@ -199,18 +201,18 @@ func warnf(format string, args ...any) {
}
var (
ipv4default = netaddr.MustParseIPPrefix("0.0.0.0/0")
ipv6default = netaddr.MustParseIPPrefix("::/0")
ipv4default = netip.MustParsePrefix("0.0.0.0/0")
ipv6default = netip.MustParsePrefix("::/0")
)
func validateViaPrefix(ipp netaddr.IPPrefix) error {
func validateViaPrefix(ipp netip.Prefix) error {
if !tsaddr.IsViaPrefix(ipp) {
return fmt.Errorf("%v is not a 4-in-6 prefix", ipp)
}
if ipp.Bits() < (128 - 32) {
return fmt.Errorf("%v 4-in-6 prefix must be at least a /%v", ipp, 128-32)
}
a := ipp.IP().As16()
a := ipp.Addr().As16()
// The first 64 bits of a are the via prefix.
// The next 32 bits are the "site ID".
// The last 32 bits are the IPv4.
@@ -223,13 +225,13 @@ func validateViaPrefix(ipp netaddr.IPPrefix) error {
return nil
}
func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]netaddr.IPPrefix, error) {
routeMap := map[netaddr.IPPrefix]bool{}
func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]netip.Prefix, error) {
routeMap := map[netip.Prefix]bool{}
if advertiseRoutes != "" {
var default4, default6 bool
advroutes := strings.Split(advertiseRoutes, ",")
for _, s := range advroutes {
ipp, err := netaddr.ParseIPPrefix(s)
ipp, err := netip.ParsePrefix(s)
if err != nil {
return nil, fmt.Errorf("%q is not a valid IP address or CIDR prefix", s)
}
@@ -251,14 +253,14 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
if default4 && !default6 {
return nil, fmt.Errorf("%s advertised without its IPv6 counterpart, please also advertise %s", ipv4default, ipv6default)
} else if default6 && !default4 {
return nil, fmt.Errorf("%s advertised without its IPv6 counterpart, please also advertise %s", ipv6default, ipv4default)
return nil, fmt.Errorf("%s advertised without its IPv4 counterpart, please also advertise %s", ipv6default, ipv4default)
}
}
if advertiseDefaultRoute {
routeMap[netaddr.MustParseIPPrefix("0.0.0.0/0")] = true
routeMap[netaddr.MustParseIPPrefix("::/0")] = true
routeMap[netip.MustParsePrefix("0.0.0.0/0")] = true
routeMap[netip.MustParsePrefix("::/0")] = true
}
routes := make([]netaddr.IPPrefix, 0, len(routeMap))
routes := make([]netip.Prefix, 0, len(routeMap))
for r := range routeMap {
routes = append(routes, r)
}
@@ -266,7 +268,7 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
if routes[i].Bits() != routes[j].Bits() {
return routes[i].Bits() < routes[j].Bits()
}
return routes[i].IP().Less(routes[j].IP())
return routes[i].Addr().Less(routes[j].Addr())
})
return routes, nil
}
@@ -375,6 +377,20 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
return false, nil, fmt.Errorf("can't change --login-server without --force-reauth")
}
// Do this after validations to avoid the 5s delay if we're going to error
// out anyway.
wantSSH, haveSSH := env.upArgs.runSSH, curPrefs.RunSSH
if wantSSH != haveSSH && isSSHOverTailscale() {
if wantSSH {
err = presentRiskToUser(riskLoseSSH, `You are connected over Tailscale; this action will reroute SSH traffic to Tailscale SSH and will result in your session disconnecting.`, env.upArgs.acceptedRisks)
} else {
err = presentRiskToUser(riskLoseSSH, `You are connected using Tailscale SSH; this action will result in your session disconnecting.`, env.upArgs.acceptedRisks)
}
if err != nil {
return false, nil, err
}
}
tagsChanged := !reflect.DeepEqual(curPrefs.AdvertiseTags, prefs.AdvertiseTags)
simpleUp = env.flagSet.NFlag() == 0 &&
@@ -405,8 +421,12 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
}
func runUp(ctx context.Context, args []string) (retErr error) {
var egg bool
if len(args) > 0 {
fatalf("too many non-flag arguments: %q", args)
egg = fmt.Sprint(args) == "[up down down left right left right b a]"
if !egg {
fatalf("too many non-flag arguments: %q", args)
}
}
st, err := localClient.Status(ctx)
@@ -470,17 +490,6 @@ func runUp(ctx context.Context, args []string) (retErr error) {
curExitNodeIP: exitNodeIP(curPrefs, st),
}
if upArgs.runSSH != curPrefs.RunSSH && isSSHOverTailscale() {
if upArgs.runSSH {
err = presentRiskToUser(riskLoseSSH, `You are connected over Tailscale; this action will reroute SSH traffic to Tailscale SSH and will result in your session disconnecting.`)
} else {
err = presentRiskToUser(riskLoseSSH, `You are connected using Tailscale SSH; this action will result in your session disconnecting.`)
}
if err != nil {
return err
}
}
defer func() {
if retErr == nil {
checkSSHUpWarnings(ctx)
@@ -492,6 +501,7 @@ func runUp(ctx context.Context, args []string) (retErr error) {
fatalf("%s", err)
}
if justEditMP != nil {
justEditMP.EggSet = true
_, err := localClient.EditPrefs(ctx, justEditMP)
return err
}
@@ -570,9 +580,9 @@ func runUp(ctx context.Context, args []string) (retErr error) {
data, err := json.MarshalIndent(js, "", "\t")
if err != nil {
log.Printf("upOutputJSON marshalling error: %v", err)
printf("upOutputJSON marshalling error: %v", err)
} else {
fmt.Println(string(data))
outln(string(data))
}
} else {
fmt.Fprintf(Stderr, "\nTo authenticate, visit:\n\n\t%s\n\n", *url)
@@ -710,7 +720,7 @@ func printUpDoneJSON(state ipn.State, errorString string) {
if err != nil {
log.Printf("printUpDoneJSON marshalling error: %v", err)
} else {
fmt.Println(string(data))
outln(string(data))
}
}
@@ -790,7 +800,7 @@ type upCheckEnv struct {
flagSet *flag.FlagSet
upArgs upArgsT
backendState string
curExitNodeIP netaddr.IP
curExitNodeIP netip.Addr
distro distro.Distro
}
@@ -913,10 +923,10 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]any) {
ret := make(map[string]any)
exitNodeIPStr := func() string {
if !prefs.ExitNodeIP.IsZero() {
if prefs.ExitNodeIP.IsValid() {
return prefs.ExitNodeIP.String()
}
if prefs.ExitNodeID.IsZero() || env.curExitNodeIP.IsZero() {
if prefs.ExitNodeID.IsZero() || !env.curExitNodeIP.IsValid() {
return ""
}
return env.curExitNodeIP.String()
@@ -991,13 +1001,13 @@ func fmtFlagValueArg(flagName string, val any) string {
return fmt.Sprintf("--%s=%v", flagName, shellquote.Join(fmt.Sprint(val)))
}
func hasExitNodeRoutes(rr []netaddr.IPPrefix) bool {
func hasExitNodeRoutes(rr []netip.Prefix) bool {
var v4, v6 bool
for _, r := range rr {
if r.Bits() == 0 {
if r.IP().Is4() {
if r.Addr().Is4() {
v4 = true
} else if r.IP().Is6() {
} else if r.Addr().Is6() {
v6 = true
}
}
@@ -1008,11 +1018,11 @@ func hasExitNodeRoutes(rr []netaddr.IPPrefix) bool {
// withoutExitNodes returns rr unchanged if it has only 1 or 0 /0
// routes. If it has both IPv4 and IPv6 /0 routes, then it returns
// a copy with all /0 routes removed.
func withoutExitNodes(rr []netaddr.IPPrefix) []netaddr.IPPrefix {
func withoutExitNodes(rr []netip.Prefix) []netip.Prefix {
if !hasExitNodeRoutes(rr) {
return rr
}
var out []netaddr.IPPrefix
var out []netip.Prefix
for _, r := range rr {
if r.Bits() > 0 {
out = append(out, r)
@@ -1023,11 +1033,11 @@ func withoutExitNodes(rr []netaddr.IPPrefix) []netaddr.IPPrefix {
// exitNodeIP returns the exit node IP from p, using st to map
// it from its ID form to an IP address if needed.
func exitNodeIP(p *ipn.Prefs, st *ipnstate.Status) (ip netaddr.IP) {
func exitNodeIP(p *ipn.Prefs, st *ipnstate.Status) (ip netip.Addr) {
if p == nil {
return
}
if !p.ExitNodeIP.IsZero() {
if p.ExitNodeIP.IsValid() {
return p.ExitNodeIP
}
id := p.ExitNodeID

View File

@@ -15,11 +15,11 @@ import (
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/cgi"
"net/netip"
"net/url"
"os"
"os/exec"
@@ -27,7 +27,6 @@ import (
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/tailcfg"
"tailscale.com/types/preftype"
@@ -59,6 +58,7 @@ type tmplData struct {
IP string
AdvertiseExitNode bool
AdvertiseRoutes string
LicensesURL string
}
var webCmd = &ffcli.Command{
@@ -253,7 +253,7 @@ func qnapAuthnFinish(user, url string) (string, *qnapAuthResponse, error) {
return "", nil, err
}
defer resp.Body.Close()
out, err := ioutil.ReadAll(resp.Body)
out, err := io.ReadAll(resp.Body)
if err != nil {
return "", nil, err
}
@@ -392,9 +392,10 @@ func webHandler(w http.ResponseWriter, r *http.Request) {
Profile: profile,
Status: st.BackendState,
DeviceName: deviceName,
LicensesURL: licensesURL(),
}
exitNodeRouteV4 := netaddr.MustParseIPPrefix("0.0.0.0/0")
exitNodeRouteV6 := netaddr.MustParseIPPrefix("::/0")
exitNodeRouteV4 := netip.MustParsePrefix("0.0.0.0/0")
exitNodeRouteV6 := netip.MustParsePrefix("::/0")
for _, r := range prefs.AdvertiseRoutes {
if r == exitNodeRouteV4 || r == exitNodeRouteV6 {
data.AdvertiseExitNode = true

View File

@@ -11,7 +11,7 @@
</head>
<body class="py-14">
<main class="container max-w-lg mx-auto py-6 px-8 bg-white rounded-md shadow-2xl" style="width: 95%">
<main class="container max-w-lg mx-auto mb-8 py-6 px-8 bg-white rounded-md shadow-2xl" style="width: 95%">
<header class="flex justify-between items-center min-width-0 py-2 mb-8">
<svg width="26" height="26" viewBox="0 0 23 23" title="Tailscale" fill="none" xmlns="http://www.w3.org/2000/svg"
class="flex-shrink-0 mr-4">
@@ -100,6 +100,9 @@
</div>
{{ end }}
</main>
<footer class="container max-w-lg mx-auto text-center">
<a class="text-xs text-gray-500 hover:text-gray-600" href="{{ .LicensesURL }}">Open Source Licenses</a>
</footer>
<script>(function () {
const advertiseExitNode = {{.AdvertiseExitNode}};
let fetchingUrl = false;

View File

@@ -1,9 +1,13 @@
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
@@ -26,11 +30,10 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
github.com/tailscale/goupnp/ssdp from github.com/tailscale/goupnp
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
💣 go4.org/intern from inet.af/netaddr
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/derp+
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
go4.org/netipx from tailscale.com/wgengine/filter
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
nhooyr.io/websocket from tailscale.com/derp/derphttp+
nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
@@ -54,12 +57,14 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/net/dnsfallback from tailscale.com/control/controlhttp
tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+
💣 tailscale.com/net/interfaces from tailscale.com/cmd/tailscale/cli+
tailscale.com/net/netaddr from tailscale.com/ipn+
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli
tailscale.com/net/neterror from tailscale.com/net/netcheck+
tailscale.com/net/netknob from tailscale.com/net/netns
tailscale.com/net/netns from tailscale.com/derp/derphttp+
tailscale.com/net/netutil from tailscale.com/client/tailscale+
tailscale.com/net/packet from tailscale.com/wgengine/filter
tailscale.com/net/ping 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/tlsdial from tailscale.com/derp/derphttp+
@@ -67,8 +72,9 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
💣 tailscale.com/net/tshttpproxy from tailscale.com/derp/derphttp+
tailscale.com/paths 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/netcheck+
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
tailscale.com/tka from tailscale.com/client/tailscale+
W tailscale.com/tsconst from tailscale.com/net/interfaces
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
tailscale.com/tstime/rate from tailscale.com/wgengine/filter
@@ -78,11 +84,13 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/types/key from tailscale.com/derp+
tailscale.com/types/logger from tailscale.com/cmd/tailscale/cli+
tailscale.com/types/netmap from tailscale.com/ipn
tailscale.com/types/nettype from tailscale.com/net/netcheck+
tailscale.com/types/opt from tailscale.com/net/netcheck+
tailscale.com/types/pad32 from tailscale.com/derp
tailscale.com/types/persist from tailscale.com/ipn
tailscale.com/types/preftype from tailscale.com/cmd/tailscale/cli+
tailscale.com/types/structs from tailscale.com/ipn+
tailscale.com/types/tkatype from tailscale.com/types/key+
tailscale.com/types/views from tailscale.com/tailcfg+
tailscale.com/util/clientmetric from tailscale.com/net/netcheck+
tailscale.com/util/cloudenv from tailscale.com/net/dnscache+
@@ -91,13 +99,17 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
W tailscale.com/util/endian from tailscale.com/net/netns
tailscale.com/util/groupmember from tailscale.com/cmd/tailscale/cli
tailscale.com/util/lineread from tailscale.com/net/interfaces+
tailscale.com/util/mak from tailscale.com/net/netcheck
tailscale.com/util/multierr from tailscale.com/control/controlhttp
tailscale.com/util/singleflight from tailscale.com/net/dnscache
L tailscale.com/util/strs from tailscale.com/hostinfo
W 💣 tailscale.com/util/winutil from tailscale.com/hostinfo+
tailscale.com/version 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
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/argon2 from tailscale.com/tka
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/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
@@ -107,12 +119,15 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
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/salsa20/salsa from golang.org/x/crypto/nacl/box+
L golang.org/x/net/bpf from github.com/mdlayher/netlink+
golang.org/x/net/bpf from github.com/mdlayher/netlink+
golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from net/http+
golang.org/x/net/http/httpproxy from net/http
golang.org/x/net/http2/hpack from net/http
golang.org/x/net/icmp from tailscale.com/net/ping
golang.org/x/net/idna from golang.org/x/net/http/httpguts+
golang.org/x/net/ipv4 from golang.org/x/net/icmp+
golang.org/x/net/ipv6 from golang.org/x/net/icmp
golang.org/x/net/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net+
golang.org/x/sync/errgroup from tailscale.com/derp+
@@ -155,6 +170,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
embed from tailscale.com/cmd/tailscale/cli+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from tailscale.com/tka
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/hex from crypto/x509+
@@ -175,7 +191,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
image/color from github.com/skip2/go-qrcode+
image/png from github.com/skip2/go-qrcode
io from bufio+
io/fs from crypto/rand+
io/fs from crypto/x509+
io/ioutil from golang.org/x/sys/cpu+
log from expvar+
math from compress/flate+
@@ -190,7 +206,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
net/http/cgi from tailscale.com/cmd/tailscale/cli
net/http/httptrace from github.com/tcnksm/go-httpstat+
net/http/internal from net/http
net/netip from net
net/netip from net+
net/textproto from golang.org/x/net/http/httpguts+
net/url from crypto/x509+
os from crypto/rand+

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package main
@@ -15,17 +15,16 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptrace"
"net/netip"
"net/url"
"os"
"strings"
"time"
"inet.af/netaddr"
"tailscale.com/derp/derphttp"
"tailscale.com/envknob"
"tailscale.com/ipn"
@@ -173,7 +172,7 @@ func checkDerp(ctx context.Context, derpRegion string) error {
return fmt.Errorf("fetch derp map failed: %w", err)
}
defer res.Body.Close()
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
b, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
if err != nil {
return fmt.Errorf("fetch derp map failed: %w", err)
}
@@ -266,11 +265,11 @@ func debugPortmap(ctx context.Context) error {
return err
}
gatewayAndSelfIP := func() (gw, self netaddr.IP, ok bool) {
gatewayAndSelfIP := func() (gw, self netip.Addr, ok bool) {
if v := os.Getenv("TS_DEBUG_GW_SELF"); strings.Contains(v, "/") {
i := strings.Index(v, "/")
gw = netaddr.MustParseIP(v[:i])
self = netaddr.MustParseIP(v[i+1:])
gw = netip.MustParseAddr(v[:i])
self = netip.MustParseAddr(v[i+1:])
return gw, self, true
}
return linkMon.GatewayAndSelfIP()

View File

@@ -1,5 +1,7 @@
tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
@@ -62,11 +64,13 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/smithy-go/waiter from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/coreos/go-iptables/iptables from tailscale.com/wgengine/router
LD 💣 github.com/creack/pty from tailscale.com/ssh/tailssh
github.com/fxamacker/cbor/v2 from tailscale.com/tka
W 💣 github.com/go-ole/go-ole from github.com/go-ole/go-ole/oleutil+
W 💣 github.com/go-ole/go-ole/oleutil from tailscale.com/wgengine/winnet
L 💣 github.com/godbus/dbus/v5 from tailscale.com/net/dns+
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/google/btree from gvisor.dev/gvisor/pkg/tcpip/header+
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/net/tstun
L github.com/insomniacslk/dhcp/iana from github.com/insomniacslk/dhcp/dhcpv4
L github.com/insomniacslk/dhcp/interfaces from github.com/insomniacslk/dhcp/dhcpv4
@@ -113,9 +117,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/u-root/uio/uio from github.com/insomniacslk/dhcp/dhcpv4+
L 💣 github.com/vishvananda/netlink/nl from github.com/tailscale/netlink
L github.com/vishvananda/netns from github.com/tailscale/netlink+
💣 go4.org/intern from inet.af/netaddr
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/mem from tailscale.com/control/controlbase+
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
go4.org/netipx from tailscale.com/ipn/ipnlocal+
W 💣 golang.zx2c4.com/wintun from golang.zx2c4.com/wireguard/tun
💣 golang.zx2c4.com/wireguard/conn from golang.zx2c4.com/wireguard/device+
W 💣 golang.zx2c4.com/wireguard/conn/winrio from golang.zx2c4.com/wireguard/conn
@@ -129,7 +133,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
💣 golang.zx2c4.com/wireguard/tun from golang.zx2c4.com/wireguard/device+
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/cmd/tailscaled+
gvisor.dev/gvisor/pkg/atomicbitops from gvisor.dev/gvisor/pkg/tcpip+
💣 gvisor.dev/gvisor/pkg/buffer from gvisor.dev/gvisor/pkg/tcpip/stack
gvisor.dev/gvisor/pkg/bits from gvisor.dev/gvisor/pkg/bufferv2
💣 gvisor.dev/gvisor/pkg/bufferv2 from gvisor.dev/gvisor/pkg/tcpip+
gvisor.dev/gvisor/pkg/context from gvisor.dev/gvisor/pkg/refs+
💣 gvisor.dev/gvisor/pkg/gohacks from gvisor.dev/gvisor/pkg/state/wire+
gvisor.dev/gvisor/pkg/linewriter from gvisor.dev/gvisor/pkg/log
@@ -143,7 +148,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
💣 gvisor.dev/gvisor/pkg/sync from gvisor.dev/gvisor/pkg/linewriter+
gvisor.dev/gvisor/pkg/tcpip from gvisor.dev/gvisor/pkg/tcpip/header+
gvisor.dev/gvisor/pkg/tcpip/adapters/gonet from tailscale.com/wgengine/netstack
💣 gvisor.dev/gvisor/pkg/tcpip/buffer from gvisor.dev/gvisor/pkg/tcpip/header+
gvisor.dev/gvisor/pkg/tcpip/hash/jenkins from gvisor.dev/gvisor/pkg/tcpip/stack+
gvisor.dev/gvisor/pkg/tcpip/header from gvisor.dev/gvisor/pkg/tcpip/header/parse+
gvisor.dev/gvisor/pkg/tcpip/header/parse from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+
@@ -152,6 +156,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
gvisor.dev/gvisor/pkg/tcpip/network/hash from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+
gvisor.dev/gvisor/pkg/tcpip/network/internal/fragmentation from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+
gvisor.dev/gvisor/pkg/tcpip/network/internal/ip from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+
gvisor.dev/gvisor/pkg/tcpip/network/internal/multicast from gvisor.dev/gvisor/pkg/tcpip/network/ipv4+
gvisor.dev/gvisor/pkg/tcpip/network/ipv4 from tailscale.com/net/tstun+
gvisor.dev/gvisor/pkg/tcpip/network/ipv6 from tailscale.com/wgengine/netstack
gvisor.dev/gvisor/pkg/tcpip/ports from gvisor.dev/gvisor/pkg/tcpip/stack+
@@ -167,7 +172,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
gvisor.dev/gvisor/pkg/tcpip/transport/tcpconntrack from gvisor.dev/gvisor/pkg/tcpip/stack
gvisor.dev/gvisor/pkg/tcpip/transport/udp from tailscale.com/net/tstun+
gvisor.dev/gvisor/pkg/waiter from gvisor.dev/gvisor/pkg/context+
inet.af/netaddr from tailscale.com/control/controlclient+
inet.af/peercred from tailscale.com/ipn/ipnserver
W 💣 inet.af/wf from tailscale.com/wf
nhooyr.io/websocket from tailscale.com/derp/derphttp+
@@ -208,13 +212,14 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/logtail/filch from tailscale.com/logpolicy
💣 tailscale.com/metrics from tailscale.com/derp+
tailscale.com/net/dns from tailscale.com/ipn/ipnlocal+
tailscale.com/net/dns/publicdns from tailscale.com/net/dns/resolver
tailscale.com/net/dns/publicdns from tailscale.com/net/dns/resolver+
tailscale.com/net/dns/resolvconffile from tailscale.com/net/dns+
tailscale.com/net/dns/resolver from tailscale.com/ipn/ipnlocal+
tailscale.com/net/dnscache from tailscale.com/control/controlclient+
tailscale.com/net/dnsfallback from tailscale.com/control/controlclient+
tailscale.com/net/flowtrack from tailscale.com/net/packet+
💣 tailscale.com/net/interfaces from tailscale.com/control/controlclient+
tailscale.com/net/netaddr from tailscale.com/ipn+
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock
tailscale.com/net/neterror from tailscale.com/net/dns/resolver+
tailscale.com/net/netknob from tailscale.com/net/netns+
@@ -222,6 +227,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
💣 tailscale.com/net/netstat from tailscale.com/ipn/ipnserver
tailscale.com/net/netutil from tailscale.com/ipn/ipnlocal+
tailscale.com/net/packet from tailscale.com/net/tstun+
tailscale.com/net/ping from tailscale.com/net/netcheck
tailscale.com/net/portmapper from tailscale.com/net/netcheck+
tailscale.com/net/proxymux from tailscale.com/cmd/tailscaled
tailscale.com/net/socks5 from tailscale.com/cmd/tailscaled
@@ -236,9 +242,10 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/safesocket from tailscale.com/client/tailscale+
tailscale.com/smallzstd from tailscale.com/ipn/ipnserver+
LD 💣 tailscale.com/ssh/tailssh from tailscale.com/cmd/tailscaled
tailscale.com/syncs from tailscale.com/control/controlknobs+
tailscale.com/syncs from tailscale.com/net/netcheck+
tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
tailscale.com/tka from tailscale.com/ipn/ipnlocal+
W tailscale.com/tsconst from tailscale.com/net/interfaces
tailscale.com/tstime from tailscale.com/wgengine/magicsock
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
@@ -251,12 +258,13 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/types/key from tailscale.com/control/controlbase+
tailscale.com/types/logger from tailscale.com/control/controlclient+
tailscale.com/types/netmap from tailscale.com/control/controlclient+
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock+
tailscale.com/types/opt from tailscale.com/control/controlclient+
tailscale.com/types/pad32 from tailscale.com/derp
tailscale.com/types/persist from tailscale.com/control/controlclient+
tailscale.com/types/preftype from tailscale.com/ipn+
tailscale.com/types/structs from tailscale.com/control/controlclient+
tailscale.com/types/tkatype from tailscale.com/tka+
tailscale.com/types/views from tailscale.com/ipn/ipnlocal+
tailscale.com/util/clientmetric from tailscale.com/control/controlclient+
tailscale.com/util/cloudenv from tailscale.com/net/dns/resolver+
@@ -265,14 +273,15 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/util/dnsname from tailscale.com/hostinfo+
LW tailscale.com/util/endian from tailscale.com/net/dns+
tailscale.com/util/groupmember from tailscale.com/ipn/ipnserver
💣 tailscale.com/util/hashx from tailscale.com/util/deephash
tailscale.com/util/lineread from tailscale.com/hostinfo+
tailscale.com/util/mak from tailscale.com/control/controlclient+
tailscale.com/util/multierr from tailscale.com/control/controlclient+
tailscale.com/util/netconv from tailscale.com/wgengine/magicsock
tailscale.com/util/osshare from tailscale.com/ipn/ipnlocal+
tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver
tailscale.com/util/racebuild from tailscale.com/logpolicy
tailscale.com/util/singleflight from tailscale.com/control/controlclient+
tailscale.com/util/strs from tailscale.com/hostinfo+
tailscale.com/util/systemd from tailscale.com/control/controlclient+
tailscale.com/util/uniq from tailscale.com/wgengine/magicsock
💣 tailscale.com/util/winutil from tailscale.com/cmd/tailscaled+
@@ -281,16 +290,18 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
W tailscale.com/wf from tailscale.com/cmd/tailscaled
tailscale.com/wgengine from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
💣 tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/monitor from tailscale.com/control/controlclient+
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled+
tailscale.com/wgengine/router from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/wgcfg/nmcfg from tailscale.com/ipn/ipnlocal
💣 tailscale.com/wgengine/wgint from tailscale.com/wgengine
tailscale.com/wgengine/wglog from tailscale.com/wgengine
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router
golang.org/x/crypto/acme from tailscale.com/ipn/localapi
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/argon2 from tailscale.com/tka
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box+
golang.org/x/crypto/blake2s from golang.zx2c4.com/wireguard/device+
LD golang.org/x/crypto/blowfish from golang.org/x/crypto/ssh/internal/bcrypt_pbkdf+
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305+
@@ -305,6 +316,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/crypto/poly1305 from golang.zx2c4.com/wireguard/device+
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
LD golang.org/x/crypto/ssh from tailscale.com/ssh/tailssh+
golang.org/x/exp/constraints from golang.org/x/exp/slices
golang.org/x/exp/slices from tailscale.com/ipn/ipnlocal+
golang.org/x/net/bpf from github.com/mdlayher/genetlink+
golang.org/x/net/dns/dnsmessage from net+
golang.org/x/net/http/httpguts from golang.org/x/net/http2+
@@ -312,8 +325,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/net/http2 from golang.org/x/net/http2/h2c+
golang.org/x/net/http2/h2c from tailscale.com/ipn/ipnlocal
golang.org/x/net/http2/hpack from golang.org/x/net/http2+
golang.org/x/net/icmp from tailscale.com/net/ping
golang.org/x/net/idna from golang.org/x/net/http/httpguts+
golang.org/x/net/ipv4 from golang.zx2c4.com/wireguard/device
golang.org/x/net/ipv4 from golang.zx2c4.com/wireguard/device+
golang.org/x/net/ipv6 from golang.zx2c4.com/wireguard/device+
golang.org/x/net/proxy from tailscale.com/net/netns
D golang.org/x/net/route from net+
@@ -361,6 +375,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
embed from tailscale.com+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from tailscale.com/tka
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/hex from crypto/x509+
@@ -377,7 +392,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
hash/maphash from go4.org/mem
html from tailscale.com/ipn/ipnlocal+
io from bufio+
io/fs from crypto/rand+
io/fs from crypto/x509+
io/ioutil from github.com/godbus/dbus/v5+
log from expvar+
LD log/syslog from tailscale.com/ssh/tailssh
@@ -390,6 +405,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
mime/quotedprintable from mime/multipart
net from crypto/tls+
net/http from expvar+
net/http/httptest from tailscale.com/control/controlclient
net/http/httptrace from github.com/tcnksm/go-httpstat+
net/http/httputil from github.com/aws/smithy-go/transport/http+
net/http/internal from net/http+

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package main
@@ -11,7 +11,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@@ -142,7 +141,7 @@ func installSystemDaemonDarwin(args []string) (err error) {
return err
}
if err := ioutil.WriteFile(sysPlist, []byte(darwinLaunchdPlist), 0700); err != nil {
if err := os.WriteFile(sysPlist, []byte(darwinLaunchdPlist), 0700); err != nil {
return err
}

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package main

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
// HTTP proxy code

View File

@@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
//go:build !go1.19
// +build !go1.19
package main
func init() {
you_need_Go_1_18_to_compile_Tailscale()
you_need_Go_1_19_to_compile_Tailscale()
}

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
// The tailscaled program is the Tailscale client daemon. It's configured
// and controlled via the tailscale CLI program.
@@ -21,15 +21,16 @@ import (
"net"
"net/http"
"net/http/pprof"
"net/netip"
"os"
"os/signal"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"inet.af/netaddr"
"tailscale.com/cmd/tailscaled/childproc"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
@@ -97,6 +98,20 @@ func defaultTunName() string {
return "tailscale0"
}
// defaultPort returns the default UDP port to listen on for disco+wireguard.
// By default it returns 0, to pick one randomly from the kernel.
// If the environment variable PORT is set, that's used instead.
// The PORT environment variable is chosen to match what the Linux systemd
// unit uses, to make documentation more consistent.
func defaultPort() uint16 {
if s := envknob.String("PORT"); s != "" {
if p, err := strconv.ParseUint(s, 10, 16); err == nil {
return uint16(p)
}
}
return 0
}
var args struct {
// tunname is a /dev/net/tun tunnel name ("tailscale0"), the
// string "userspace-networking", "tap:TAPNAME[:BRIDGENAME]"
@@ -113,6 +128,7 @@ var args struct {
verbose int
socksAddr string // listen address for SOCKS5 server
httpProxyAddr string // listen address for HTTP proxy server
disableLogs bool
}
var (
@@ -128,7 +144,12 @@ var subCommands = map[string]*func([]string) error{
"be-child": &beChildFunc,
}
var beCLI func() // non-nil if CLI is linked in
func main() {
envknob.PanicIfAnyEnvCheckedInInit()
envknob.ApplyDiskConfig()
printVersion := false
flag.IntVar(&args.verbose, "verbose", 0, "log verbosity level; 0 is default, 1 or higher are increasingly verbose")
flag.BoolVar(&args.cleanup, "cleanup", false, "clean up system state and exit")
@@ -136,12 +157,18 @@ func main() {
flag.StringVar(&args.socksAddr, "socks5-server", "", `optional [ip]:port to run a SOCK5 server (e.g. "localhost:1080")`)
flag.StringVar(&args.httpProxyAddr, "outbound-http-proxy-listen", "", `optional [ip]:port to run an outbound HTTP proxy (e.g. "localhost:8080")`)
flag.StringVar(&args.tunname, "tun", defaultTunName(), `tunnel interface name; use "userspace-networking" (beta) to not use TUN`)
flag.Var(flagtype.PortValue(&args.port, 0), "port", "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select")
flag.Var(flagtype.PortValue(&args.port, defaultPort()), "port", "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select")
flag.StringVar(&args.statepath, "state", "", "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM; use 'mem:' to not store state and register as an emphemeral node. If empty and --statedir is provided, the default is <statedir>/tailscaled.state. Default: "+paths.DefaultTailscaledStateFile())
flag.StringVar(&args.statedir, "statedir", "", "path to directory for storage of config state, TLS certs, temporary incoming Taildrop files, etc. If empty, it's derived from --state when possible.")
flag.StringVar(&args.socketpath, "socket", paths.DefaultTailscaledSocket(), "path of the service unix socket")
flag.StringVar(&args.birdSocketPath, "bird-socket", "", "path of the bird unix socket")
flag.BoolVar(&printVersion, "version", false, "print version information and exit")
flag.BoolVar(&args.disableLogs, "no-logs-no-support", false, "disable log uploads; this also disables any technical support")
if len(os.Args) > 0 && filepath.Base(os.Args[0]) == "tailscale" && beCLI != nil {
beCLI()
return
}
if len(os.Args) > 1 {
sub := os.Args[1]
@@ -192,6 +219,10 @@ func main() {
args.statepath = paths.DefaultTailscaledStateFile()
}
if args.disableLogs {
envknob.SetNoLogsNoSupport()
}
if beWindowsSubprocess() {
return
}
@@ -295,6 +326,10 @@ func run() error {
pol.Shutdown(ctx)
}()
if err := envknob.ApplyDiskConfigError(); err != nil {
log.Printf("Error reading environment config: %v", err)
}
if isWindowsService() {
// Run the IPN server from the Windows service manager.
log.Printf("Running service...")
@@ -363,14 +398,14 @@ func run() error {
return fmt.Errorf("newNetstack: %w", err)
}
ns.ProcessLocalIPs = useNetstack
ns.ProcessSubnets = useNetstack || wrapNetstack
ns.ProcessSubnets = useNetstack || shouldWrapNetstack()
if useNetstack {
dialer.UseNetstackForIP = func(ip netaddr.IP) bool {
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
_, ok := e.PeerForIP(ip)
return ok
}
dialer.NetstackDialTCP = func(ctx context.Context, dst netaddr.IPPort) (net.Conn, error) {
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
return ns.DialContextTCP(ctx, dst)
}
}
@@ -464,8 +499,6 @@ func createEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer)
return nil, false, multierr.New(errs...)
}
var wrapNetstack = shouldWrapNetstack()
func shouldWrapNetstack() bool {
if v, ok := envknob.LookupBool("TS_DEBUG_WRAP_NETSTACK"); ok {
return v
@@ -474,7 +507,7 @@ func shouldWrapNetstack() bool {
return true
}
switch runtime.GOOS {
case "windows", "darwin", "freebsd":
case "windows", "darwin", "freebsd", "openbsd":
// Enable on Windows and tailscaled-on-macOS (this doesn't
// affect the GUI clients), and on FreeBSD.
return true
@@ -536,7 +569,7 @@ func tryEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer, na
}
conf.DNS = d
conf.Router = r
if wrapNetstack {
if shouldWrapNetstack() {
conf.Router = netstack.NewSubnetRouterWrapper(conf.Router)
}
}

View File

@@ -7,7 +7,7 @@ After=network-pre.target NetworkManager.service systemd-resolved.service
[Service]
EnvironmentFile=/etc/default/tailscaled
ExecStartPre=/usr/sbin/tailscaled --cleanup
ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port $PORT $FLAGS
ExecStart=/usr/sbin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port=${PORT} $FLAGS
ExecStopPost=/usr/sbin/tailscaled --cleanup
Restart=on-failure

View File

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

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows && go1.18
// +build !windows,go1.18
//go:build !windows && go1.19
// +build !windows,go1.19
package main // import "tailscale.com/cmd/tailscaled"

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.18
// +build go1.18
//go:build go1.19
// +build go1.19
package main // import "tailscale.com/cmd/tailscaled"
@@ -25,6 +25,7 @@ import (
"encoding/json"
"fmt"
"log"
"net/netip"
"os"
"time"
@@ -32,7 +33,6 @@ import (
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnserver"
"tailscale.com/ipn/store"
@@ -197,6 +197,9 @@ func beWindowsSubprocess() bool {
log.Printf("Program starting: v%v: %#v", version.Long, os.Args)
log.Printf("subproc mode: logid=%v", logid)
if err := envknob.ApplyDiskConfigError(); err != nil {
log.Printf("Error reading environment config: %v", err)
}
go func() {
b := make([]byte, 16)
@@ -245,7 +248,7 @@ func beFirewallKillswitch() bool {
// is passed in via stdin encoded in json.
dcd := json.NewDecoder(os.Stdin)
for {
var routes []netaddr.IPPrefix
var routes []netip.Prefix
if err := dcd.Decode(&routes); err != nil {
log.Fatalf("parent process died or requested exit, exiting (%v)", err)
}
@@ -274,7 +277,7 @@ func startIPNServer(ctx context.Context, logid string) error {
dev.Close()
return nil, nil, fmt.Errorf("router: %w", err)
}
if wrapNetstack {
if shouldWrapNetstack() {
r = netstack.NewSubnetRouterWrapper(r)
}
d, err := dns.NewOSConfigurator(logf, devName)
@@ -301,7 +304,7 @@ func startIPNServer(ctx context.Context, logid string) error {
return nil, nil, fmt.Errorf("newNetstack: %w", err)
}
ns.ProcessLocalIPs = false
ns.ProcessSubnets = wrapNetstack
ns.ProcessSubnets = shouldWrapNetstack()
if err := ns.Start(); err != nil {
return nil, nil, fmt.Errorf("failed to start netstack: %w", err)
}

View File

@@ -0,0 +1,25 @@
// 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.
//go:build ts_include_cli
// +build ts_include_cli
package main
import (
"fmt"
"os"
"tailscale.com/cmd/tailscale/cli"
)
func init() {
beCLI = func() {
args := os.Args[1:]
if err := cli.Run(args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
}

View File

@@ -1,4 +1,3 @@
src/wasm_exec.js
src/main.wasm
node_modules/
dist/
/dist
/pkg

49
cmd/tsconnect/README.md Normal file
View File

@@ -0,0 +1,49 @@
# tsconnect
The tsconnect command builds and serves the static site that is generated for
the Tailscale Connect JS/WASM client.
## Development
To start the development server:
```
./tool/go run ./cmd/tsconnect dev
```
The site is served at http://localhost:9090/. JavaScript and CSS changes can be picked up with a browser reload. Go changes (including to the `wasm` package) require the server to be stopped and restarted. In development mode the state the Tailscale client is stored in `sessionStorage` and will thus survive page reloads (but not the tab being closed).
## Deployment
To build the static assets necessary for serving, run:
```
./tool/go run ./cmd/tsconnect build
```
To serve them, run:
```
./tool/go run ./cmd/tsconnect serve
```
By default the build output is placed in the `dist/` directory and embedded in the binary, but this can be controlled by the `-distdir` flag. The `-addr` flag controls the interface and port that the serve listens on.
# Library / NPM Package
The client is also available as an NPM package. To build it, run:
```
./tool/go run ./cmd/tsconnect build-pkg
```
That places the output in the `pkg/` directory, which may then be uploaded to a package registry (or installed from the file path directly).
To do two-sided development (on both the NPM package and code that uses it), run:
```
./tool/go run ./cmd/tsconnect dev-pkg
```
This serves the module at http://localhost:9090/pkg/pkg.js and the generated wasm file at http://localhost:9090/pkg/main.wasm. The two files can be used as drop-in replacements for normal imports of the NPM module.

View File

@@ -0,0 +1,74 @@
// 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 main
import (
"encoding/json"
"fmt"
"log"
"os"
"path"
"github.com/tailscale/hujson"
"tailscale.com/version"
)
func runBuildPkg() {
buildOptions, err := commonPkgSetup(prodMode)
if err != nil {
log.Fatalf("Cannot setup: %v", err)
}
log.Printf("Linting...\n")
if err := runYarn("lint"); err != nil {
log.Fatalf("Linting failed: %v", err)
}
if err := cleanDir(*pkgDir, "package.json"); err != nil {
log.Fatalf("Cannot clean %s: %v", *pkgDir, err)
}
buildOptions.Write = true
buildOptions.MinifyWhitespace = true
buildOptions.MinifyIdentifiers = true
buildOptions.MinifySyntax = true
runEsbuild(*buildOptions)
log.Printf("Generating types...\n")
if err := runYarn("pkg-types"); err != nil {
log.Fatalf("Type generation failed: %v", err)
}
if err := updateVersion(); err != nil {
log.Fatalf("Cannot update version: %v", err)
}
log.Printf("Built package version %s", version.Long)
}
func updateVersion() error {
packageJSONBytes, err := os.ReadFile("package.json.tmpl")
if err != nil {
return fmt.Errorf("Could not read package.json: %w", err)
}
var packageJSON map[string]any
packageJSONBytes, err = hujson.Standardize(packageJSONBytes)
if err != nil {
return fmt.Errorf("Could not standardize template package.json: %w", err)
}
if err := json.Unmarshal(packageJSONBytes, &packageJSON); err != nil {
return fmt.Errorf("Could not unmarshal package.json: %w", err)
}
packageJSON["version"] = version.Long
packageJSONBytes, err = json.MarshalIndent(packageJSON, "", " ")
if err != nil {
return fmt.Errorf("Could not marshal package.json: %w", err)
}
return os.WriteFile(path.Join(*pkgDir, "package.json"), packageJSONBytes, 0644)
}

View File

@@ -5,21 +5,14 @@
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"io/fs"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"github.com/andybalholm/brotli"
esbuild "github.com/evanw/esbuild/pkg/api"
"golang.org/x/sync/errgroup"
"tailscale.com/util/precompress"
)
func runBuild() {
@@ -28,7 +21,12 @@ func runBuild() {
log.Fatalf("Cannot setup: %v", err)
}
if err := cleanDist(); err != nil {
log.Printf("Linting...\n")
if err := runYarn("lint"); err != nil {
log.Fatalf("Linting failed: %v", err)
}
if err := cleanDir(*distDir, "placeholder"); err != nil {
log.Fatalf("Cannot clean %s: %v", *distDir, err)
}
@@ -41,32 +39,18 @@ func runBuild() {
buildOptions.AssetNames = "[name]-[hash]"
buildOptions.Metafile = true
log.Printf("Running esbuild...\n")
result := esbuild.Build(*buildOptions)
if len(result.Errors) > 0 {
log.Printf("ESBuild Error:\n")
for _, e := range result.Errors {
log.Printf("%v", e)
}
log.Fatal("Build failed")
}
if len(result.Warnings) > 0 {
log.Printf("ESBuild Warnings:\n")
for _, w := range result.Warnings {
log.Printf("%v", w)
}
}
result := runEsbuild(*buildOptions)
// Preserve build metadata so we can extract hashed file names for serving.
metadataBytes, err := fixEsbuildMetadataPaths(result.Metafile)
if err != nil {
log.Fatalf("Cannot fix esbuild metadata paths: %v", err)
}
if err := ioutil.WriteFile(path.Join(*distDir, "/esbuild-metadata.json"), metadataBytes, 0666); err != nil {
if err := os.WriteFile(path.Join(*distDir, "/esbuild-metadata.json"), metadataBytes, 0666); err != nil {
log.Fatalf("Cannot write metadata: %v", err)
}
if er := precompressDist(); err != nil {
if er := precompressDist(*fastCompression); err != nil {
log.Fatalf("Cannot precompress resources: %v", er)
}
}
@@ -98,8 +82,6 @@ func fixEsbuildMetadataPaths(metadataStr string) ([]byte, error) {
return json.Marshal(metadata)
}
// cleanDist removes files from the dist build directory, except the placeholder
// one that we keep to make sure Git still creates the directory.
func cleanDist() error {
log.Printf("Cleaning %s...\n", *distDir)
files, err := os.ReadDir(*distDir)
@@ -120,71 +102,12 @@ func cleanDist() error {
return nil
}
func precompressDist() error {
func precompressDist(fastCompression bool) error {
log.Printf("Pre-compressing files in %s/...\n", *distDir)
var eg errgroup.Group
err := fs.WalkDir(os.DirFS(*distDir), ".", func(p string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if !compressibleExtensions[filepath.Ext(p)] {
return nil
}
p = path.Join(*distDir, p)
log.Printf("Pre-compressing %v\n", p)
eg.Go(func() error {
return precompress(p)
})
return nil
return precompress.PrecompressDir(*distDir, precompress.Options{
FastCompression: fastCompression,
ProgressFn: func(path string) {
log.Printf("Pre-compressing %v\n", path)
},
})
if err != nil {
return err
}
return eg.Wait()
}
var compressibleExtensions = map[string]bool{
".js": true,
".css": true,
".wasm": true,
}
func precompress(path string) error {
contents, err := os.ReadFile(path)
if err != nil {
return err
}
fi, err := os.Lstat(path)
if err != nil {
return err
}
err = writeCompressed(contents, func(w io.Writer) (io.WriteCloser, error) {
return gzip.NewWriterLevel(w, gzip.BestCompression)
}, path+".gz", fi.Mode())
if err != nil {
return err
}
return writeCompressed(contents, func(w io.Writer) (io.WriteCloser, error) {
return brotli.NewWriterLevel(w, brotli.BestCompression), nil
}, path+".br", fi.Mode())
}
func writeCompressed(contents []byte, compressedWriterCreator func(io.Writer) (io.WriteCloser, error), outputPath string, outputMode fs.FileMode) error {
var buf bytes.Buffer
compressedWriter, err := compressedWriterCreator(&buf)
if err != nil {
return err
}
if _, err := compressedWriter.Write(contents); err != nil {
return err
}
if err := compressedWriter.Close(); err != nil {
return err
}
return os.WriteFile(outputPath, buf.Bytes(), outputMode)
}

View File

@@ -7,14 +7,17 @@ package main
import (
"fmt"
"log"
"net"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"time"
esbuild "github.com/evanw/esbuild/pkg/api"
"golang.org/x/exp/slices"
)
const (
@@ -31,77 +34,198 @@ func commonSetup(dev bool) (*esbuild.BuildOptions, error) {
return nil, fmt.Errorf("Cannot change cwd: %w", err)
}
}
if err := buildDeps(dev); err != nil {
return nil, fmt.Errorf("Cannot build deps: %w", err)
if err := installJSDeps(); err != nil {
return nil, fmt.Errorf("Cannot install JS deps: %w", err)
}
return &esbuild.BuildOptions{
EntryPoints: []string{"src/index.js", "src/index.css"},
Loader: map[string]esbuild.Loader{".wasm": esbuild.LoaderFile},
EntryPoints: []string{"src/app/index.ts", "src/app/index.css"},
Outdir: *distDir,
Bundle: true,
Sourcemap: esbuild.SourceMapLinked,
LogLevel: esbuild.LogLevelInfo,
Define: map[string]string{"DEBUG": strconv.FormatBool(dev)},
Target: esbuild.ES2017,
Plugins: []esbuild.Plugin{
{
Name: "tailscale-tailwind",
Setup: func(build esbuild.PluginBuild) {
setupEsbuildTailwind(build, dev)
},
},
{
Name: "tailscale-go-wasm-exec-js",
Setup: setupEsbuildWasmExecJS,
},
{
Name: "tailscale-wasm",
Setup: func(build esbuild.PluginBuild) {
setupEsbuildWasm(build, dev)
},
},
},
JSXMode: esbuild.JSXModeAutomatic,
}, nil
}
// buildDeps builds the static assets that are needed for the server (except for
// JS/CSS bundling, which is handled by esbuild).
func buildDeps(dev bool) error {
if err := copyWasmExec(); err != nil {
return fmt.Errorf("Cannot copy wasm_exec.js: %w", err)
func commonPkgSetup(dev bool) (*esbuild.BuildOptions, error) {
buildOptions, err := commonSetup(dev)
if err != nil {
return nil, err
}
if err := buildWasm(dev); err != nil {
return fmt.Errorf("Cannot build main.wasm: %w", err)
buildOptions.EntryPoints = []string{"src/pkg/pkg.ts", "src/pkg/pkg.css"}
buildOptions.Outdir = *pkgDir
buildOptions.Format = esbuild.FormatESModule
buildOptions.AssetNames = "[name]"
return buildOptions, nil
}
// cleanDir removes files from dirPath, except the ones specified by
// preserveFiles.
func cleanDir(dirPath string, preserveFiles ...string) error {
log.Printf("Cleaning %s...\n", dirPath)
files, err := os.ReadDir(dirPath)
if err != nil {
if os.IsNotExist(err) {
return os.MkdirAll(dirPath, 0755)
}
return err
}
if err := installJSDeps(); err != nil {
return fmt.Errorf("Cannot install JS deps: %w", err)
for _, file := range files {
if !slices.Contains(preserveFiles, file.Name()) {
if err := os.Remove(filepath.Join(dirPath, file.Name())); err != nil {
return err
}
}
}
return nil
}
// copyWasmExec grabs the current wasm_exec.js runtime helper library from the
// Go toolchain.
func copyWasmExec() error {
log.Printf("Copying wasm_exec.js...\n")
wasmExecSrcPath := filepath.Join(runtime.GOROOT(), "misc", "wasm", "wasm_exec.js")
wasmExecDstPath := filepath.Join("src", "wasm_exec.js")
contents, err := os.ReadFile(wasmExecSrcPath)
func runEsbuildServe(buildOptions esbuild.BuildOptions) {
host, portStr, err := net.SplitHostPort(*addr)
if err != nil {
return err
log.Fatalf("Cannot parse addr: %v", err)
}
return os.WriteFile(wasmExecDstPath, contents, 0600)
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
log.Fatalf("Cannot parse port: %v", err)
}
result, err := esbuild.Serve(esbuild.ServeOptions{
Port: uint16(port),
Host: host,
Servedir: "./",
}, buildOptions)
if err != nil {
log.Fatalf("Cannot start esbuild server: %v", err)
}
log.Printf("Listening on http://%s:%d\n", result.Host, result.Port)
result.Wait()
}
// buildWasm builds the Tailscale wasm binary and places it where the JS can
// load it.
func buildWasm(dev bool) error {
log.Printf("Building wasm...\n")
func runEsbuild(buildOptions esbuild.BuildOptions) esbuild.BuildResult {
log.Printf("Running esbuild...\n")
result := esbuild.Build(buildOptions)
if len(result.Errors) > 0 {
log.Printf("ESBuild Error:\n")
for _, e := range result.Errors {
log.Printf("%v", e)
}
log.Fatal("Build failed")
}
if len(result.Warnings) > 0 {
log.Printf("ESBuild Warnings:\n")
for _, w := range result.Warnings {
log.Printf("%v", w)
}
}
return result
}
// setupEsbuildWasmExecJS generates an esbuild plugin that serves the current
// wasm_exec.js runtime helper library from the Go toolchain.
func setupEsbuildWasmExecJS(build esbuild.PluginBuild) {
wasmExecSrcPath := filepath.Join(runtime.GOROOT(), "misc", "wasm", "wasm_exec.js")
build.OnResolve(esbuild.OnResolveOptions{
Filter: "./wasm_exec$",
}, func(args esbuild.OnResolveArgs) (esbuild.OnResolveResult, error) {
return esbuild.OnResolveResult{Path: wasmExecSrcPath}, nil
})
}
// setupEsbuildWasm generates an esbuild plugin that builds the Tailscale wasm
// binary and serves it as a file that the JS can load.
func setupEsbuildWasm(build esbuild.PluginBuild, dev bool) {
// Add a resolve hook to convince esbuild that the path exists.
build.OnResolve(esbuild.OnResolveOptions{
Filter: "./main.wasm$",
}, func(args esbuild.OnResolveArgs) (esbuild.OnResolveResult, error) {
return esbuild.OnResolveResult{
Path: "./src/main.wasm",
Namespace: "generated",
}, nil
})
build.OnLoad(esbuild.OnLoadOptions{
Filter: "./src/main.wasm$",
}, func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
contents, err := buildWasm(dev)
if err != nil {
return esbuild.OnLoadResult{}, fmt.Errorf("Cannot build main.wasm: %w", err)
}
contentsStr := string(contents)
return esbuild.OnLoadResult{
Contents: &contentsStr,
Loader: esbuild.LoaderFile,
}, nil
})
}
func buildWasm(dev bool) ([]byte, error) {
start := time.Now()
outputFile, err := os.CreateTemp("", "main.*.wasm")
if err != nil {
return nil, fmt.Errorf("Cannot create main.wasm output file: %w", err)
}
outputPath := outputFile.Name()
defer os.Remove(outputPath)
args := []string{"build", "-tags", "tailscale_go,osusergo,netgo,nethttpomithttp2,omitidna,omitpemdecrypt"}
if !dev {
if *devControl != "" {
return nil, fmt.Errorf("Development control URL can only be used in dev mode.")
}
// Omit long paths and debug symbols in release builds, to reduce the
// generated WASM binary size.
args = append(args, "-trimpath", "-ldflags", "-s -w")
} else if *devControl != "" {
args = append(args, "-ldflags", fmt.Sprintf("-X 'main.ControlURL=%v'", *devControl))
}
args = append(args, "-o", "src/main.wasm", "./wasm")
args = append(args, "-o", outputPath, "./wasm")
cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("Cannot build main.wasm: %w", err)
}
log.Printf("Built wasm in %v\n", time.Since(start))
return os.ReadFile(outputPath)
}
// installJSDeps installs the JavaScript dependencies specified by package.json
func installJSDeps() error {
log.Printf("Installing JS deps...\n")
stdoutStderr, err := exec.Command("yarn").CombinedOutput()
if err != nil {
log.Printf("yarn failed: %s", stdoutStderr)
}
return err
return runYarn()
}
func runYarn(args ...string) error {
cmd := exec.Command(*yarnPath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
// EsbuildMetadata is the subset of metadata struct (described by
@@ -109,6 +233,36 @@ func installJSDeps() error {
// from entry points to hashed file names.
type EsbuildMetadata struct {
Outputs map[string]struct {
Inputs map[string]struct {
BytesInOutput int64 `json:"bytesInOutput"`
} `json:"inputs,omitempty"`
EntryPoint string `json:"entryPoint,omitempty"`
} `json:"outputs,omitempty"`
}
func setupEsbuildTailwind(build esbuild.PluginBuild, dev bool) {
build.OnLoad(esbuild.OnLoadOptions{
Filter: "./src/.*\\.css$",
}, func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
start := time.Now()
yarnArgs := []string{"--silent", "tailwind", "-i", args.Path}
if !dev {
yarnArgs = append(yarnArgs, "--minify")
}
cmd := exec.Command(*yarnPath, yarnArgs...)
tailwindOutput, err := cmd.Output()
log.Printf("Ran tailwind in %v\n", time.Since(start))
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
log.Printf("Tailwind stderr: %s", exitErr.Stderr)
}
return esbuild.OnLoadResult{}, fmt.Errorf("Cannot run tailwind: %w", err)
}
tailwindOutputStr := string(tailwindOutput)
return esbuild.OnLoadResult{
Contents: &tailwindOutputStr,
Loader: esbuild.LoaderCSS,
}, nil
})
}

17
cmd/tsconnect/dev-pkg.go Normal file
View File

@@ -0,0 +1,17 @@
// 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 main
import (
"log"
)
func runDevPkg() {
buildOptions, err := commonPkgSetup(devMode)
if err != nil {
log.Fatalf("Cannot setup: %v", err)
}
runEsbuildServe(*buildOptions)
}

View File

@@ -6,10 +6,6 @@ package main
import (
"log"
"net"
"strconv"
esbuild "github.com/evanw/esbuild/pkg/api"
)
func runDev() {
@@ -17,22 +13,5 @@ func runDev() {
if err != nil {
log.Fatalf("Cannot setup: %v", err)
}
host, portStr, err := net.SplitHostPort(*addr)
if err != nil {
log.Fatalf("Cannot parse addr: %v", err)
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
log.Fatalf("Cannot parse port: %v", err)
}
result, err := esbuild.Serve(esbuild.ServeOptions{
Port: uint16(port),
Host: host,
Servedir: "./",
}, *buildOptions)
if err != nil {
log.Fatalf("Cannot start esbuild server: %v", err)
}
log.Printf("Listening on http://%s:%d\n", result.Host, result.Port)
result.Wait()
runEsbuildServe(*buildOptions)
}

View File

@@ -3,14 +3,18 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tailscale Connect</title>
<link rel="stylesheet" type="text/css" href="dist/index.css" />
<script src="dist/index.js" defer></script>
</head>
<body>
<div id="header">
<h1>Tailscale Connect</h1>
<div id="state">Loading…</div>
<body class="flex flex-col h-screen overflow-hidden">
<!-- Placeholder so that we don't have an empty page while the JS loads.
It should match the markup generated by Header component. -->
<div class="bg-gray-100 border-b border-gray-200 pt-4 pb-2">
<header class="container mx-auto px-4 flex flex-row items-center">
<h1 class="text-3xl font-bold grow">Tailscale Connect</h1>
<div class="text-gray-600">Loading…</div>
</header>
</div>
<div id="peers"></div>
<script src="dist/index.js"></script>
</body>
</html>

View File

@@ -1,9 +1,22 @@
{
"name": "@tailscale/ssh",
"name": "tsconnect",
"version": "0.0.1",
"license": "BSD-3-Clause",
"devDependencies": {
"@types/golang-wasm-exec": "^1.15.0",
"@types/qrcode": "^1.4.2",
"dts-bundle-generator": "^6.12.0",
"preact": "^10.10.0",
"qrcode": "^1.5.0",
"xterm": "^4.18.0"
"tailwindcss": "^3.1.6",
"typescript": "^4.7.4",
"xterm": "5.0.0-beta.58",
"xterm-addon-fit": "^0.5.0",
"xterm-addon-web-links": "0.7.0-beta.6"
},
"scripts": {
"lint": "tsc --noEmit",
"pkg-types": "dts-bundle-generator --inline-declare-global=true --no-banner -o pkg/pkg.d.ts src/pkg/pkg.ts"
},
"prettier": {
"semi": false,

View File

@@ -0,0 +1,17 @@
// 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.
// Template for the package.json that is generated by the build-pkg command.
// The version number will be replaced by the current Tailscale client version
// number.
{
"author": "Tailscale Inc.",
"description": "Tailscale Connect SDK",
"license": "BSD-3-Clause",
"name": "tailscale-connect",
"type": "module",
"main": "./pkg.js",
"types": "./pkg.d.ts",
"version": "AUTO_GENERATED"
}

View File

@@ -11,7 +11,6 @@ import (
"fmt"
"io"
"io/fs"
"io/ioutil"
"log"
"net/http"
"os"
@@ -19,6 +18,7 @@ import (
"time"
"tailscale.com/tsweb"
"tailscale.com/util/precompress"
)
//go:embed index.html
@@ -74,7 +74,7 @@ func generateServeIndex(distFS fs.FS) ([]byte, error) {
return nil, fmt.Errorf("Could not open esbuild-metadata.json: %w", err)
}
defer esbuildMetadataFile.Close()
esbuildMetadataBytes, err := ioutil.ReadAll(esbuildMetadataFile)
esbuildMetadataBytes, err := io.ReadAll(esbuildMetadataFile)
if err != nil {
return nil, fmt.Errorf("Could not read esbuild-metadata.json: %w", err)
}
@@ -83,10 +83,19 @@ func generateServeIndex(distFS fs.FS) ([]byte, error) {
return nil, fmt.Errorf("Could not parse esbuild-metadata.json: %w", err)
}
entryPointsToHashedDistPaths := make(map[string]string)
mainWasmPath := ""
for outputPath, output := range esbuildMetadata.Outputs {
if output.EntryPoint != "" {
entryPointsToHashedDistPaths[output.EntryPoint] = path.Join("dist", outputPath)
}
if path.Ext(outputPath) == ".wasm" {
for input := range output.Inputs {
if input == "src/main.wasm" {
mainWasmPath = path.Join("dist", outputPath)
break
}
}
}
}
indexBytes := rawIndexBytes
@@ -96,39 +105,25 @@ func generateServeIndex(distFS fs.FS) ([]byte, error) {
indexBytes = bytes.ReplaceAll(indexBytes, []byte(defaultDistPath), []byte(hashedDistPath))
}
}
if mainWasmPath != "" {
mainWasmPrefetch := fmt.Sprintf("</title>\n<link rel='preload' as='fetch' crossorigin='anonymous' href='%s'>", mainWasmPath)
indexBytes = bytes.ReplaceAll(indexBytes, []byte("</title>"), []byte(mainWasmPrefetch))
}
return indexBytes, nil
}
var entryPointsToDefaultDistPaths = map[string]string{
"src/index.css": "dist/index.css",
"src/index.js": "dist/index.js",
"src/app/index.css": "dist/index.css",
"src/app/index.ts": "dist/index.js",
}
func handleServeDist(w http.ResponseWriter, r *http.Request, distFS fs.FS) {
path := r.URL.Path
var f fs.File
// Prefer pre-compressed versions generated during the build step.
if tsweb.AcceptsEncoding(r, "br") {
if brotliFile, err := distFS.Open(path + ".br"); err == nil {
f = brotliFile
w.Header().Set("Content-Encoding", "br")
}
}
if f == nil && tsweb.AcceptsEncoding(r, "gzip") {
if gzipFile, err := distFS.Open(path + ".gz"); err == nil {
f = gzipFile
w.Header().Set("Content-Encoding", "gzip")
}
}
if f == nil {
if rawFile, err := distFS.Open(path); err == nil {
f = rawFile
} else {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
f, err := precompress.OpenPrecompressedFile(w, r, path, distFS)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
defer f.Close()

View File

@@ -0,0 +1,129 @@
// 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.
import { render, Component } from "preact"
import { URLDisplay } from "./url-display"
import { Header } from "./header"
import { GoPanicDisplay } from "./go-panic-display"
import { SSH } from "./ssh"
type AppState = {
ipn?: IPN
ipnState: IPNState
netMap?: IPNNetMap
browseToURL?: string
goPanicError?: string
}
class App extends Component<{}, AppState> {
state: AppState = { ipnState: "NoState" }
#goPanicTimeout?: number
render() {
const { ipn, ipnState, goPanicError, netMap, browseToURL } = this.state
let goPanicDisplay
if (goPanicError) {
goPanicDisplay = (
<GoPanicDisplay error={goPanicError} dismiss={this.clearGoPanic} />
)
}
let urlDisplay
if (browseToURL) {
urlDisplay = <URLDisplay url={browseToURL} />
}
let machineAuthInstructions
if (ipnState === "NeedsMachineAuth") {
machineAuthInstructions = (
<div class="container mx-auto px-4 text-center">
An administrator needs to authorize this device.
</div>
)
}
let ssh
if (ipn && ipnState === "Running" && netMap) {
ssh = <SSH netMap={netMap} ipn={ipn} />
}
return (
<>
<Header state={ipnState} ipn={ipn} />
{goPanicDisplay}
<div class="flex-grow flex flex-col justify-center overflow-hidden">
{urlDisplay}
{machineAuthInstructions}
{ssh}
</div>
</>
)
}
runWithIPN(ipn: IPN) {
this.setState({ ipn }, () => {
ipn.run({
notifyState: this.handleIPNState,
notifyNetMap: this.handleNetMap,
notifyBrowseToURL: this.handleBrowseToURL,
notifyPanicRecover: this.handleGoPanic,
})
})
}
handleIPNState = (state: IPNState) => {
const { ipn } = this.state
this.setState({ ipnState: state })
if (state === "NeedsLogin") {
ipn?.login()
} else if (["Running", "NeedsMachineAuth"].includes(state)) {
this.setState({ browseToURL: undefined })
}
}
handleNetMap = (netMapStr: string) => {
const netMap = JSON.parse(netMapStr) as IPNNetMap
if (DEBUG) {
console.log("Received net map: " + JSON.stringify(netMap, null, 2))
}
this.setState({ netMap })
}
handleBrowseToURL = (url: string) => {
if (this.state.ipnState === "Running") {
// Ignore URL requests if we're already running -- it's most likely an
// SSH check mode trigger and we already linkify the displayed URL
// in the terminal.
return
}
this.setState({ browseToURL: url })
}
handleGoPanic = (error: string) => {
if (DEBUG) {
console.error("Go panic", error)
}
this.setState({ goPanicError: error })
if (this.#goPanicTimeout) {
window.clearTimeout(this.#goPanicTimeout)
}
this.#goPanicTimeout = window.setTimeout(this.clearGoPanic, 10000)
}
clearGoPanic = () => {
window.clearTimeout(this.#goPanicTimeout)
this.#goPanicTimeout = undefined
this.setState({ goPanicError: undefined })
}
}
export function renderApp(): Promise<App> {
return new Promise((resolve) => {
render(
<App ref={(app) => (app ? resolve(app) : undefined)} />,
document.body
)
})
}

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.
export function GoPanicDisplay({
error,
dismiss,
}: {
error: string
dismiss: () => void
}) {
return (
<div
class="rounded bg-red-500 p-2 absolute top-2 right-2 text-white font-bold text-right cursor-pointer"
onClick={dismiss}
>
Tailscale has encountered an error.
<div class="text-sm font-normal">Click to reload</div>
</div>
)
}

View File

@@ -0,0 +1,38 @@
// 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.
export function Header({ state, ipn }: { state: IPNState; ipn?: IPN }) {
const stateText = STATE_LABELS[state]
let logoutButton
if (state === "Running") {
logoutButton = (
<button
class="button bg-gray-500 border-gray-500 text-white hover:bg-gray-600 hover:border-gray-600 ml-2 font-bold"
onClick={() => ipn?.logout()}
>
Logout
</button>
)
}
return (
<div class="bg-gray-100 border-b border-gray-200 pt-4 pb-2">
<header class="container mx-auto px-4 flex flex-row items-center">
<h1 class="text-3xl font-bold grow">Tailscale Connect</h1>
<div class="text-gray-600">{stateText}</div>
{logoutButton}
</header>
</div>
)
}
const STATE_LABELS = {
NoState: "Initializing…",
InUseOtherUser: "In-use by another user",
NeedsLogin: "Needs login",
NeedsMachineAuth: "Needs authorization",
Stopped: "Stopped",
Starting: "Starting…",
Running: "Running",
} as const

Some files were not shown because too many files have changed in this diff Show More