Compare commits

...

146 Commits

Author SHA1 Message Date
Brad Fitzpatrick
ccdc41988c cmd/tailscale, ipn/ipn{local,server}: add start of CLI admin API + over Noise
Change-Id: I2936f6baf50e7eeac7190051adba493d4245b3ea
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-20 13:01:18 -07:00
Brad Fitzpatrick
bfb4a4d9e9 tsnet: fix format string/argument mismatch in log output
Change-Id: Ia7291ea47a289baec6cc6013d63d2f248ae57d9e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-19 20:24:33 -07:00
David Anderson
19f61607b6 prober: run all probes once on initial registration.
Turns out, it's annoying to have to wait the entire interval
before getting any monitorable data, especially for very long
interval probes like hourly/daily checks.

Signed-off-by: David Anderson <danderson@tailscale.com>
2022-03-19 18:54:33 -07:00
David Anderson
e41a3b983c prober: library to build healthchecking probers.
Signed-off-by: David Anderson <danderson@tailscale.com>
2022-03-19 18:38:32 -07:00
Brad Fitzpatrick
f2041c9088 all: use strings.Cut even more
Change-Id: I943ce72c6f339589235bddbe10d07799c4e37979
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-19 13:02:38 -07:00
Brad Fitzpatrick
f30473211b ssh/tailssh: start of implementing optional session recording
To asciinema cast format.

Updates #3802

Change-Id: Ifd3ea31922cd2c99068369cb1650e21f2545b0e1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-19 12:59:51 -07:00
Josh Bleecher Snyder
32fd42430b all: use cibuild.On
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-18 15:19:26 -07:00
Maisem Ali
b775df0b57 ssh/tailssh_test: skip TestSSH/stdin in CI
Updates #4051

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-18 10:57:12 -07:00
Maisem Ali
309c0a13a5 tsweb: add FQDN to Port80Handler to allow HTTPS redirects
When the request comes in say over http://mon, the current
implementation would rewrite it https://mon which causes the cert
validation to fail. This PR keeps the existing behavior intact but also
allows passing in a FQDN to the handler to reroute to the correct
hostname.

Related to https://github.com/tailscale/tailscale/pull/4208#pullrequestreview-913832340

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-18 10:16:08 -07:00
Maisem Ali
7f3d0992aa Makefile: use ./tool/go everywhere
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-18 10:13:18 -07:00
Aaron Klotz
6e91f872af net/tshttpproxy: ensure we pass the correct flags to WinHttpOpen on Win7 and Win8.0
The best flag to use on Win7 and Win8.0 is deprecated in Win8.1, so we resolve
the flag depending on OS version info.

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

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-03-18 11:05:02 -06:00
Brad Fitzpatrick
1db46919ab cmd/tailscaled: make build fail nicely on older Go versions
Due to a bug in Go (golang/go#51778), cmd/go doesn't warn about your
Go version being older than the go.mod's declared Go version in that
case that package loading fails before the build starts, such as when
you use packages that are only in the current version of Go, like our
use of net/netip.

This change works around that Go bug by adding build tags and a
pre-Go1.18-only file that will cause Go 1.17 and earlier to fail like:

    $ ~/sdk/go1.17/bin/go install ./cmd/tailscaled
    # tailscale.com/cmd/tailscaled
    ./required_version.go:11:2: undefined: you_need_Go_1_18_to_compile_Tailscale
    note: module requires Go 1.18

Change-Id: I39f5820de646703e19dde448dd86a7022252f75c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-18 08:25:51 -07:00
David Anderson
2a412ac9ee .github/workflows: work around golang/go#51629
Incidentally, simplify the go generate CI workflow, by
marking the dnsfallback update non-hermetic (so CI will
skip it) rather than manually filter it out of `go list`.

Updates #4194

Signed-off-by: David Anderson <danderson@tailscale.com>
2022-03-17 17:22:17 -07:00
Brad Fitzpatrick
18818763d1 derp: set Basic Constraints on metacert
See https://github.com/golang/go/issues/51759#issuecomment-1071147836

Once we deploy this, tailscaled should work again for macOS users with
Go 1.18.

Updates golang/go#51759

Change-Id: I869b6ddc556a2de885e96ccf9f335dfc8f6f6a7e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-17 15:38:21 -07:00
Simon Deziel
eaf5591953 scripts: install gnupg only when apt-key is needed
apt-key depends on gnupg but apt-key itself if not used
on modern systems (APT_KEY_TYPE=keyring).

Signed-off-by: Simon Deziel <simon@sdeziel.info>
2022-03-17 15:11:25 -07:00
Maisem Ali
bd073b8dd6 types/views: rename Generic to Unwrap
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-17 14:41:57 -07:00
Maisem Ali
1e12a29806 ssh/tailssh_test: Skip the env test in CI
Updates #4051

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-17 14:34:49 -07:00
Josh Bleecher Snyder
0868329936 all: use any instead of interface{}
My favorite part of generics.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-17 11:35:09 -07:00
Josh Bleecher Snyder
5f176f24db go.mod: upgrade to the latest wireguard-go
This pulls in a handful of fixes and an update to Go 1.18.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-17 10:59:39 -07:00
Brad Fitzpatrick
2708544018 tsnet: add some usability polish, remove WIP env var restriction
Change-Id: Id9ec1713c65cdd597d20b03e21e11cd60b54bb6a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-17 10:59:21 -07:00
Josh Bleecher Snyder
997b19545b syncs: use TryLock and TryRLock instead of unsafe
The docs say:

Note that while correct uses of TryLock do exist, they are rare,
and use of TryLock is often a sign of a deeper problem in a particular use of mutexes.

Rare code! Or bad code! Who can tell!

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-17 10:57:41 -07:00
Brad Fitzpatrick
ead16b24ec cmd/tailscaled: fail early with nice error on macOS with go1.18
Due to golang/go#51759

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-17 10:41:50 -07:00
Josh Bleecher Snyder
9d4ffd135f go.toolchain.rev: pick up crypto/x509 crash fix
68c97fb924

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-17 10:37:15 -07:00
Maisem Ali
6b9d938c1a types/views: add generic Slice[T] and remove StringSlice
Also make IPPrefixSliceOf use Slice[netaddr.IPPrefix] as it also
provides additional functions besides the standard ones provided by
Slice[T].

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-16 22:13:16 -07:00
Denton Gentry
d8953bf2ba cmd/derpprobe: don't alert for smaller failures.
There is a Cosmic Background level of DERP Unreachability,
with individual nodes or regions becoming unreachable briefly
and returning a short time later. This is due to hosting provider
outages or just the Internet sloshing about.

Returning a 500 error pages a human. Being awoken at 3am for
a transient error is annoying.

For relatively small levels of badness don't page a human,
just post to Slack. If the outage impacts a significant fraction
of the DERP fleet, then page a human.

Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-03-16 18:22:22 -07:00
Josh Bleecher Snyder
84a2dc3a7e go.toolchain.rev: update to slightly less forked Go 1.18
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 16:10:29 -07:00
Josh Bleecher Snyder
8c2cb4b431 go.mod: update to latest certstore
It includes a fix to allow us to use Go 1.18.
We can now remove our Tailscale-only build tags.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 16:10:29 -07:00
Brad Fitzpatrick
61ee72940c all: use Go 1.18's strings.Cut
More remain.

Change-Id: I6ec562cc1f687600758deae1c9d7dbd0d04004cb
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-16 14:53:59 -07:00
Brad Fitzpatrick
1f22507c06 version: use Go 1.18's git stamping as default implementation
No more manual version bumps!

Fixes #81

Change-Id: I3a9e544a7248f0b83bcbacbaabbc4dabc435e62d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-16 14:53:51 -07:00
Josh Bleecher Snyder
c2c97f8f38 go.toolchain.rev: remove second entry
No idea how that happened.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:55:26 -07:00
Josh Bleecher Snyder
26021b07ec control/controlclient: only build certstore-related code with the Tailscale Go toolchain
The certstore code is impacted by golang/go#51726.
The Tailscale Go toolchain fork contains a temporary workaround,
so it can compile it. Once the upstream toolchain can compile certstore,
presumably in Go 1.18.1, we can revert this change.

Note that depaware runs with the upstream toolchain.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
0ef74f37a5 net/dns/resolver: remove closure allocation explanation
As of Go 1.18, the register ABI list includes arm64, amd64,
ppc64, and ppc64le. This is a large enough percentage of the
architectures that it's not worth explaining.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
9482576bb1 ipn/ipnserver: use strings.Cut
We now require Go 1.18.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
97a01b7b17 util/deephash: remove Tailscale toolchain compatibility shim
The future is now.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
1b57b0380d wgengine/magicsock: remove final alloc from ReceiveFrom
And now that we don't have to play escape analysis and inlining games,
simplify the code.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
463728a885 util/netconv: add package to convert between netip and netaddr types
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
5cb9999be3 go.toolchain.rev: upgrade to our Go 1.18 fork
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
927fc36123 go.toolchain.branch: upgrade to Go 1.18
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
71b535fc94 go.mod: require Go 1.18
Also, update depaware for Go 1.18's dependency tree.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
f695f0b178 go.mod: update golang.org/x/tools and honnef.co/go/tools
This is required for staticcheck to process code
using Go 1.18.

This puts us on a random commit on the bleeding edge
of staticcheck, which isn't great, but there don't
appear to have been any releases yet that support 1.18.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
f143ff89b7 README.md: update current Go release
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
d77b4c1344 Dockerfile: require Go 1.18
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
4b1e02057a .github/workflows: request Go 1.18
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
Josh Bleecher Snyder
08cf54f386 wgengine/magicsock: fix goMajorVersion for 1.18 ts release
The version string changed slightly. Adapt.
And always check the current Go version to prevent future
accidental regressions. I would have missed this one had
I not explicitly manually checked it.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-16 12:45:28 -07:00
David Eger
5be42c0af1 cmd/tailscale: add file get options for dealing with existing files
A new flag --conflict=(skip|overwrite|rename) lets users specify
what to do when receiving files that match a same-named file in
the target directory.

Updates #3548

Signed-off-by: David Eger <david.eger@gmail.com>
2022-03-16 12:05:41 -07:00
Maisem Ali
07f48a7bfe wgengine: handle nil netmaps when assigning isSubnetRouter.
Fixes tailscale/coral#51

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-16 10:51:12 -07:00
James Tucker
858286d97f github/windows: improve caching in -race builder (#4172)
Signed-off-by: James Tucker <james@tailscale.com>
Co-authored-by: James Tucker <james@tailscale.com>
2022-03-15 10:04:02 -07:00
Brad Fitzpatrick
5f529d1359 logtail: add Logger.PrivateID accessor
For the control plane to use.

Change-Id: I0f02321fc4fa3a41c3ece3b51eee729ea9770905
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-14 20:59:04 -07:00
James Tucker
36b148c2d2 github/windows: improve cache performance (#4171)
- Remove the expanded module files, as Go can likely expand the zips
  faster than tar can expand the extra copies.
- Add the go-build cache.
- Remove the extra restore key to avoid extra cache lookups on miss.

Signed-off-by: James Tucker <james@tailscale.com>
Co-authored-by: James Tucker <james@tailscale.com>
2022-03-14 17:10:13 -07:00
Maisem Ali
45a7f6689c tailcfg: add field to allow LocalPortForwarding in SSHAction
Updates #3802, #4129

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-14 13:39:42 -07:00
Maisem Ali
98b45ef12c ssh/tailssh: add support for agent forwarding.
Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-14 13:38:53 -07:00
Brad Fitzpatrick
6e86bbcb06 ssh/tailssh: add a new sshSession type to clean up existing+future code
Updates #3802

Change-Id: I7054dca387f5e5aee1185937ecf41b77a5a07f1a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Co-authored-by: Maisem Ali <maisem@tailscale.com>
2022-03-14 12:01:49 -07:00
Maisem Ali
462e75666b ssh/tailssh: start sending the server version
Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-12 19:40:51 -08:00
Maisem Ali
bf3559171f ssh/tailssh: set DBUS_SESSION_BUS_ADDRESS and SSH_TTY variables
Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-12 19:40:51 -08:00
Maisem Ali
6d61b7906e ssh/tailssh: handle terminal opcodes
Updates #3802 #4146

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-12 17:57:07 -08:00
Maisem Ali
da6ce27416 go.mod: move from github.com/gliderlabs/ssh to github.com/tailscale/ssh
Updates #4146

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-12 17:57:07 -08:00
Brad Fitzpatrick
012098ec32 ssh/tailssh: fix terminal corruption (temporary hack)
Maisem figured out the real problem but will take several commits
(e.g. tailscale/ssh#2) in different repos to get it fixed
properly. This is an interim hack.

Details of real fix:
https://github.com/tailscale/tailscale/issues/4146#issuecomment-1065952947

Updates #4146
Updates #3802

Change-Id: I7b7dc5713baa3e5de75b87b69e7179a6e7549b0b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-12 14:19:45 -08:00
Brad Fitzpatrick
ba1adf6c24 ssh/tailssh: make pty termios options match OpenSSH
Still not sure the exact rules of how/when/who's supposed to set
these, but this works for now on making them match. Baby steps.
Will research more and adjust later.

Updates #4146 (but not enough to fix it, something's still wrong)
Updates #3802

Change-Id: I496d8cd7e31d45fe9ede88fc8894f35dc096de67
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-11 12:16:10 -08:00
Brad Fitzpatrick
1dd5cf62a5 ssh/tailssh: start login shell, fix arg passing, width/height mismatch
Updates #3802

Change-Id: I137d7a79195ee86d5dd7c8999f2797fc3cb57cec
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-10 20:11:41 -08:00
Brad Fitzpatrick
efc48b0578 ssh/tailssh, ipnlocal, controlclient: fetch next SSHAction from network
Updates #3802

Change-Id: I08e98805ab86d6bbabb6c365ed4526f54742fd8e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-10 13:41:08 -08:00
Brad Fitzpatrick
6b11004a2a control/controlclient: proactively close TLS connection after /key fetch
When using Noise.

Updates #3488

Change-Id: I1049963763075a15b72fd8065dcf44a9cf37975f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-10 13:26:18 -08:00
Aaron Klotz
f8a4df66de cmd/tailscale/cli, ipn: move exit node IP parsing and validation from cli into prefs.
We need to be able to provide the ability for the GUI clients to resolve and set
the exit node IP from an untrusted string, thus enabling the ability to specify
that information via enterprise policy.

This patch moves the relevant code out of the handler for `tailscale up`,
into a method on `Prefs` that may then be called by GUI clients.

We also update tests accordingly.

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

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-03-10 10:51:05 -07:00
Maisem Ali
888e50e1f6 ipn/ipnlocal: migrate all platforms to controlplane.tailscale.com
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-09 21:14:06 -08:00
Nick O'Neill
1625e87526 control/controlclient, localapi: shorten expiry time via localapi (#4112)
Signed-off-by: Nick O'Neill <nick@tailscale.com>
2022-03-09 14:42:42 -08:00
Maisem Ali
2bcc047d4f tailcfg: bump capVer for Noise
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-09 14:41:24 -08:00
Joonas Kuorilehto
c1b3500a05 cmd/tailscale: allow use of flags in gokrazy
Enable use of command line arguments with tailscale cli on gokrazy. Before
this change using arguments like "up" would cause tailscale cli to be
repeatedly restarted by gokrazy process supervisor.

We never want to have gokrazy restart tailscale cli, even if user would
manually start the process.

Expected usage is that user creates files:

flags/tailscale.com/cmd/tailscale/flags.txt:

    up

flags/tailscale.com/cmd/tailscaled/flags.txt:

    --statedir=/perm/tailscaled/
    --tun=userspace-networking

Then tailscale prints URL for user to log in with browser.

Alternatively it should be possible to use up with auth key to allow
unattended gokrazy installs.

Signed-off-by: Joonas Kuorilehto <joneskoo@derbian.fi>
2022-03-09 12:30:32 -08:00
Maisem Ali
2c89b3a601 control/controlbase: make Conn.Write return consumed bytes
Currently `Write` returns the number of ciphertext bytes written.
According to the docs for io.Writer, Write should return the amount
of bytes consumed from the input.
```
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
Write(p []byte) (n int, err error)
```

Fixes #4126

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-09 11:42:11 -08:00
Maisem Ali
e82a74553b control/controlclient: make MapRequests go over noise.
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-09 11:32:11 -08:00
Maisem Ali
56bf2ce642 ssh/tailssh: handle local port forwarding
Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-09 11:31:04 -08:00
Maisem Ali
598c7a22e7 ssh/tailssh: use lu.Username not lu.Name.
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 22:39:03 -08:00
Maisem Ali
06c147d848 ssh/tailssh: create login sessions for new connections
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 21:47:19 -08:00
Maisem Ali
ba2c0c3145 control/controlclient: call direct.Close after map requests are complete
This was causing a flake in another repo.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 21:17:35 -08:00
Brad Fitzpatrick
61cdcf4082 net/interfaces: add FreeBSD default route lookup (portmapping, etc)
Updates #4101 (probably fixes)

Change-Id: I2b75ee3ced276fb7b211f17c382621cf1ef882fa
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-08 13:02:02 -08:00
Maisem Ali
2fb087891b net/socks5: always close client connections after serving
Customer reported an issue where the connections were not closing, and
would instead just stay open. This commit makes it so that we close out
the connection regardless of what error we see. I've verified locally
that it fixes the issue, we should add a test for this.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 12:52:20 -08:00
Maisem Ali
91a8cdc84b control/controlclient: make Auto.Shutdown call Direct.Close
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 11:57:41 -08:00
Maisem Ali
0f37317664 control/controlclient: make RegisterRequest go over Noise
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-08 11:57:41 -08:00
Brad Fitzpatrick
c4f6df47e5 control/controlclient: fix Noise HTTP/2 regression from earlier commit
Fix regression from 21069124db caught by tests in another repo.

The HTTP/2 Transport that was being returned had a ConnPool that never
dialed.

Updates #3488

Change-Id: I3184d6393813448ae143d37ece14eb732334c05f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-08 09:50:36 -08:00
Brad Fitzpatrick
21069124db control/controlclient: fix the Noise HTTP/2 timeout config
We want to close the connection after a minute of inactivity,
not heartbeat once a minute to keep it alive forever.

Updates #3488

Change-Id: I4b5275e8d1f2528e13de2d54808773c70537db91
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-08 08:48:28 -08:00
Brad Fitzpatrick
740e3c006c cmd/derper: add --stun-port flag
And flesh out docs on the --http-port flag.

Change-Id: If9d42665f67409082081cb9a25ad74e98869337b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-08 07:19:33 -08:00
Maisem Ali
0588ca5d8b control/controlclient: make SetDNS attempt to go over Noise first
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-07 16:27:13 -08:00
Maisem Ali
da1821197a tailcfg: add SetDNSResponse
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-07 16:27:13 -08:00
Maisem Ali
0f31a0fc76 control/controlclient: add Noise client
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-07 15:43:19 -08:00
Brad Fitzpatrick
26f27a620a wgengine/router: delete legacy netfilter rule cleanup [Linux]
This was just cleanup for an ancient version of Tailscale. Any such machines
have upgraded since then.

Change-Id: Iadcde05b37c2b867f92e02ec5d2b18bf2b8f653a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-07 14:39:04 -08:00
Maisem Ali
249758df90 control/controlclient: start fetching the server noise key
Updates #3488

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-07 11:29:27 -08:00
Brad Fitzpatrick
d5f8f38ac6 tailcfg: rename map request version to "capability version"
And add a CapabilityVersion type, primarily for documentation.

This makes MapRequest.Version, RegisterRequest.Version, and
SetDNSRequest.Version all use the same version, which will avoid
confusing in the future if Register or SetDNS ever changed their
semantics on Version change. (Currently they're both always 1)

This will requre a control server change to allow a
SetDNSRequest.Version value other than 1 to be deployed first.

Change-Id: I073042a216e0d745f52ee2dbc45cf336b9f84b7c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-06 14:29:08 -08:00
Brad Fitzpatrick
105dfa1efa tailcfg: add OverTLSPublicKeyResponse for the new response from /key
Updates #3488

Change-Id: I8729cb3fb7f6dda1a874f8ae2d9570311ed158db
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-06 13:51:32 -08:00
Robert Fritzsche
0e62a7d1a2 tstime/mono: fix Before function comment
Signed-off-by: Robert Fritzsche <r.fritzsche@gridx.de>
2022-03-05 15:05:57 -08:00
Maisem Ali
c85694fac4 types/views: add ContainsExitRoutes to IPPrefixSlice
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-03-04 09:08:42 -08:00
Brad Fitzpatrick
b493ef5b71 net/tsaddr: add func ContainsExitRoutes
Change-Id: I772441a406083e2fe0f9374b2b23d89aac18928f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-04 08:56:06 -08:00
Josh Bleecher Snyder
7ddf2e2fea go.toolchain.rev: bump to Go 1.17.8
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-03-03 14:51:16 -08:00
Brad Fitzpatrick
f18bb6397b cmd/tailscale: tell gokrazy to not manage the CLI as a daemon
In the future we'll probably want to run the "tailscale web"
server instead, but for now stop the infinite restart loop.

See https://gokrazy.org/userguide/process-interface/ for details.

Updates #1866

Change-Id: I4133a5fdb859b848813972620495865727fe397a
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-01 20:35:18 -08:00
Brad Fitzpatrick
db85384f9c cmd/tailscaled: default to userspace-networking mode on gokrazy, set paths
One of the current few steps to run Tailscale on gokrazy is to
specify the --tun=userspace-networking flag:

    https://gokrazy.org/userguide/install/tailscale/

Instead, make it the default for now. Later we can change the
default to kernel mode if available and fall back to userspace
mode like Synology, once #391 is done.

Likewise, set default paths for Gokrazy, as its filesystem hierarchy
is not the Linux standard one. Instead, use the conventional paths as
documented at https://gokrazy.org/userguide/install/tailscale/.

Updates #1866

RELNOTE=default to userspace-networking mode on gokrazy

Change-Id: I3766159a294738597b4b30629d2860312dbb7609
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-01 20:34:45 -08:00
Brad Fitzpatrick
c9a5dadce8 ssh/tailssh: skip flaky test on CI for now
Updates #4051

Change-Id: I94f2165dd248eba9ca3f782c907a13bd6dde4a5e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-01 19:57:07 -08:00
Brad Fitzpatrick
58a6c9b2b8 version, hostinfo: recognize gokrazy as a distro
Now:

/tmp/breakglass3929186798 # /user/tailscale debug hostinfo
{
  "IPNVersion": "1.23.0-date.20220107",
  "OS": "linux",
  "OSVersion": "Gokrazy; kernel=5.16.11",
  "DeviceModel": "Raspberry Pi 4 Model B Rev 1.2",
  "Hostname": "gokrazy",
  "GoArch": "arm64"
}

Also, cache the distro lookup. It doesn't change while the program is
running:

name   old time/op    new time/op    delta
Get-6    5.21µs ± 5%    0.00µs ± 3%   -99.91%  (p=0.008 n=5+5)

name   old alloc/op   new alloc/op   delta
Get-6      792B ± 0%        0B       -100.00%  (p=0.008 n=5+5)

name   old allocs/op  new allocs/op  delta
Get-6      8.00 ± 0%      0.00       -100.00%  (p=0.008 n=5+5)

Updates #1866

Change-Id: Ifb9a63b94287010d3f4c8bfeb6b78119e8a9b203
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-03-01 19:37:20 -08:00
Brad Fitzpatrick
6a2e94cbeb tstime/rate: deflake TestLongRunningQPS even more
Previous de-flakings:
* 8cf1af8a07 for #3733
* 30458c71c8 for #2727

Fixes #4044

Change-Id: I506cf1ff37bb224f5a9929f1998901e60b24535d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-28 20:22:16 -08:00
Brad Fitzpatrick
55095df644 net/interfaces: get Linux default route from netlink as fallback
If it's in a non-standard table, as it is on Unifi UDM Pro, apparently.

Updates #4038 (probably fixes, but don't have hardware to verify)

Change-Id: I2cb9a098d8bb07d1a97a6045b686aca31763a937
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-28 19:57:34 -08:00
Maisem Ali
518f6cee63 ipn/store: [TestNewStore] do not use an empty file
Otherwise it would log warnings about an empty file.
```
    stores.go:138: store.NewFileStore("/tmp/3777352782"): file empty; treating it like a missing file [warning]
```

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-28 18:26:24 -08:00
Maisem Ali
497324ddf6 ipn/store: add common package for instantiating ipn.StateStores
Also move KubeStore and MemStore into their own package.

RELNOTE: tsnet now supports providing a custom ipn.StateStore.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-28 13:23:33 -08:00
Dmytro Shynkevych
d9a7205be5 net/tstun: set link speed to SPEED_UNKNOWN
Fixes #3933.

Signed-off-by: Dmytro Shynkevych <dm.shynk@gmail.com>
2022-02-27 23:11:35 -08:00
Brad Fitzpatrick
5d085a6f41 controlhttp: add some docs, change Dial's path from /switch to /ts2021
When I deployed server-side changes, I put the upgrade handler at /ts2021
instead of /switch. We could move the server to /switch, but ts2021 seems
more specific and better, but I don't feel strongly.

Updates #3488

Change-Id: Ifbf8ea60a815fd2fa1bfbe1b7af1ac2a27218354
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-26 12:44:29 -08:00
Brad Fitzpatrick
4b50977422 ssh/tailssh: add more SSH tests, blend in env from ssh session
Updates #3802

Change-Id: I568c661cacbb0524afcd8be9577457ddba611f19
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 16:02:01 -08:00
Brad Fitzpatrick
4686224e5a cmd/tailscaled: add a no-op test for profiling init-time memory allocs
Turns out we're pretty good already at init-time work in tailscaled.
The regexp/syntax shows up but it's hard to get rid of that; zstd even
uses regexp. *shrug*

Change-Id: I856aca056dcb7489f5fc22ef07f55f34ddf19bd6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 14:58:12 -08:00
Brad Fitzpatrick
4cbdc84d27 cmd/tailscaled/childproc: add be-child registration mechanism
For ssh and maybe windows service babysitter later.

Updates #3802

Change-Id: I7492b98df98971b3fb72d148ba92c2276cca491f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 14:20:20 -08:00
Brad Fitzpatrick
6e4f3614cf ssh/tailssh: add start of real ssh tests
Updates #3802

Change-Id: I9aea4250062d3a06ca7a5e71a81d31c27a988615
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 14:13:12 -08:00
Brad Fitzpatrick
c9eca9451a ssh: make it build on darwin
For local dev testing initially. Product-wise, it'll probably only be
workable on the two unsandboxed builds.

Updates #3802

Change-Id: Ic352f966e7fb29aff897217d79b383131bf3f92b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 13:00:45 -08:00
Brad Fitzpatrick
c4a6d9fa5d ipn/ipnlocal: generate tailscaled-owned SSH keys as needed
Updates #3802

Change-Id: Ie1bc9ae3f3639603b88b4e19b7eb12bea528ff77
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 12:15:57 -08:00
Brad Fitzpatrick
cce6aad6c0 ssh/tailssh: fix non-interactive commands as non-root user
Updates #3802

Change-Id: I89a3f14420b8782bc407b1939dce54a1d24636da
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 12:13:16 -08:00
Brad Fitzpatrick
e2ed06c53c ssh/tailssh: break a method into half in prep for testing
And add a private context type in the process.

Updates #3802

Change-Id: I257187f4cfb0f2248d95b81c1dfe0911ef203b60
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 09:59:00 -08:00
Brad Fitzpatrick
1b5bb2e81d ssh/tailssh: rename sshContext to sshConnInfo
So it's not confused for a context.Context and we can add contexts
later and not look like we have two.

Updates #3802

Change-Id: Icf229ae2c020d173f3cbf09a13ccd03a60cbb85e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-24 09:06:21 -08:00
Denton Gentry
8175504584 VERSION.txt: This is 1.23.
Signed-off-by: Denton Gentry <dgentry@tailscale.com>
2022-02-23 15:51:28 -08:00
Brad Fitzpatrick
3c2cd854be ssh/tailssh: flesh out env, support non-pty commands
Updates #3802

Change-Id: I7022460117542a5424919144828bf571c7c19ec0
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-23 15:00:41 -08:00
Brad Fitzpatrick
7d897229d9 net/dns: ignore permission errors on Synology DSM7 for now
Updates #4017

Change-Id: Ia7fd4df47588c010dea8e63d88f397cc8eb748e5
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-23 10:13:53 -08:00
Brad Fitzpatrick
29279b34fa cmd/tailscale: make configure-host on Synology also add CAP_NET_RAW
Updates #4012

Change-Id: Ic45b5709a73b4f1cd466823e177b52d1d20ba84e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-23 07:56:38 -08:00
Maisem Ali
38c59c0ad2 tsnet: fix typo in Ephemeral
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-22 15:35:24 -08:00
Brad Fitzpatrick
bb94561c96 net/netutil: fix regression where peerapi would get closed after 1st req
I introduced a bug in 8fe503057d when unifying oneConnListener
implementations.

The NewOneConnListenerFrom API was easy to misuse (its Close method
closes the underlying Listener), and we did (via http.Serve, which
closes the listener after use, which meant we were close the peerapi's
listener, even though we only wanted its Addr)

Instead, combine those two constructors into one and pass in the Addr
explicitly, without delegating through to any Listener.

Change-Id: I061d7e5f842e0cada416e7b2dd62100d4f987125
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-22 13:52:18 -08:00
Aaron Klotz
e31d68d64e hostinfo: use the sentinel value set by the MSI installer to detect MSI package type
The MSI installer sets a special sentinel value that we can use to detect it.

I also removed the code that bails out when the installation path is not
`Program Files`, as both the NSIS and MSI installers permit the user to install
to a different path.

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
2022-02-22 12:48:20 -07:00
Brad Fitzpatrick
4fee321004 hostinfo: move packageType out to platform-specific files
Change-Id: I3236b3d4e2376dd7e2482c2562817b1b6f44872e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-22 10:23:13 -08:00
Maisem Ali
c7a8f0992d ipn/ipnlocal: use views for Peer.PrimaryRoutes and Peer.Tags
RELNOTE=`tailscale status --json` now shows Tags and PrimaryRoutes

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-22 10:09:02 -08:00
Maisem Ali
9cbb0913be ipn/{ipnlocal,ipnstate}: add Tags and PrimaryRoutes to PeerStatus
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-21 20:06:48 -08:00
David Anderson
0fc1479633 go.mod: update github.com/mdlayher/netlink to 1.6.0
This unbreaks some downstream users of tailscale who end up
with build errors from importing a v0 indirect dependency.

Signed-off-by: David Anderson <danderson@tailscale.com>
2022-02-21 14:31:03 -08:00
Brad Fitzpatrick
e921e1b02d cmd/tailscale: add "tailscale debug hostinfo" subcommand
Change-Id: Ifa09364d42e0516fdf80feddaf33c95880228049
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-20 08:06:37 -08:00
Brad Fitzpatrick
300d897fd7 hostinfo: detect NSIS vs MSI package type on Windows
Change-Id: I624a4cb04803e483553eb53c952060393029c435
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-20 07:58:49 -08:00
Brad Fitzpatrick
d19a63ddf6 ipn/localapi: treat ACME "invalid" state as terminal, log more
Fixes #3975

Change-Id: Idb2cc8d4730e140939898c7dcc15c2014acca142
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-19 16:22:58 -08:00
Brad Fitzpatrick
de72a1f9fc ipn/ipnserver: let TS_PERMIT_CERT_UID contain a username too, not just uid
Don't make users map their system's "caddy" (or whatever) system user
to its userid. We can do that. Support either a uid or a username.

RELNOTE=TS_PERMIT_CERT_UID can contain a uid or username

Change-Id: I7451b537a5e118b818addf1353882291d5f0d07f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-19 16:22:43 -08:00
Brad Fitzpatrick
03caa95bf2 ssh/tailssh: get login shell when running as non-root
And also reject attempts to use other users.

Updates #3802

Change-Id: Iddc85f6ea2dba17d12be66a50408d24c1f92833e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-18 19:22:11 -08:00
Brad Fitzpatrick
e1e20f6d39 ssh/tailssh: evaluate tailcfg.SSHPolicy on incoming connections
Updates #3802
Fixes #3960

Change-Id: Ieda2007d462ddce6c217b958167417ae9755774e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-18 18:07:39 -08:00
Josh Bleecher Snyder
66f5aa6814 types/logger: add more reserved top level field names
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-18 15:21:35 -08:00
Maisem Ali
f9a50779e2 cmd/tailscaled: add -state=mem: to support creation of an ephemeral node.
RELNOTE=`tailscaled --state=mem:` registers as an ephemeral node and
does not store state to disk.

Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-18 13:40:39 -08:00
Josh Bleecher Snyder
823d970d60 control/controlclient: use structured logging for MapResponse.ControlTime
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-18 13:05:42 -08:00
Brad Fitzpatrick
84138450a4 types/logger, logtail: add mechanism to do structured JSON logs
e.g. the change to ipnlocal in this commit ultimately logs out:

{"logtail":{"client_time":"2022-02-17T20:40:30.511381153-08:00","server_time":"2022-02-18T04:40:31.057771504Z"},"type":"Hostinfo","val":{"GoArch":"amd64","Hostname":"tsdev","IPNVersion":"1.21.0-date.20220107","OS":"linux","OSVersion":"Debian 11.2 (bullseye); kernel=5.10.0-10-amd64"},"v":1}

Change-Id: I668646b19aeae4a2fed05170d7b279456829c844
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-18 12:42:06 -08:00
Josh Bleecher Snyder
8c3c5e80b7 tailcfg: make MapResponse.ControlTime a pointer
Otherwise omitempty doesn't work.

This is wire-compatible with a non-pointer type, so switching
is safe, now and in the future.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-18 10:37:27 -08:00
Brad Fitzpatrick
bb93e29d5c tailcfg, ipn/ipnlocal: add Hostinfo.SSH_HostKeys, send when SSH enabled
(The name SSH_HostKeys is bad but SSHHostKeys is worse.)

Updates #3802

Change-Id: I2a889019c9e8b065b668dd58140db4fcab868a91
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-17 15:46:57 -08:00
Josh Bleecher Snyder
4609096271 tailcfg: fix stale docs for MapResponse.KeepAlive
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-17 15:15:43 -08:00
Brad Fitzpatrick
dd6472d4e8 api: document preauthorized auth keys
Fixes #2120

Change-Id: If6a803680b544df1f70449c26fd0f5e15940226b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-17 14:27:39 -08:00
Brad Fitzpatrick
fbff1555fc ipnlocal, tailssh: start moving host key stuff into the right spot
Make tailssh ask LocalBackend for the SSH hostkeys, as we'll need to
distribute them to peers.

For now only the hacky use-same-as-actual-host mode is implemented.

Updates #3802

Change-Id: I819dcb25c14e42e6692c441186c1dc744441592b
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-17 14:01:50 -08:00
Josh Bleecher Snyder
94409db7e2 cmd/tailscale: rewrite --authkey to --auth-key
That way humans don't have to remember which is correct.

RELNOTE=--auth-key is the new --authkey, but --authkey still works

Updates tailscale/corp#3486

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-17 10:00:46 -08:00
Xe Iaso
a45f8accdb scripts/installer: add Ubuntu 22.04 LTS Jammy Jellyfish (#3955)
Signed-off-by: Xe Iaso <xe@tailscale.com>
2022-02-17 09:52:35 -05:00
Josh Bleecher Snyder
8cf6d0a17b tailcfg: add MapResponse.ControlTime field
And log it when provided in map responses.

The test uses the date on which I joined Tailscale. :)

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-16 20:18:03 -08:00
Maisem Ali
72d8672ef7 tailcfg: make Node.Hostinfo a HostinfoView
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-16 12:55:57 -08:00
Maisem Ali
53998e26a6 tailcfg: introduce HostinfoView
Signed-off-by: Maisem Ali <maisem@tailscale.com>
2022-02-16 12:55:57 -08:00
Brad Fitzpatrick
2ff481ff10 net/dns: add health check for particular broken-ish Linux DNS config
Updates #3937 (need to write docs before closing)

Change-Id: I1df7244cfbb0303481e2621ee750d21358bd67c6
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-16 10:40:04 -08:00
Brad Fitzpatrick
57115e923e tailcfg: add start of SSH policy to be sent from control plane to nodes
Updates #3802

Change-Id: Iec58f35d445aaa267d0f7e7e2f30c049c1df4c0e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-15 16:05:42 -08:00
Josh Bleecher Snyder
b486448ab9 go.toolchain.rev: bump to Go 1.17.7
Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
2022-02-15 13:29:17 -08:00
Brad Fitzpatrick
1b87e025e9 ssh/tailssh: move SSH code from wgengine/netstack to this new package
Still largely incomplete, but in a better home now.

Updates #3802

Change-Id: I46c5ffdeb12e306879af801b06266839157bc624
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-15 12:21:01 -08:00
Ross Zurowski
6d02a48d8d ipn: add TailnetStatus field to tailscale status --json (#3865)
We need to capture some tailnet-related information for some Docker
features we're building. This exposes the tailnet name and MagicDNS
information via `tailscale status --json`.

Fixes tailscale/corp#3670

Signed-off-by: Ross Zurowski <ross@rosszurowski.com>
2022-02-15 12:36:01 -05:00
Brad Fitzpatrick
c988bd6ed1 net/dns/resolvconffile: unify three /etc/resolv.conf parsers into new package
Change-Id: I2120893ca802d12f1bd0407d49077d3672627d33
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-02-14 20:55:57 -08:00
228 changed files with 7415 additions and 1790 deletions

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
- name: Check out code
uses: actions/checkout@v1

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env sh
#
# This is a temporary hack to work around
# https://github.com/golang/go/issues/51629 , wherein the stringer
# generator doesn't work with generics.
#
# This script is the equivalent of `go generate ./...`, except that it
# only runs generate on packages that don't try to use stringer.
set -e
find . -name '*.go' | xargs grep -l go:generate | xargs -n1 dirname | sort -u | while read dir; do
if ! egrep "cmd/(stringer|cloner)" $dir/*.go; then
set -x
go generate -tags=hermetic $dir
set +x
fi
done

View File

@@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
- name: Check out code
uses: actions/checkout@v2
@@ -25,14 +25,13 @@ jobs:
fetch-depth: 0
- name: check 'go generate' is clean
# The shell script invocation below is a temporary hack for
# https://github.com/tailscale/tailscale/issues/4194. When
# that issue is fixed, replace its invocation with:
# go generate --tags=hermetic ./...
run: |
if [[ "${{github.ref}}" == release-branch/* ]]
then
pkgs=$(go list ./... | grep -v dnsfallback)
else
pkgs=$(go list ./... | grep -v dnsfallback)
fi
go generate $pkgs
set -e
./.github/workflows/go-generate-without-stringer.sh
echo
echo
git diff --name-only --exit-code || (echo "The files above need updating. Please run 'go generate'."; exit 1)

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
- name: Check out code
uses: actions/checkout@v1

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -19,7 +19,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
id: go
- name: Check out code into the Go module directory

View File

@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
- name: Check out code
uses: actions/checkout@v1

View File

@@ -18,7 +18,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17
go-version: 1.18
- name: Checkout Code
uses: actions/checkout@v1

View File

@@ -19,7 +19,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17.x
go-version: 1.18.x
- name: Checkout code
uses: actions/checkout@v2
@@ -27,10 +27,21 @@ jobs:
- name: Restore Cache
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# 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: Test with -race flag
# Don't use -bench=. -benchtime=1x.

View File

@@ -19,7 +19,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2.1.5
with:
go-version: 1.17.x
go-version: 1.18.x
- name: Checkout code
uses: actions/checkout@v2
@@ -27,10 +27,18 @@ jobs:
- name: Restore Cache
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# 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).
# TODO(raggi): add a go version here.
key: ${{ runner.os }}-go-2-${{ hashFiles('**/go.sum') }}
- name: Test
# Don't use -bench=. -benchtime=1x.

View File

@@ -32,7 +32,7 @@
# $ docker exec tailscaled tailscale status
FROM golang:1.17-alpine AS build-env
FROM golang:1.18-alpine AS build-env
WORKDIR /go/src/tailscale

View File

@@ -6,24 +6,24 @@ usage:
echo "See Makefile"
vet:
go vet ./...
./tool/go vet ./...
updatedeps:
go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscaled
go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscale
./tool/go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscaled
./tool/go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscale
depaware:
go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
./tool/go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
./tool/go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
buildwindows:
GOOS=windows GOARCH=amd64 go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
GOOS=windows GOARCH=amd64 ./tool/go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
build386:
GOOS=linux GOARCH=386 go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
GOOS=linux GOARCH=386 ./tool/go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
buildlinuxarm:
GOOS=linux GOARCH=arm go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
GOOS=linux GOARCH=arm ./tool/go install tailscale.com/cmd/tailscale tailscale.com/cmd/tailscaled
buildmultiarchimage:
./build_docker.sh
@@ -31,7 +31,7 @@ buildmultiarchimage:
check: staticcheck vet depaware buildwindows build386 buildlinuxarm
staticcheck:
go run honnef.co/go/tools/cmd/staticcheck -- $$(go list ./... | grep -v tempfork)
./tool/go run honnef.co/go/tools/cmd/staticcheck -- $$(./tool/go list ./... | grep -v tempfork)
spk:
PATH="${PWD}/tool:${PATH}" ./tool/go run github.com/tailscale/tailscale-synology@main -o tailscale.spk --source=. --goarch=${SYNO_ARCH} --dsm-version=${SYNO_DSM}

View File

@@ -44,7 +44,7 @@ If your distro has conventions that preclude the use of
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.17) in module mode. It might
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.

View File

@@ -1 +1 @@
1.21.0
1.23.0

7
api.md
View File

@@ -317,9 +317,14 @@ Allows for updating properties on the device key.
- Provide `false` to enable the device's key expiry. Sets the key to expire at the original expiry time prior to disabling. The key may already have expired. In that case, the device must be re-authenticated.
- Empty value will not change the key expiry.
`preauthorized`
- If `true`, don't require machine authorization (if enabled on the tailnet)
```
{
"keyExpiryDisabled": true
"keyExpiryDisabled": true,
"preauthorized": true
}
```

View File

@@ -80,7 +80,7 @@ func (b *BIRDClient) EnableProtocol(protocol string) error {
// Reply codes starting with 0 stand for action successfully completed messages,
// 1 means table entry, 8 runtime error and 9 syntax error.
func (b *BIRDClient) exec(cmd string, args ...interface{}) (string, error) {
func (b *BIRDClient) exec(cmd string, args ...any) (string, error) {
if _, err := fmt.Fprintf(b.conn, cmd, args...); err != nil {
return "", err
}

View File

@@ -0,0 +1,12 @@
// 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 !go1.18
// +build !go1.18
package tailscale
func init() {
you_need_Go_1_18_to_compile_Tailscale()
}

View File

@@ -2,6 +2,9 @@
// 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
// Package tailscale contains Tailscale client code.
package tailscale
@@ -113,10 +116,7 @@ func doLocalRequestNiceError(req *http.Request) (*http.Response, error) {
if ue, ok := err.(*url.Error); ok {
if oe, ok := ue.Err.(*net.OpError); ok && oe.Op == "dial" {
path := req.URL.Path
pathPrefix := path
if i := strings.Index(path, "?"); i != -1 {
pathPrefix = path[:i]
}
pathPrefix, _, _ := strings.Cut(path, "?")
return nil, fmt.Errorf("Failed to connect to local Tailscale daemon for %s; %s Error: %w", pathPrefix, tailscaledConnectHint(), oe)
}
}
@@ -517,8 +517,8 @@ func tailscaledConnectHint() string {
// SubState=dead
st := map[string]string{}
for _, line := range strings.Split(string(out), "\n") {
if i := strings.Index(line, "="); i != -1 {
st[line[:i]] = strings.TrimSpace(line[i+1:])
if k, v, ok := strings.Cut(line, "="); ok {
st[k] = strings.TrimSpace(v)
}
}
if st["LoadState"] == "loaded" &&

View File

@@ -69,14 +69,14 @@ func main() {
gen(buf, imports, typ, pkg.Types)
}
w := func(format string, args ...interface{}) {
w := func(format string, args ...any) {
fmt.Fprintf(buf, format+"\n", args...)
}
if *flagCloneFunc {
w("// Clone duplicates src into dst and reports whether it succeeded.")
w("// To succeed, <src, dst> must be of types <*T, *T> or <*T, **T>,")
w("// where T is one of %s.", *flagTypes)
w("func Clone(dst, src interface{}) bool {")
w("func Clone(dst, src any) bool {")
w(" switch src := src.(type) {")
for _, typeName := range typeNames {
w(" case *%s:", typeName)
@@ -158,7 +158,7 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
fmt.Fprintf(buf, "// Clone makes a deep copy of %s.\n", name)
fmt.Fprintf(buf, "// The result aliases no memory with the original.\n")
fmt.Fprintf(buf, "func (src *%s) Clone() *%s {\n", name, name)
writef := func(format string, args ...interface{}) {
writef := func(format string, args ...any) {
fmt.Fprintf(buf, "\t"+format+"\n", args...)
}
writef("if src == nil {")
@@ -172,9 +172,15 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
if !codegen.ContainsPointers(ft) {
continue
}
if named, _ := ft.(*types.Named); named != nil && !hasBasicUnderlying(ft) {
writef("dst.%s = *src.%s.Clone()", fname, fname)
continue
if named, _ := ft.(*types.Named); named != nil {
if isViewType(ft) {
writef("dst.%s = src.%s", fname, fname)
continue
}
if !hasBasicUnderlying(ft) {
writef("dst.%s = *src.%s.Clone()", fname, fname)
continue
}
}
switch ft := ft.Underlying().(type) {
case *types.Slice:
@@ -234,6 +240,17 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, typ *types.Named, thisP
buf.Write(codegen.AssertStructUnchanged(t, thisPkg, name, "Clone", imports))
}
func isViewType(typ types.Type) bool {
t, ok := typ.Underlying().(*types.Struct)
if !ok {
return false
}
if t.NumFields() != 1 {
return false
}
return t.Field(0).Name() == "ж"
}
func hasBasicUnderlying(typ types.Type) bool {
switch typ.Underlying().(type) {
case *types.Slice, *types.Map:

View File

@@ -39,7 +39,8 @@ 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")
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")
@@ -212,7 +213,7 @@ func main() {
debug.Handle("traffic", "Traffic check", http.HandlerFunc(s.ServeDebugTraffic))
if *runSTUN {
go serveSTUN(listenHost)
go serveSTUN(listenHost, *stunPort)
}
httpsrv := &http.Server{
@@ -322,8 +323,8 @@ func probeHandler(w http.ResponseWriter, r *http.Request) {
}
}
func serveSTUN(host string) {
pc, err := net.ListenPacket("udp", net.JoinHostPort(host, "3478"))
func serveSTUN(host string, port int) {
pc, err := net.ListenPacket("udp", net.JoinHostPort(host, fmt.Sprint(port)))
if err != nil {
log.Fatalf("failed to open STUN listener: %v", err)
}

View File

@@ -19,7 +19,9 @@ import (
"log"
"net"
"net/http"
"os"
"sort"
"strings"
"sync"
"time"
@@ -54,7 +56,15 @@ var (
func main() {
flag.Parse()
// proactively load the DERP map. Nothing terrible happens if this fails, so we ignore
// the error. The Slack bot will print a notification that the DERP map was empty.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, _ = getDERPMap(ctx)
go probeLoop()
go slackLoop()
log.Fatal(http.ListenAndServe(*listen, http.HandlerFunc(serve)))
}
@@ -68,11 +78,11 @@ type overallStatus struct {
good, bad []string
}
func (st *overallStatus) addBadf(format string, a ...interface{}) {
func (st *overallStatus) addBadf(format string, a ...any) {
st.bad = append(st.bad, fmt.Sprintf(format, a...))
}
func (st *overallStatus) addGoodf(format string, a ...interface{}) {
func (st *overallStatus) addGoodf(format string, a ...any) {
st.good = append(st.good, fmt.Sprintf(format, a...))
}
@@ -138,10 +148,14 @@ func getOverallStatus() (o overallStatus) {
func serve(w http.ResponseWriter, r *http.Request) {
st := getOverallStatus()
summary := "All good"
if len(st.bad) > 0 {
if (float64(len(st.bad)) / float64(len(st.bad)+len(st.good))) > 0.25 {
// This will generate an alert and page a human.
// It also ends up in Slack, but as part of the alert handling pipeline not
// because we generated a Slack notification from here.
w.WriteHeader(500)
summary = fmt.Sprintf("%d problems", len(st.bad))
}
io.WriteString(w, "<html><head><style>.bad { font-weight: bold; color: #700; }</style></head>\n")
fmt.Fprintf(w, "<body><h1>derp probe</h1>\n%s:<ul>", summary)
for _, s := range st.bad {
@@ -153,6 +167,71 @@ func serve(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "</ul></body></html>\n")
}
func notifySlack(text string) error {
type SlackRequestBody struct {
Text string `json:"text"`
}
slackBody, err := json.Marshal(SlackRequestBody{Text: text})
if err != nil {
return err
}
webhookUrl := os.Getenv("SLACK_WEBHOOK")
if webhookUrl == "" {
return errors.New("No SLACK_WEBHOOK configured")
}
req, err := http.NewRequest("POST", webhookUrl, bytes.NewReader(slackBody))
if err != nil {
return err
}
req.Header.Add("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New(resp.Status)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != "ok" {
return errors.New("Non-ok response returned from Slack")
}
return nil
}
// We only page a human if it looks like there is a significant outage across multiple regions.
// To Slack, we report all failures great and small.
func slackLoop() {
inBadState := false
for {
time.Sleep(time.Second * 30)
st := getOverallStatus()
if len(st.bad) > 0 && !inBadState {
err := notifySlack(strings.Join(st.bad, "\n"))
if err == nil {
inBadState = true
} else {
log.Printf("%d problems, notify Slack failed: %v", len(st.bad), err)
}
}
if len(st.bad) == 0 && inBadState {
err := notifySlack("All DERPs recovered.")
if err == nil {
inBadState = false
}
}
}
}
func sortedRegions(dm *tailcfg.DERPMap) []*tailcfg.DERPRegion {
ret := make([]*tailcfg.DERPRegion, 0, len(dm.Regions))
for _, r := range dm.Regions {
@@ -347,7 +426,7 @@ func probeNodePair(ctx context.Context, dm *tailcfg.DERPMap, from, to *tailcfg.D
}
// Receive the random packet.
recvc := make(chan interface{}, 1) // either derp.ReceivedPacket or error
recvc := make(chan any, 1) // either derp.ReceivedPacket or error
go func() {
for {
m, err := toc.Recv()

View File

@@ -196,7 +196,7 @@ func root(w http.ResponseWriter, r *http.Request) {
LoginName: who.UserProfile.LoginName,
ProfilePicURL: who.UserProfile.ProfilePicURL,
MachineName: firstLabel(who.Node.ComputedName),
MachineOS: who.Node.Hostinfo.OS,
MachineOS: who.Node.Hostinfo.OS(),
IP: tailscaleIP(who),
}
}
@@ -206,8 +206,6 @@ func root(w http.ResponseWriter, r *http.Request) {
// firstLabel s up until the first period, if any.
func firstLabel(s string) string {
if i := strings.Index(s, "."); i != -1 {
return s[:i]
}
s, _, _ = strings.Cut(s, ".")
return s
}

231
cmd/tailscale/cli/admin.go Normal file
View File

@@ -0,0 +1,231 @@
// 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.
// Admin commands.
package cli
import (
"bytes"
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale"
)
var adminCmd = &ffcli.Command{
Name: "admin",
Exec: runAdmin,
LongHelp: `"tailscale admin" contains admin commands to manage a Tailscale network.`,
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("admin")
fs.StringVar(&adminArgs.apiBase, "api-server", "https://api.tailscale.com", "which Tailscale server instance to use. Ignored when --token-file is empty.")
fs.StringVar(&adminArgs.tokenFile, "token-file", "", "if non-empty, filename containing API token to use. If empty, authentication is done via the active Tailscale control plane connection.")
fs.StringVar(&adminArgs.tailnet, "tailnet", "", "Tailnet to query or edit. Required if token-file is used. Must be blank if token-file is blank, in which case the tailnet used is the same as the active tailnet.")
return fs
})(),
Subcommands: []*ffcli.Command{
newTailnetACLGetCmd(),
newTailnetDeviceListCmd(),
newTailnetKeyListCmd(),
},
}
var adminArgs struct {
tokenFile string
tailnet string
apiBase string
}
func runAdmin(ctx context.Context, args []string) error {
if len(args) > 0 {
return errors.New("unknown command; see 'tailscale admin --help'")
}
return errors.New("see 'tailscale admin --help'")
}
type adminClient struct {
apiBase string // e.g. "https://api.tailscale.com"
token string // non-empty if using token-based auth
hc *http.Client
tailnet string // always non-empty
}
func getAdminHTTPClient() (*adminClient, error) {
tokenFile := adminArgs.tokenFile
tailnet := adminArgs.tailnet
apiBase := adminArgs.apiBase
if (tokenFile != "") != (tailnet != "") {
return nil, errors.New("--token-file and --tailnet must both be blank or both be specified")
}
if tailnet == "" {
st, err := tailscale.StatusWithoutPeers(context.Background())
if err != nil {
return nil, err
}
if st.BackendState != "Running" {
return nil, fmt.Errorf("Tailscale must be running; currently in state %q", st.BackendState)
}
if st.CurrentTailnet == nil {
return nil, fmt.Errorf("no CurrentTailnet in status")
}
tailnet = st.CurrentTailnet.Name
// TODO(bradfitz): put apiBase in *ipnstate.TailnetStatus? update apiBase here?
}
ac := &adminClient{
tailnet: tailnet,
apiBase: apiBase,
}
if tokenFile != "" {
v, err := os.ReadFile(tokenFile)
if err != nil {
return nil, err
}
token := strings.TrimSpace(string(v))
if token == "" || strings.Contains(token, "\n") {
return nil, fmt.Errorf("expect exactly 1 line in API token file %v", tokenFile)
}
ac.token = token
ac.hc = http.DefaultClient
} else {
// Otherwise, proxy via the local tailscaled and use its identity.
ac.hc = &http.Client{Transport: apiViaTailscaledTransport{}}
ac.apiBase = "http://local-tailscaled.sock"
}
return ac, nil
}
func newTailnetDeviceListCmd() *ffcli.Command {
var fields string
const sub = "tailnet-device-list"
fs := newFlagSet(sub)
fs.StringVar(&fields, "fields", "default", "comma-separated fields to include in response or 'default', 'all'")
return &ffcli.Command{
Name: sub,
ShortHelp: "list devices",
FlagSet: fs,
Exec: func(ctx context.Context, args []string) error {
ac, err := getAdminHTTPClient()
if err != nil {
return err
}
q := url.Values{"fields": []string{fields}}
return writeResJSON(ac.hc.Get(ac.apiBase + "/api/v2/tailnet/" + ac.tailnet + "/devices?" + q.Encode()))
},
}
}
func newTailnetKeyListCmd() *ffcli.Command {
const sub = "tailnet-key-list"
return &ffcli.Command{
Name: sub,
ShortHelp: "list keys or specific key (with keyID as argument)",
Exec: func(ctx context.Context, args []string) error {
var suf string
if len(args) == 1 {
suf = "/" + args[0]
} else if len(args) > 1 {
return errors.New("too many arguments")
}
ac, err := getAdminHTTPClient()
if err != nil {
return err
}
return writeResJSON(ac.hc.Get(ac.apiBase + "/api/v2/tailnet/" + ac.tailnet + "/keys" + suf))
},
}
}
func newTailnetACLGetCmd() *ffcli.Command {
var asJSON bool // true is JSON, false is HuJSON
const sub = "tailnet-acl-get"
fs := newFlagSet(sub)
fs.BoolVar(&asJSON, "json", false, "if true, return ACL is JSON format. The default of false means to use the original HuJSON JSON superset form that allows comments and trailing commas.")
return &ffcli.Command{
Name: sub,
ShortHelp: "list Tailnet ACL/config policy",
FlagSet: fs,
Exec: func(ctx context.Context, args []string) error {
ac, err := getAdminHTTPClient()
if err != nil {
return err
}
req, err := http.NewRequest("GET", ac.apiBase+"/api/v2/tailnet/"+ac.tailnet+"/acl", nil)
if err != nil {
return err
}
if asJSON {
req.Header.Set("Accept", "application/json")
}
res, err := ac.hc.Do(req)
if err != nil {
return err
}
if asJSON {
return writeResJSON(res, err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
body, _ := io.ReadAll(res.Body)
return fmt.Errorf("%v: %s", res.Status, body)
}
all, err := io.ReadAll(res.Body)
if err != nil {
return err
}
var buf bytes.Buffer
buf.Write(all)
ensureTrailingNewline(&buf)
os.Stdout.Write(buf.Bytes())
return nil
},
}
}
// apiViaTailscaledTransport is an http.RoundTripper that makes
// Tailscale API HTTP requests via the localapi to tailscaled,
// which then forwards them on over Noise.
type apiViaTailscaledTransport struct{}
func (apiViaTailscaledTransport) RoundTrip(r *http.Request) (*http.Response, error) {
return tailscale.DoLocalRequest(r)
}
func ensureTrailingNewline(buf *bytes.Buffer) {
if buf.Len() > 0 && buf.Bytes()[buf.Len()-1] != '\n' {
buf.WriteByte('\n')
}
}
func writeResJSON(res *http.Response, err error) error {
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
body, _ := io.ReadAll(res.Body)
return fmt.Errorf("%v: %s", res.Status, body)
}
all, err := io.ReadAll(res.Body)
if err != nil {
return err
}
var buf bytes.Buffer
if err := json.Indent(&buf, all, "", "\t"); err != nil {
return err
}
ensureTrailingNewline(&buf)
os.Stdout.Write(buf.Bytes())
return nil
}

View File

@@ -79,7 +79,7 @@ func runCert(ctx context.Context, args []string) error {
}
domain := args[0]
printf := func(format string, a ...interface{}) {
printf := func(format string, a ...any) {
printf(format, a...)
}
if certArgs.certFile == "-" || certArgs.keyFile == "-" {

View File

@@ -35,7 +35,7 @@ import (
var Stderr io.Writer = os.Stderr
var Stdout io.Writer = os.Stdout
func printf(format string, a ...interface{}) {
func printf(format string, a ...any) {
fmt.Fprintf(Stdout, format, a...)
}
@@ -44,7 +44,7 @@ func printf(format string, a ...interface{}) {
//
// It's not named println because that looks like the Go built-in
// which goes to stderr and formats slightly differently.
func outln(a ...interface{}) {
func outln(a ...any) {
fmt.Fprintln(Stdout, a...)
}
@@ -104,11 +104,43 @@ func newFlagSet(name string) *flag.FlagSet {
return fs
}
// CleanUpArgs rewrites command line arguments for simplicity and backwards compatibility.
// In particular, it rewrites --authkey to --auth-key.
func CleanUpArgs(args []string) []string {
out := make([]string, 0, len(args))
for _, arg := range args {
// Rewrite --authkey to --auth-key, and --authkey=x to --auth-key=x,
// and the same for the -authkey variant.
switch {
case arg == "--authkey", arg == "-authkey":
arg = "--auth-key"
case strings.HasPrefix(arg, "--authkey="), strings.HasPrefix(arg, "-authkey="):
arg = strings.TrimLeft(arg, "-")
arg = strings.TrimPrefix(arg, "authkey=")
arg = "--auth-key=" + arg
}
out = append(out, arg)
}
return out
}
// Run runs the CLI. The args do not include the binary name.
func Run(args []string) error {
func Run(args []string) (err error) {
if len(args) == 1 && (args[0] == "-V" || args[0] == "--version") {
args = []string{"version"}
}
if runtime.GOOS == "linux" && distro.Get() == distro.Gokrazy &&
os.Getenv("GOKRAZY_FIRST_START") == "1" {
defer func() {
// Exit with 125 otherwise the CLI binary is restarted
// forever in a loop by the Gokrazy process supervisor.
// See https://gokrazy.org/userguide/process-interface/
if err != nil {
log.Println(err)
}
os.Exit(125)
}()
}
var warnOnce sync.Once
tailscale.SetVersionMismatchHandler(func(clientVer, serverVer string) {
@@ -143,6 +175,7 @@ change in the future.
fileCmd,
bugReportCmd,
certCmd,
adminCmd,
},
FlagSet: rootfs,
Exec: func(context.Context, []string) error { return flag.ErrHelp },
@@ -174,7 +207,7 @@ change in the future.
}
})
err := rootCmd.Run(context.Background())
err = rootCmd.Run(context.Background())
if tailscale.IsAccessDeniedError(err) && os.Getuid() != 0 && runtime.GOOS != "windows" {
return fmt.Errorf("%v\n\nUse 'sudo tailscale %s' or 'tailscale up --operator=$USER' to not require root.", err, strings.Join(args, " "))
}
@@ -184,7 +217,7 @@ change in the future.
return err
}
func fatalf(format string, a ...interface{}) {
func fatalf(format string, a ...any) {
if Fatalf != nil {
Fatalf(format, a...)
return
@@ -194,7 +227,7 @@ func fatalf(format string, a ...interface{}) {
}
// Fatalf, if non-nil, is used instead of log.Fatalf.
var Fatalf func(format string, a ...interface{})
var Fatalf func(format string, a ...any)
var rootArgs struct {
socket string

View File

@@ -13,12 +13,12 @@ import (
"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"
"tailscale.com/types/key"
"tailscale.com/types/persist"
"tailscale.com/types/preftype"
"tailscale.com/version/distro"
@@ -380,7 +380,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
Hostname: "foo",
},
want: accidentalUpPrefix + " --authkey=secretrand --force-reauth=false --reset --hostname=foo",
want: accidentalUpPrefix + " --auth-key=secretrand --force-reauth=false --reset --hostname=foo",
},
{
name: "error_exit_node_omit_with_ip_pref",
@@ -487,7 +487,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
}
var upArgs upArgsT
flagSet := newUpFlagSet(goos, &upArgs)
flagSet.Parse(tt.flags)
flags := CleanUpArgs(tt.flags)
flagSet.Parse(flags)
newPrefs, err := prefsFromUpArgs(upArgs, t.Logf, new(ipnstate.Status), goos)
if err != nil {
t.Fatal(err)
@@ -630,7 +631,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
st: &ipnstate.Status{
TailscaleIPs: []netaddr.IP{netaddr.MustParseIP("100.105.106.107")},
},
wantErr: `cannot use 100.105.106.107 as the exit node as it is a local IP address to this machine, did you mean --advertise-exit-node?`,
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?`,
},
{
name: "warn_linux_netfilter_nodivert",
@@ -858,7 +859,8 @@ func TestUpdatePrefs(t *testing.T) {
tt.env.goos = "linux"
}
tt.env.flagSet = newUpFlagSet(tt.env.goos, &tt.env.upArgs)
tt.env.flagSet.Parse(tt.flags)
flags := CleanUpArgs(tt.flags)
tt.env.flagSet.Parse(flags)
newPrefs, err := prefsFromUpArgs(tt.env.upArgs, t.Logf, new(ipnstate.Status), tt.env.goos)
if err != nil {
@@ -894,132 +896,28 @@ var cmpIP = cmp.Comparer(func(a, b netaddr.IP) bool {
return a == b
})
func TestExitNodeIPOfArg(t *testing.T) {
mustIP := netaddr.MustParseIP
func TestCleanUpArgs(t *testing.T) {
c := qt.New(t)
tests := []struct {
name string
arg string
st *ipnstate.Status
want netaddr.IP
wantErr string
in []string
want []string
}{
{
name: "ip_while_stopped_okay",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Stopped",
},
want: mustIP("1.2.3.4"),
},
{
name: "ip_not_found",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
},
wantErr: `no node found in netmap with IP 1.2.3.4`,
},
{
name: "ip_not_exit",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
},
},
},
wantErr: `node 1.2.3.4 is not advertising an exit node`,
},
{
name: "ip",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
ExitNodeOption: true,
},
},
},
want: mustIP("1.2.3.4"),
},
{
name: "no_match",
arg: "unknown",
st: &ipnstate.Status{MagicDNSSuffix: ".foo"},
wantErr: `invalid value "unknown" for --exit-node; must be IP or unique node name`,
},
{
name: "name",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},
},
want: mustIP("1.0.0.2"),
},
{
name: "name_not_exit",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
},
},
},
wantErr: `node "skippy" is not advertising an exit node`,
},
{
name: "ambiguous",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
key.NewNode().Public(): {
DNSName: "SKIPPY.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},
},
wantErr: `ambiguous exit node name "skippy"`,
},
{in: []string{"something"}, want: []string{"something"}},
{in: []string{}, want: []string{}},
{in: []string{"--authkey=0"}, want: []string{"--auth-key=0"}},
{in: []string{"a", "--authkey=1", "b"}, want: []string{"a", "--auth-key=1", "b"}},
{in: []string{"a", "--auth-key=2", "b"}, want: []string{"a", "--auth-key=2", "b"}},
{in: []string{"a", "-authkey=3", "b"}, want: []string{"a", "--auth-key=3", "b"}},
{in: []string{"a", "-auth-key=4", "b"}, want: []string{"a", "-auth-key=4", "b"}},
{in: []string{"a", "--authkey", "5", "b"}, want: []string{"a", "--auth-key", "5", "b"}},
{in: []string{"a", "-authkey", "6", "b"}, want: []string{"a", "--auth-key", "6", "b"}},
{in: []string{"a", "authkey", "7", "b"}, want: []string{"a", "authkey", "7", "b"}},
{in: []string{"--authkeyexpiry", "8"}, want: []string{"--authkeyexpiry", "8"}},
{in: []string{"--auth-key-expiry", "9"}, want: []string{"--auth-key-expiry", "9"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := exitNodeIPOfArg(tt.arg, tt.st)
if err != nil {
if err.Error() == tt.wantErr {
return
}
if tt.wantErr == "" {
t.Fatal(err)
}
t.Fatalf("error = %#q; want %#q", err, tt.wantErr)
}
if tt.wantErr != "" {
t.Fatalf("got %v; want error %#q", got, tt.wantErr)
}
if got != tt.want {
t.Fatalf("got %v; want %v", got, tt.want)
}
})
got := CleanUpArgs(tt.in)
c.Assert(got, qt.DeepEquals, tt.want)
}
}

View File

@@ -77,7 +77,7 @@ func runConfigureHost(ctx context.Context, args []string) error {
}
return err
}
if out, err := exec.Command("/bin/setcap", "cap_net_admin+eip", daemonBin).CombinedOutput(); err != nil {
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")

View File

@@ -22,6 +22,7 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/client/tailscale"
"tailscale.com/hostinfo"
"tailscale.com/ipn"
"tailscale.com/paths"
"tailscale.com/safesocket"
@@ -65,6 +66,11 @@ var debugCmd = &ffcli.Command{
Exec: runEnv,
ShortHelp: "print cmd/tailscale environment",
},
{
Name: "hostinfo",
Exec: runHostinfo,
ShortHelp: "print hostinfo",
},
{
Name: "local-creds",
Exec: runLocalCreds,
@@ -186,7 +192,7 @@ func runDebug(ctx context.Context, args []string) error {
// to subcommands.
return nil
}
return errors.New("see 'tailscale debug --help")
return errors.New("see 'tailscale debug --help'")
}
func runLocalCreds(ctx context.Context, args []string) error {
@@ -270,6 +276,13 @@ func runEnv(ctx context.Context, args []string) error {
return nil
}
func runHostinfo(ctx context.Context, args []string) error {
hi := hostinfo.New()
j, _ := json.MarshalIndent(hi, "", " ")
os.Stdout.Write(j)
return nil
}
func runDaemonGoroutines(ctx context.Context, args []string) error {
goroutines, err := tailscale.Goroutines(ctx)
if err != nil {

View File

@@ -15,6 +15,7 @@ import (
"mime"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"time"
@@ -286,22 +287,116 @@ func runCpTargets(ctx context.Context, args []string) error {
return nil
}
// onConflict is a flag.Value for the --conflict flag's three string options.
type onConflict string
const (
skipOnExist onConflict = "skip"
overwriteExisting onConflict = "overwrite" // Overwrite any existing file at the target location
createNumberedFiles onConflict = "rename" // Create an alternately named file in the style of Chrome Downloads
)
func (v *onConflict) String() string { return string(*v) }
func (v *onConflict) Set(s string) error {
if s == "" {
*v = skipOnExist
return nil
}
*v = onConflict(strings.ToLower(s))
if *v != skipOnExist && *v != overwriteExisting && *v != createNumberedFiles {
return fmt.Errorf("%q is not one of (skip|overwrite|rename)", s)
}
return nil
}
var fileGetCmd = &ffcli.Command{
Name: "get",
ShortUsage: "file get [--wait] [--verbose] <target-directory>",
ShortUsage: "file get [--wait] [--verbose] [--conflict=(skip|overwrite|rename)] <target-directory>",
ShortHelp: "Move files out of the Tailscale file inbox",
Exec: runFileGet,
FlagSet: (func() *flag.FlagSet {
fs := newFlagSet("get")
fs.BoolVar(&getArgs.wait, "wait", false, "wait for a file to arrive if inbox is empty")
fs.BoolVar(&getArgs.verbose, "verbose", false, "verbose output")
fs.Var(&getArgs.conflict, "conflict", `behavior when a conflicting (same-named) file already exists in the target directory.
skip: skip conflicting files: leave them in the taildrop inbox and print an error. get any non-conflicting files
overwrite: overwrite existing file
rename: write to a new number-suffixed filename`)
return fs
})(),
}
var getArgs struct {
wait bool
verbose bool
var getArgs = struct {
wait bool
verbose bool
conflict onConflict
}{conflict: skipOnExist}
func numberedFileName(dir, name string, i int) string {
ext := path.Ext(name)
return filepath.Join(dir, fmt.Sprintf("%s (%d)%s",
strings.TrimSuffix(name, ext),
i, ext))
}
func openFileOrSubstitute(dir, base string, action onConflict) (*os.File, error) {
targetFile := filepath.Join(dir, base)
f, err := os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)
if err == nil {
return f, nil
}
// Something went wrong trying to open targetFile as a new file for writing.
switch action {
default:
// This should not happen.
return nil, fmt.Errorf("file issue. how to resolve this conflict? no one knows.")
case skipOnExist:
if _, statErr := os.Stat(targetFile); statErr == nil {
// we can stat a file at that path: so it already exists.
return nil, fmt.Errorf("refusing to overwrite file: %w", err)
}
return nil, fmt.Errorf("failed to write; %w", err)
case overwriteExisting:
// remove the target file and create it anew so we don't fall for an
// attacker who symlinks a known target name to a file he wants changed.
if err = os.Remove(targetFile); err != nil {
return nil, fmt.Errorf("unable to remove target file: %w", err)
}
if f, err = os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
return nil, fmt.Errorf("unable to overwrite: %w", err)
}
return f, nil
case createNumberedFiles:
// It's possible the target directory or filesystem isn't writable by us,
// not just that the target file(s) already exists. For now, give up after
// a limited number of attempts. In future, maybe distinguish this case
// and follow in the style of https://tinyurl.com/chromium100
maxAttempts := 100
for i := 1; i < maxAttempts; i++ {
if f, err = os.OpenFile(numberedFileName(dir, base, i), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err == nil {
return f, nil
}
}
return nil, fmt.Errorf("unable to find a name for writing %v, final attempt: %w", targetFile, err)
}
}
func receiveFile(ctx context.Context, wf apitype.WaitingFile, dir string) (targetFile string, size int64, err error) {
rc, size, err := tailscale.GetWaitingFile(ctx, wf.Name)
if err != nil {
return "", 0, fmt.Errorf("opening inbox file %q: %w", wf.Name, err)
}
f, err := openFileOrSubstitute(dir, wf.Name, getArgs.conflict)
if err != nil {
return "", 0, err
}
_, err = io.Copy(f, rc)
rc.Close()
if err != nil {
return "", 0, fmt.Errorf("failed to write %v: %v", f.Name(), err)
}
return f.Name(), size, f.Close()
}
func runFileGet(ctx context.Context, args []string) error {
@@ -330,47 +425,40 @@ func runFileGet(ctx context.Context, args []string) error {
break
}
if getArgs.verbose {
log.Printf("waiting for file...")
printf("waiting for file...")
}
if err := waitForFile(ctx); err != nil {
return err
}
}
var errs []error
deleted := 0
for _, wf := range wfs {
rc, size, err := tailscale.GetWaitingFile(ctx, wf.Name)
writtenFile, size, err := receiveFile(ctx, wf, dir)
if err != nil {
return fmt.Errorf("opening inbox file %q: %v", wf.Name, err)
}
targetFile := filepath.Join(dir, wf.Name)
of, err := os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
if _, err := os.Stat(targetFile); err == nil {
return fmt.Errorf("refusing to overwrite %v", targetFile)
}
return err
}
_, err = io.Copy(of, rc)
rc.Close()
if err != nil {
return fmt.Errorf("failed to write %v: %v", targetFile, err)
}
if err := of.Close(); err != nil {
return err
errs = append(errs, err)
continue
}
if getArgs.verbose {
log.Printf("wrote %v (%d bytes)", wf.Name, size)
printf("wrote %v as %v (%d bytes)\n", wf.Name, writtenFile, size)
}
if err := tailscale.DeleteWaitingFile(ctx, wf.Name); err != nil {
return fmt.Errorf("deleting %q from inbox: %v", wf.Name, err)
if err = tailscale.DeleteWaitingFile(ctx, wf.Name); err != nil {
errs = append(errs, fmt.Errorf("deleting %q from inbox: %v", wf.Name, err))
continue
}
deleted++
}
if getArgs.verbose {
log.Printf("moved %d files", deleted)
printf("moved %d/%d files\n", deleted, len(wfs))
}
return nil
if len(errs) == 0 {
return nil
}
for _, err := range errs[:len(errs)-1] {
outln(err)
}
return errs[len(errs)-1]
}
func wipeInbox(ctx context.Context) error {

View File

@@ -136,7 +136,7 @@ func runStatus(ctx context.Context, args []string) error {
}
var buf bytes.Buffer
f := func(format string, a ...interface{}) { fmt.Fprintf(&buf, format, a...) }
f := func(format string, a ...any) { fmt.Fprintf(&buf, format, a...) }
printPS := func(ps *ipnstate.PeerStatus) {
f("%-15s %-20s %-12s %-7s ",
firstIPString(ps.TailscaleIPs),

View File

@@ -31,7 +31,6 @@ import (
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/preftype"
"tailscale.com/util/dnsname"
"tailscale.com/version"
"tailscale.com/version/distro"
)
@@ -103,7 +102,7 @@ func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
upf.BoolVar(&upArgs.runSSH, "ssh", false, "run an SSH server, permitting access per tailnet admin's declared policy")
}
upf.StringVar(&upArgs.advertiseTags, "advertise-tags", "", "comma-separated ACL tags to request; each must start with \"tag:\" (e.g. \"tag:eng,tag:montreal,tag:ssh\")")
upf.StringVar(&upArgs.authKeyOrFile, "authkey", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`)
upf.StringVar(&upArgs.authKeyOrFile, "auth-key", "", `node authorization key; if it begins with "file:", then it's a path to a file containing the authkey`)
upf.StringVar(&upArgs.hostname, "hostname", "", "hostname to use instead of the one provided by the OS")
upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. \"10.0.0.0/8,192.168.0.0/24\") or empty string to not advertise routes")
upf.BoolVar(&upArgs.advertiseDefaultRoute, "advertise-exit-node", false, "offer to be an exit node for internet traffic for the tailnet")
@@ -193,7 +192,7 @@ type upOutputJSON struct {
Error string `json:",omitempty"` // description of an error
}
func warnf(format string, args ...interface{}) {
func warnf(format string, args ...any) {
printf("Warning: "+format+"\n", args...)
}
@@ -245,65 +244,6 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
return routes, nil
}
// peerWithTailscaleIP returns the peer in st with the provided
// Tailscale IP.
func peerWithTailscaleIP(st *ipnstate.Status, ip netaddr.IP) (ps *ipnstate.PeerStatus, ok bool) {
for _, ps := range st.Peer {
for _, ip2 := range ps.TailscaleIPs {
if ip == ip2 {
return ps, true
}
}
}
return nil, false
}
// exitNodeIPOfArg maps from a user-provided CLI flag value to an IP
// address they want to use as an exit node.
func exitNodeIPOfArg(arg string, st *ipnstate.Status) (ip netaddr.IP, err error) {
if arg == "" {
return ip, errors.New("invalid use of exitNodeIPOfArg with empty string")
}
ip, err = netaddr.ParseIP(arg)
if err == nil {
// If we're online already and have a netmap, double check that the IP
// address specified is valid.
if st.BackendState == "Running" {
ps, ok := peerWithTailscaleIP(st, ip)
if !ok {
return ip, fmt.Errorf("no node found in netmap with IP %v", ip)
}
if !ps.ExitNodeOption {
return ip, fmt.Errorf("node %v is not advertising an exit node", ip)
}
}
return ip, err
}
match := 0
for _, ps := range st.Peer {
baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix)
if !strings.EqualFold(arg, baseName) {
continue
}
match++
if len(ps.TailscaleIPs) == 0 {
return ip, fmt.Errorf("node %q has no Tailscale IP?", arg)
}
if !ps.ExitNodeOption {
return ip, fmt.Errorf("node %q is not advertising an exit node", arg)
}
ip = ps.TailscaleIPs[0]
}
switch match {
case 0:
return ip, fmt.Errorf("invalid value %q for --exit-node; must be IP or unique node name", arg)
case 1:
return ip, nil
default:
return ip, fmt.Errorf("ambiguous exit node name %q", arg)
}
}
// prefsFromUpArgs returns the ipn.Prefs for the provided args.
//
// Note that the parameters upArgs and warnf are named intentionally
@@ -316,25 +256,10 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
return nil, err
}
var exitNodeIP netaddr.IP
if upArgs.exitNodeIP != "" {
var err error
exitNodeIP, err = exitNodeIPOfArg(upArgs.exitNodeIP, st)
if err != nil {
return nil, err
}
} else if upArgs.exitNodeAllowLANAccess {
if upArgs.exitNodeIP == "" && upArgs.exitNodeAllowLANAccess {
return nil, fmt.Errorf("--exit-node-allow-lan-access can only be used with --exit-node")
}
if upArgs.exitNodeIP != "" {
for _, ip := range st.TailscaleIPs {
if exitNodeIP == ip {
return nil, fmt.Errorf("cannot use %s as the exit node as it is a local IP address to this machine, did you mean --advertise-exit-node?", upArgs.exitNodeIP)
}
}
}
var tags []string
if upArgs.advertiseTags != "" {
tags = strings.Split(upArgs.advertiseTags, ",")
@@ -354,7 +279,17 @@ func prefsFromUpArgs(upArgs upArgsT, warnf logger.Logf, st *ipnstate.Status, goo
prefs.ControlURL = upArgs.server
prefs.WantRunning = true
prefs.RouteAll = upArgs.acceptRoutes
prefs.ExitNodeIP = exitNodeIP
if upArgs.exitNodeIP != "" {
if err := prefs.SetExitNodeIP(upArgs.exitNodeIP, st); err != nil {
var e ipn.ExitNodeLocalIPError
if errors.As(err, &e) {
return nil, fmt.Errorf("%w; did you mean --advertise-exit-node?", err)
}
return nil, err
}
}
prefs.ExitNodeAllowLANAccess = upArgs.exitNodeAllowLANAccess
prefs.CorpDNS = upArgs.acceptDNS
prefs.AllowSingleHosts = upArgs.singleRoutes
@@ -743,7 +678,7 @@ func addPrefFlagMapping(flagName string, prefNames ...string) {
// correspond to an ipn.Pref.
func preflessFlag(flagName string) bool {
switch flagName {
case "authkey", "force-reauth", "reset", "qr", "json":
case "auth-key", "force-reauth", "reset", "qr", "json":
return true
}
return false
@@ -888,8 +823,8 @@ func flagAppliesToOS(flag, goos string) bool {
return true
}
func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]interface{}) {
ret := make(map[string]interface{})
func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]any) {
ret := make(map[string]any)
exitNodeIPStr := func() string {
if !prefs.ExitNodeIP.IsZero() {
@@ -906,7 +841,7 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]interfac
if preflessFlag(f.Name) {
return
}
set := func(v interface{}) {
set := func(v any) {
if flagAppliesToOS(f.Name, env.goos) {
ret[f.Name] = v
} else {
@@ -960,7 +895,7 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]interfac
return ret
}
func fmtFlagValueArg(flagName string, val interface{}) string {
func fmtFlagValueArg(flagName string, val any) string {
if val == true {
return "--" + flagName
}

View File

@@ -313,7 +313,7 @@ func webHandler(w http.ResponseWriter, r *http.Request) {
AdvertiseExitNode bool
Reauthenticate bool
}
type mi map[string]interface{}
type mi map[string]any
if err := json.NewDecoder(r.Body).Decode(&postData); err != nil {
w.WriteHeader(400)
json.NewEncoder(w).Encode(mi{"error": err.Error()})

View File

@@ -4,8 +4,14 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
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/golang/groupcache/lru from tailscale.com/net/dnscache
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/kballard/go-shellquote from tailscale.com/cmd/tailscale/cli
L 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/cmd/tailscale/cli+
github.com/peterbourgon/ff/v3 from github.com/peterbourgon/ff/v3/ffcli
github.com/peterbourgon/ff/v3/ffcli from tailscale.com/cmd/tailscale/cli
@@ -31,7 +37,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com from tailscale.com/version
tailscale.com/atomicfile from tailscale.com/ipn+
tailscale.com/client/tailscale from tailscale.com/cmd/tailscale/cli+
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
tailscale.com/client/tailscale/apitype from tailscale.com/cmd/tailscale/cli+
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
tailscale.com/control/controlknobs from tailscale.com/net/portmapper
tailscale.com/derp from tailscale.com/derp/derphttp
@@ -42,7 +48,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/hostinfo from tailscale.com/net/interfaces+
tailscale.com/ipn from tailscale.com/cmd/tailscale/cli+
tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+
tailscale.com/kube from tailscale.com/ipn
💣 tailscale.com/metrics from tailscale.com/derp
tailscale.com/net/dnscache from tailscale.com/derp/derphttp
tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+
@@ -59,7 +64,7 @@ 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/interfaces+
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
W tailscale.com/tsconst from tailscale.com/net/interfaces
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
@@ -75,11 +80,15 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
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/views from tailscale.com/tailcfg+
tailscale.com/util/clientmetric from tailscale.com/net/netcheck+
W tailscale.com/util/cmpver from tailscale.com/net/tshttpproxy
tailscale.com/util/dnsname from tailscale.com/cmd/tailscale/cli+
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+
W tailscale.com/util/winutil from tailscale.com/hostinfo
W 💣 tailscale.com/util/winutil/vss from tailscale.com/util/winutil
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
@@ -92,8 +101,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
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/poly1305 from golang.org/x/crypto/chacha20poly1305
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
@@ -177,6 +186,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/textproto from golang.org/x/net/http/httpguts+
net/url from crypto/x509+
os from crypto/rand+
@@ -188,7 +198,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
reflect from crypto/x509+
regexp from github.com/tailscale/goupnp/httpu+
regexp/syntax from regexp
runtime/debug from golang.org/x/sync/singleflight
runtime/debug from golang.org/x/sync/singleflight+
sort from compress/flate+
strconv from compress/flate+
strings from bufio+

View File

@@ -17,6 +17,7 @@ import (
func main() {
args := os.Args[1:]
args = cli.CleanUpArgs(args)
if name, _ := os.Executable(); strings.HasSuffix(filepath.Base(name), ".cgi") {
args = []string{"web", "-cgi"}
}

View File

@@ -0,0 +1,20 @@
// 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 childproc allows other packages to register "tailscaled be-child"
// child process hook code. This avoids duplicating build tags in the
// tailscaled package. Instead, the code that needs to fork/exec the self
// executable (when it's tailscaled) can instead register the code
// they want to run.
package childproc
var Code = map[string]func([]string) error{}
// Add registers code f to run as 'tailscaled be-child <typ> [args]'.
func Add(typ string, f func(args []string) error) {
if _, dup := Code[typ]; dup {
panic("dup hook " + typ)
}
Code[typ] = f
}

View File

@@ -2,6 +2,9 @@
// 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
package main
import (

View File

@@ -3,10 +3,10 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
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
L github.com/anmitsu/go-shlex from github.com/gliderlabs/ssh
LD github.com/anmitsu/go-shlex from github.com/tailscale/ssh
L github.com/aws/aws-sdk-go-v2 from github.com/aws/aws-sdk-go-v2/internal/ini
L github.com/aws/aws-sdk-go-v2/aws from github.com/aws/aws-sdk-go-v2/aws/middleware+
L github.com/aws/aws-sdk-go-v2/aws/arn from tailscale.com/ipn/store/aws
L github.com/aws/aws-sdk-go-v2/aws/arn from tailscale.com/ipn/store/awsstore
L github.com/aws/aws-sdk-go-v2/aws/middleware from github.com/aws/aws-sdk-go-v2/aws/retry+
L github.com/aws/aws-sdk-go-v2/aws/protocol/query from github.com/aws/aws-sdk-go-v2/service/sts
L github.com/aws/aws-sdk-go-v2/aws/protocol/restjson from github.com/aws/aws-sdk-go-v2/service/ssm+
@@ -16,7 +16,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4 from github.com/aws/aws-sdk-go-v2/aws/signer/v4
L github.com/aws/aws-sdk-go-v2/aws/signer/v4 from github.com/aws/aws-sdk-go-v2/service/internal/presigned-url+
L github.com/aws/aws-sdk-go-v2/aws/transport/http from github.com/aws/aws-sdk-go-v2/config+
L github.com/aws/aws-sdk-go-v2/config from tailscale.com/ipn/store/aws
L github.com/aws/aws-sdk-go-v2/config from tailscale.com/ipn/store/awsstore
L github.com/aws/aws-sdk-go-v2/credentials from github.com/aws/aws-sdk-go-v2/config
L github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds from github.com/aws/aws-sdk-go-v2/config
L github.com/aws/aws-sdk-go-v2/credentials/endpointcreds from github.com/aws/aws-sdk-go-v2/config
@@ -36,7 +36,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/aws-sdk-go-v2/internal/sync/singleflight from github.com/aws/aws-sdk-go-v2/aws
L github.com/aws/aws-sdk-go-v2/internal/timeconv from github.com/aws/aws-sdk-go-v2/aws/retry
L github.com/aws/aws-sdk-go-v2/service/internal/presigned-url from github.com/aws/aws-sdk-go-v2/service/sts
L github.com/aws/aws-sdk-go-v2/service/ssm from tailscale.com/ipn/store/aws
L github.com/aws/aws-sdk-go-v2/service/ssm from tailscale.com/ipn/store/awsstore
L github.com/aws/aws-sdk-go-v2/service/ssm/internal/endpoints from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/aws/aws-sdk-go-v2/service/ssm/types from github.com/aws/aws-sdk-go-v2/service/ssm+
L github.com/aws/aws-sdk-go-v2/service/sso from github.com/aws/aws-sdk-go-v2/config+
@@ -61,11 +61,10 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/smithy-go/transport/http/internal/io from github.com/aws/smithy-go/transport/http
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
L 💣 github.com/creack/pty from tailscale.com/wgengine/netstack
L github.com/gliderlabs/ssh from tailscale.com/wgengine/netstack
LD 💣 github.com/creack/pty from tailscale.com/ssh/tailssh
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
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+
L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/net/tstun
@@ -74,7 +73,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/insomniacslk/dhcp/rfc1035label from github.com/insomniacslk/dhcp/dhcpv4
L github.com/jmespath/go-jmespath from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/wgengine/monitor
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 from github.com/klauspost/compress/zstd
L github.com/klauspost/compress/flate from nhooyr.io/websocket
@@ -83,6 +82,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/klauspost/compress/internal/snapref from github.com/klauspost/compress/zstd
github.com/klauspost/compress/zstd from tailscale.com/smallzstd
github.com/klauspost/compress/zstd/internal/xxhash from github.com/klauspost/compress/zstd
L github.com/mdlayher/genetlink from tailscale.com/net/tstun
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/sdnotify from tailscale.com/util/systemd
@@ -97,7 +97,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/tailscale/goupnp/soap from github.com/tailscale/goupnp+
github.com/tailscale/goupnp/ssdp from github.com/tailscale/goupnp
L 💣 github.com/tailscale/netlink from tailscale.com/wgengine/router
LD github.com/tailscale/ssh from tailscale.com/ssh/tailssh
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
LD github.com/u-root/u-root/pkg/termios from tailscale.com/ssh/tailssh
L github.com/u-root/uio/rand from github.com/insomniacslk/dhcp/dhcpv4
L github.com/u-root/uio/ubinary from github.com/u-root/uio/uio
L github.com/u-root/uio/uio from github.com/insomniacslk/dhcp/dhcpv4+
@@ -168,7 +170,10 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
LD tailscale.com/chirp from tailscale.com/cmd/tailscaled
tailscale.com/client/tailscale from tailscale.com/derp
tailscale.com/client/tailscale/apitype from tailscale.com/client/tailscale+
tailscale.com/control/controlclient from tailscale.com/ipn/ipnlocal+
tailscale.com/cmd/tailscaled/childproc from tailscale.com/cmd/tailscaled+
tailscale.com/control/controlbase from tailscale.com/control/controlclient+
tailscale.com/control/controlclient from tailscale.com/cmd/tailscaled+
tailscale.com/control/controlhttp from tailscale.com/control/controlclient
tailscale.com/control/controlknobs from tailscale.com/control/controlclient+
tailscale.com/derp from tailscale.com/derp/derphttp+
tailscale.com/derp/derphttp from tailscale.com/cmd/tailscaled+
@@ -183,23 +188,27 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/ipn/ipnstate from tailscale.com/client/tailscale+
tailscale.com/ipn/localapi from tailscale.com/ipn/ipnserver
tailscale.com/ipn/policy from tailscale.com/ipn/ipnlocal
tailscale.com/ipn/store/aws from tailscale.com/ipn/ipnserver
tailscale.com/kube from tailscale.com/ipn
tailscale.com/ipn/store from tailscale.com/cmd/tailscaled
L tailscale.com/ipn/store/awsstore from tailscale.com/ipn/store
L tailscale.com/ipn/store/kubestore from tailscale.com/ipn/store
tailscale.com/ipn/store/mem from tailscale.com/ipn/store+
L tailscale.com/kube from tailscale.com/ipn/store/kubestore
tailscale.com/log/filelogger from tailscale.com/logpolicy
tailscale.com/log/logheap from tailscale.com/control/controlclient
tailscale.com/logpolicy from tailscale.com/cmd/tailscaled+
tailscale.com/logtail from tailscale.com/logpolicy+
tailscale.com/logtail from tailscale.com/cmd/tailscaled+
tailscale.com/logtail/backoff from tailscale.com/cmd/tailscaled+
tailscale.com/logtail/filch from tailscale.com/logpolicy
💣 tailscale.com/metrics from tailscale.com/derp+
tailscale.com/net/dns from tailscale.com/cmd/tailscaled+
tailscale.com/net/dns/resolver from tailscale.com/net/dns+
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/cmd/tailscaled+
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock
tailscale.com/net/neterror from tailscale.com/net/netcheck+
tailscale.com/net/neterror from tailscale.com/net/dns/resolver+
tailscale.com/net/netknob from tailscale.com/logpolicy+
tailscale.com/net/netns from tailscale.com/cmd/tailscaled+
💣 tailscale.com/net/netstat from tailscale.com/ipn/ipnserver
@@ -210,7 +219,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/socks5 from tailscale.com/cmd/tailscaled
tailscale.com/net/stun from tailscale.com/net/netcheck+
tailscale.com/net/tlsdial from tailscale.com/control/controlclient+
tailscale.com/net/tsaddr from tailscale.com/ipn/ipnlocal+
tailscale.com/net/tsaddr from tailscale.com/ipn+
tailscale.com/net/tsdial from tailscale.com/cmd/tailscaled+
💣 tailscale.com/net/tshttpproxy from tailscale.com/cmd/tailscaled+
tailscale.com/net/tstun from tailscale.com/cmd/tailscaled+
@@ -218,7 +227,8 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/portlist from tailscale.com/ipn/ipnlocal
tailscale.com/safesocket from tailscale.com/client/tailscale+
tailscale.com/smallzstd from tailscale.com/ipn/ipnserver+
💣 tailscale.com/syncs from tailscale.com/control/controlknobs+
LD 💣 tailscale.com/ssh/tailssh from tailscale.com/wgengine/netstack
tailscale.com/syncs from tailscale.com/control/controlknobs+
tailscale.com/tailcfg from tailscale.com/client/tailscale+
W tailscale.com/tsconst from tailscale.com/net/interfaces
tailscale.com/tstime from tailscale.com/wgengine/magicsock
@@ -238,14 +248,16 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
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/util/clientmetric from tailscale.com/ipn/localapi+
L tailscale.com/util/cmpver from tailscale.com/net/dns
tailscale.com/types/views from tailscale.com/ipn/ipnlocal+
tailscale.com/util/clientmetric from tailscale.com/cmd/tailscaled+
LW tailscale.com/util/cmpver from tailscale.com/net/dns+
💣 tailscale.com/util/deephash from tailscale.com/ipn/ipnlocal+
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/lineread from tailscale.com/hostinfo+
tailscale.com/util/multierr from tailscale.com/cmd/tailscaled+
tailscale.com/util/netconv from tailscale.com/wgengine/magicsock
tailscale.com/util/osshare from tailscale.com/cmd/tailscaled+
tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver
tailscale.com/util/racebuild from tailscale.com/logpolicy
@@ -258,9 +270,9 @@ 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/cmd/tailscaled+
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
tailscale.com/wgengine/magicsock from tailscale.com/wgengine+
tailscale.com/wgengine/magicsock from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/monitor from tailscale.com/cmd/tailscaled+
💣 tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
tailscale.com/wgengine/netstack from tailscale.com/cmd/tailscaled
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
tailscale.com/wgengine/wgcfg from tailscale.com/ipn/ipnlocal+
tailscale.com/wgengine/wgcfg/nmcfg from tailscale.com/ipn/ipnlocal
@@ -268,34 +280,34 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
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/blake2s from golang.zx2c4.com/wireguard/device
L golang.org/x/crypto/blowfish from golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
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+
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+
L golang.org/x/crypto/ed25519 from golang.org/x/crypto/ssh
golang.org/x/crypto/hkdf from crypto/tls
LD golang.org/x/crypto/ed25519 from golang.org/x/crypto/ssh
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/poly1305 from golang.org/x/crypto/chacha20poly1305+
golang.org/x/crypto/poly1305 from golang.zx2c4.com/wireguard/device
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
L golang.org/x/crypto/ssh from github.com/gliderlabs/ssh+
golang.org/x/net/bpf from github.com/mdlayher/netlink+
LD golang.org/x/crypto/ssh from github.com/tailscale/ssh+
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 net/http+
golang.org/x/net/http/httpguts from golang.org/x/net/http2+
golang.org/x/net/http/httpproxy from net/http
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 net/http+
golang.org/x/net/http2/hpack from golang.org/x/net/http2+
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/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+
golang.org/x/sync/errgroup from github.com/tailscale/goupnp/httpu+
golang.org/x/sync/singleflight from tailscale.com/net/dnscache
golang.org/x/sync/errgroup from github.com/mdlayher/socket+
golang.org/x/sync/singleflight from tailscale.com/control/controlclient+
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
LD golang.org/x/sys/unix from github.com/insomniacslk/dhcp/interfaces+
W golang.org/x/sys/windows from github.com/go-ole/go-ole+
@@ -311,7 +323,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
bufio from compress/flate+
bytes from bufio+
compress/flate from compress/gzip+
compress/gzip from internal/profile+
compress/gzip from golang.org/x/net/http2+
container/heap from gvisor.dev/gvisor/pkg/tcpip/transport/tcp
container/list from crypto/tls+
context from crypto/tls+
@@ -335,7 +347,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
crypto/tls from github.com/aws/aws-sdk-go-v2/aws/transport/http+
crypto/x509 from crypto/tls+
crypto/x509/pkix from crypto/x509+
embed from tailscale.com/net/dns+
embed from crypto/elliptic+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base64 from encoding/json+
@@ -357,6 +369,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
io/fs from crypto/rand+
io/ioutil from github.com/aws/aws-sdk-go-v2/aws/protocol/query+
log from expvar+
LD log/syslog from tailscale.com/ssh/tailssh
math from compress/flate+
math/big from crypto/dsa+
math/bits from compress/flate+
@@ -370,6 +383,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
net/http/httputil from github.com/aws/smithy-go/transport/http+
net/http/internal from net/http+
net/http/pprof from tailscale.com/cmd/tailscaled+
net/netip from golang.zx2c4.com/wireguard/conn+
net/textproto from github.com/aws/aws-sdk-go-v2/aws/signer/v4+
net/url from crypto/x509+
os from crypto/rand+

View File

@@ -2,6 +2,9 @@
// 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
package main
import (

View File

@@ -2,6 +2,9 @@
// 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
package main
import (

View File

@@ -2,6 +2,9 @@
// 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
// HTTP proxy code
package main

View File

@@ -0,0 +1,12 @@
// 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 !go1.18
// +build !go1.18
package main
func init() {
you_need_Go_1_18_to_compile_Tailscale()
}

View File

@@ -2,6 +2,9 @@
// 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
// The tailscaled program is the Tailscale client daemon. It's configured
// and controlled via the tailscale CLI program.
//
@@ -28,9 +31,12 @@ import (
"time"
"inet.af/netaddr"
"tailscale.com/cmd/tailscaled/childproc"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnserver"
"tailscale.com/ipn/store"
"tailscale.com/logpolicy"
"tailscale.com/logtail"
"tailscale.com/net/dns"
@@ -67,11 +73,27 @@ func defaultTunName() string {
// as a magic value that uses/creates any free number.
return "utun"
case "linux":
if distro.Get() == distro.Synology {
switch distro.Get() {
case distro.Synology:
// Try TUN, but fall back to userspace networking if needed.
// See https://github.com/tailscale/tailscale-synology/issues/35
return "tailscale0,userspace-networking"
case distro.Gokrazy:
// Gokrazy doesn't yet work in tun mode because the whole
// Gokrazy thing is no C code, and Tailscale currently
// depends on the iptables binary for Linux's
// wgengine/router.
// But on Gokrazy there's no legacy iptables, so we could use netlink
// to program nft-iptables directly. It just isn't done yet;
// see https://github.com/tailscale/tailscale/issues/391
//
// But Gokrazy does have the tun module built-in, so users
// can stil run --tun=tailscale0 if they wish, if they
// arrange for iptables to be present or run in "tailscale
// up --netfilter-mode=off" mode, perhaps. Untested.
return "userspace-networking"
}
}
return "tailscale0"
}
@@ -104,6 +126,7 @@ var subCommands = map[string]*func([]string) error{
"install-system-daemon": &installSystemDaemon,
"uninstall-system-daemon": &uninstallSystemDaemon,
"debug": &debugModeFunc,
"be-child": &beChildFunc,
}
func main() {
@@ -123,7 +146,7 @@ func main() {
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.StringVar(&args.statepath, "state", paths.DefaultTailscaledStateFile(), "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM. If empty and --statedir is provided, the default is <statedir>/tailscaled.state")
flag.StringVar(&args.statepath, "state", paths.DefaultTailscaledStateFile(), "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")
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")
@@ -158,6 +181,9 @@ func main() {
os.Exit(0)
}
if runtime.GOOS == "darwin" && runtime.Version() == "go1.18" {
log.Fatalf("tailscaled is broken on macOS with go1.18 due to upstream bug https://github.com/golang/go/issues/51759; use 1.18.1+ or Tailscale's Go fork")
}
if runtime.GOOS == "darwin" && os.Getuid() != 0 && !strings.Contains(args.tunname, "userspace-networking") && !args.cleanup {
log.SetFlags(0)
log.Fatalf("tailscaled requires root; use sudo tailscaled (or use --tun=userspace-networking)")
@@ -238,8 +264,19 @@ func ipnServerOpts() (o ipnserver.Options) {
o.VarRoot = dir
}
}
if strings.HasPrefix(statePathOrDefault(), "mem:") {
// Register as an ephemeral node.
o.LoginFlags = controlclient.LoginEphemeral
}
switch goos {
case "js":
// The js/wasm client has no state storage so for now
// treat all interactive logins as ephemeral.
// TODO(bradfitz): if we start using browser LocalStorage
// or something, then rethink this.
o.LoginFlags = controlclient.LoginEphemeral
fallthrough
default:
o.SurviveDisconnects = true
o.AutostartStateKey = ipn.GlobalDaemonStateKey
@@ -380,9 +417,9 @@ func run() error {
opts := ipnServerOpts()
store, err := ipnserver.StateStore(statePathOrDefault(), logf)
store, err := store.New(logf, statePathOrDefault())
if err != nil {
return fmt.Errorf("ipnserver.StateStore: %w", err)
return fmt.Errorf("store.New: %w", err)
}
srv, err := ipnserver.New(logf, pol.PublicID.String(), store, e, dialer, nil, opts)
if err != nil {
@@ -463,7 +500,20 @@ func tryEngine(logf logger.Logf, linkMon *monitor.Mon, dialer *tsdial.Dialer, na
return nil, false, fmt.Errorf("createBIRDClient: %w", err)
}
}
if !useNetstack {
if useNetstack {
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
// On Synology in netstack mode, still init a DNS
// manager (directManager) to avoid the health check
// warnings in 'tailscale status' about DNS base
// configuration being unavailable (from the noop
// manager). More in Issue 4017.
// TODO(bradfitz): add a Synology-specific DNS manager.
conf.DNS, err = dns.NewOSConfigurator(logf, "") // empty interface name
if err != nil {
return nil, false, fmt.Errorf("dns.NewOSConfigurator: %w", err)
}
}
} else {
dev, devName, err := tstun.New(logf, name)
if err != nil {
tstun.Diagnose(logf, name)
@@ -576,3 +626,17 @@ func mustStartProxyListeners(socksAddr, httpAddr string) (socksListener, httpLis
return socksListener, httpListener
}
var beChildFunc = beChild
func beChild(args []string) error {
if len(args) == 0 {
return errors.New("missing mode argument")
}
typ := args[0]
f, ok := childproc.Code[typ]
if !ok {
return fmt.Errorf("unknown be-child mode %q", typ)
}
return f(args[1:])
}

View File

@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux || darwin || freebsd || openbsd
//go:build go1.18 && (linux || darwin || freebsd || openbsd)
// +build go1.18
// +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
// +build !windows
//go:build !windows && go1.18
// +build !windows,go1.18
package main // import "tailscale.com/cmd/tailscaled"

View File

@@ -0,0 +1,13 @@
// 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 "tailscale.com/cmd/tailscaled"
import "testing"
func TestNothing(t *testing.T) {
// This test does nothing on purpose, so we can run
// GODEBUG=memprofilerate=1 go test -v -run=Nothing -memprofile=prof.mem
// without any errors about no matching tests.
}

View File

@@ -2,6 +2,9 @@
// 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
package main // import "tailscale.com/cmd/tailscaled"
// TODO: check if administrator, like tswin does.
@@ -31,6 +34,7 @@ import (
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnserver"
"tailscale.com/ipn/store"
"tailscale.com/logpolicy"
"tailscale.com/net/dns"
"tailscale.com/net/tsdial"
@@ -335,8 +339,7 @@ func startIPNServer(ctx context.Context, logid string) error {
return nil, fmt.Errorf("%w\n\nlogid: %v", res.Err, logid)
}
}
store, err := ipnserver.StateStore(statePathOrDefault(), logf)
store, err := store.New(logf, statePathOrDefault())
if err != nil {
return err
}

View File

@@ -46,10 +46,10 @@ type fakeTB struct {
}
func (t fakeTB) Cleanup(_ func()) {}
func (t fakeTB) Error(args ...interface{}) {
func (t fakeTB) Error(args ...any) {
t.Fatal(args...)
}
func (t fakeTB) Errorf(format string, args ...interface{}) {
func (t fakeTB) Errorf(format string, args ...any) {
t.Fatalf(format, args...)
}
func (t fakeTB) Fail() {
@@ -61,17 +61,17 @@ func (t fakeTB) FailNow() {
func (t fakeTB) Failed() bool {
return false
}
func (t fakeTB) Fatal(args ...interface{}) {
func (t fakeTB) Fatal(args ...any) {
log.Fatal(args...)
}
func (t fakeTB) Fatalf(format string, args ...interface{}) {
func (t fakeTB) Fatalf(format string, args ...any) {
log.Fatalf(format, args...)
}
func (t fakeTB) Helper() {}
func (t fakeTB) Log(args ...interface{}) {
func (t fakeTB) Log(args ...any) {
log.Print(args...)
}
func (t fakeTB) Logf(format string, args ...interface{}) {
func (t fakeTB) Logf(format string, args ...any) {
log.Printf(format, args...)
}
func (t fakeTB) Name() string {
@@ -80,13 +80,13 @@ func (t fakeTB) Name() string {
func (t fakeTB) Setenv(key string, value string) {
panic("not implemented")
}
func (t fakeTB) Skip(args ...interface{}) {
func (t fakeTB) Skip(args ...any) {
t.Fatal("skipped")
}
func (t fakeTB) SkipNow() {
t.Fatal("skipnow")
}
func (t fakeTB) Skipf(format string, args ...interface{}) {
func (t fakeTB) Skipf(format string, args ...any) {
t.Logf(format, args...)
t.Fatal("skipped")
}

View File

@@ -31,7 +31,7 @@ import (
"unsafe"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/tailscale/ssh"
gossh "golang.org/x/crypto/ssh"
"inet.af/netaddr"
"tailscale.com/net/interfaces"

View File

@@ -267,18 +267,16 @@ func (c *Conn) Write(bs []byte) (n int, err error) {
ciphertext, err := c.encryptLocked(toSend)
if err != nil {
return 0, err
return sent, err
}
n, err := c.conn.Write(ciphertext)
sent += n
if err != nil {
if _, err := c.conn.Write(ciphertext); err != nil {
// Return the raw error on the Write that actually
// failed. For future writes, return that error wrapped in
// a desync error.
c.tx.err = errPartialWrite{err}
return sent, err
}
sent += len(toSend)
}
return sent, nil
}

View File

@@ -7,6 +7,7 @@ package controlclient
import (
"context"
"fmt"
"net/http"
"sync"
"time"
@@ -91,7 +92,7 @@ func NewNoStart(opts Options) (*Auto, error) {
return nil, err
}
if opts.Logf == nil {
opts.Logf = func(fmt string, args ...interface{}) {}
opts.Logf = func(fmt string, args ...any) {}
}
if opts.TimeNow == nil {
opts.TimeNow = time.Now
@@ -657,6 +658,10 @@ func (c *Auto) Logout(ctx context.Context) error {
}
}
func (c *Auto) SetExpirySooner(ctx context.Context, expiry time.Time) error {
return c.direct.SetExpirySooner(ctx, expiry)
}
// UpdateEndpoints sets the client's discovered endpoints and sends
// them to the control server if they've changed.
//
@@ -677,6 +682,7 @@ func (c *Auto) Shutdown() {
c.mu.Lock()
inSendStatus := c.inSendStatus
closed := c.closed
direct := c.direct
if !closed {
c.closed = true
c.statusFunc = nil
@@ -691,6 +697,9 @@ func (c *Auto) Shutdown() {
<-c.authDone
c.cancelMapUnsafely()
<-c.mapDone
if direct != nil {
direct.Close()
}
c.logf("Client.Shutdown done.")
}
}
@@ -717,3 +726,7 @@ func (c *Auto) TestOnlyTimeNow() time.Time {
func (c *Auto) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) error {
return c.direct.SetDNS(ctx, req)
}
func (c *Auto) DoNoiseRequest(req *http.Request) (*http.Response, error) {
return c.direct.DoNoiseRequest(req)
}

View File

@@ -11,6 +11,8 @@ package controlclient
import (
"context"
"net/http"
"time"
"tailscale.com/tailcfg"
)
@@ -45,6 +47,9 @@ type Client interface {
// Logout starts a synchronous logout process. It doesn't return
// until the logout operation has been completed.
Logout(context.Context) error
// SetExpirySooner sets the node's expiry time via the controlclient,
// as long as it's shorter than the current expiry time.
SetExpirySooner(context.Context, time.Time) error
// SetPaused pauses or unpauses the controlclient activity as much
// as possible, without losing its internal state, to minimize
// unnecessary network activity.
@@ -78,6 +83,9 @@ type Client interface {
// SetDNS sends the SetDNSRequest request to the control plane server,
// requesting a DNS record be created or updated.
SetDNS(context.Context, *tailcfg.SetDNSRequest) error
// DoNoiseRequest sends an HTTP request to the control plane
// over the Noise transport.
DoNoiseRequest(*http.Request) (*http.Response, error)
}
// UserVisibleError is an error that should be shown to users.

View File

@@ -27,6 +27,7 @@ import (
"time"
"go4.org/mem"
"golang.org/x/sync/singleflight"
"inet.af/netaddr"
"tailscale.com/control/controlknobs"
"tailscale.com/envknob"
@@ -47,6 +48,7 @@ import (
"tailscale.com/types/opt"
"tailscale.com/types/persist"
"tailscale.com/util/clientmetric"
"tailscale.com/util/multierr"
"tailscale.com/util/systemd"
"tailscale.com/wgengine/monitor"
)
@@ -68,8 +70,13 @@ type Direct struct {
skipIPForwardingCheck bool
pinger Pinger
mu sync.Mutex // mutex guards the following fields
serverKey key.MachinePublic
mu sync.Mutex // mutex guards the following fields
serverKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key
serverNoiseKey key.MachinePublic
sfGroup singleflight.Group // protects noiseClient creation.
noiseClient *noiseClient
persist persist.Persist
authKey string
tryingNewKey key.NodePrivate
@@ -200,6 +207,19 @@ func NewDirect(opts Options) (*Direct, error) {
return c, nil
}
// Close closes the underlying Noise connection(s).
func (c *Direct) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
if c.noiseClient != nil {
if err := c.noiseClient.Close(); err != nil {
return err
}
}
c.noiseClient = nil
return nil
}
// SetHostinfo clones the provided Hostinfo and remembers it for the
// next update. It reports whether the Hostinfo has changed.
func (c *Direct) SetHostinfo(hi *tailcfg.Hostinfo) bool {
@@ -282,12 +302,33 @@ func (c *Direct) doLoginOrRegen(ctx context.Context, opt loginOpt) (newURL strin
return url, err
}
// SetExpirySooner attempts to shorten the expiry to the specified time.
func (c *Direct) SetExpirySooner(ctx context.Context, expiry time.Time) error {
c.logf("[v1] direct.SetExpirySooner()")
newURL, err := c.doLoginOrRegen(ctx, loginOpt{Expiry: &expiry})
c.logf("[v1] SetExpirySooner control response: newURL=%v, err=%v", newURL, err)
return err
}
type loginOpt struct {
Token *tailcfg.Oauth2Token
Flags LoginFlags
Regen bool
Regen bool // generate a new nodekey, can be overridden in doLogin
URL string
Logout bool
Logout bool // set the expiry to the far past, expiring the node
// Expiry, if non-nil, attempts to set the node expiry to the
// specified time and cannot be used to extend the expiry.
// It is ignored if Logout is set since Logout works by setting a
// expiry time in the far past.
Expiry *time.Time
}
// httpClient provides a common interface for the noiseClient and
// the NaCl box http.Client.
type httpClient interface {
Do(req *http.Request) (*http.Response, error)
}
func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, newURL string, err error) {
@@ -295,6 +336,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
persist := c.persist
tryingNewKey := c.tryingNewKey
serverKey := c.serverKey
serverNoiseKey := c.serverNoiseKey
authKey := c.authKey
hi := c.hostinfo.Clone()
backendLogID := hi.BackendLogID
@@ -326,18 +368,27 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
c.logf("doLogin(regen=%v, hasUrl=%v)", regen, opt.URL != "")
if serverKey.IsZero() {
var err error
serverKey, err = loadServerKey(ctx, c.httpc, c.serverURL)
keys, err := loadServerPubKeys(ctx, c.httpc, c.serverURL)
if err != nil {
return regen, opt.URL, err
}
c.logf("control server key %s from %s", serverKey.ShortString(), c.serverURL)
c.mu.Lock()
c.serverKey = serverKey
c.serverKey = keys.LegacyPublicKey
c.serverNoiseKey = keys.PublicKey
c.mu.Unlock()
}
serverKey = keys.LegacyPublicKey
serverNoiseKey = keys.PublicKey
// For servers supporting the Noise transport,
// proactively shut down our TLS TCP connection.
// We're not going to need it and it's nicer to the
// server.
if !serverNoiseKey.IsZero() {
c.httpc.CloseIdleConnections()
}
}
var oldNodeKey key.NodePublic
switch {
case opt.Logout:
@@ -378,6 +429,8 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
}
if opt.Logout {
request.Expiry = time.Unix(123, 0) // far in the past
} else if opt.Expiry != nil {
request.Expiry = *opt.Expiry
}
c.logf("RegisterReq: onode=%v node=%v fup=%v",
request.OldNodeKey.ShortString(),
@@ -405,22 +458,32 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
c.logf("RegisterRequest: %s", j)
}
bodyData, err := encode(request, serverKey, machinePrivKey)
// URL and httpc are protocol specific.
var url string
var httpc httpClient
if serverNoiseKey.IsZero() {
httpc = c.httpc
url = fmt.Sprintf("%s/machine/%s", c.serverURL, machinePrivKey.Public().UntypedHexString())
} else {
request.Version = tailcfg.CurrentCapabilityVersion
httpc, err = c.getNoiseClient()
if err != nil {
return regen, opt.URL, fmt.Errorf("getNoiseClient: %w", err)
}
url = fmt.Sprintf("%s/machine/register", c.serverURL)
url = strings.Replace(url, "http:", "https:", 1)
}
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
if err != nil {
return regen, opt.URL, err
}
body := bytes.NewReader(bodyData)
u := fmt.Sprintf("%s/machine/%s", c.serverURL, machinePrivKey.Public().UntypedHexString())
req, err := http.NewRequest("POST", u, body)
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyData))
if err != nil {
return regen, opt.URL, err
}
req = req.WithContext(ctx)
res, err := c.httpc.Do(req)
res, err := httpc.Do(req)
if err != nil {
return regen, opt.URL, fmt.Errorf("register request: %v", err)
return regen, opt.URL, fmt.Errorf("register request: %w", err)
}
if res.StatusCode != 200 {
msg, _ := ioutil.ReadAll(res.Body)
@@ -429,7 +492,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
res.StatusCode, strings.TrimSpace(string(msg)))
}
resp := tailcfg.RegisterResponse{}
if err := decode(res, &resp, serverKey, machinePrivKey); err != nil {
if err := decode(res, &resp, serverKey, serverNoiseKey, machinePrivKey); err != nil {
c.logf("error decoding RegisterResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
return regen, opt.URL, fmt.Errorf("register request: %v", err)
}
@@ -576,6 +639,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
persist := c.persist
serverURL := c.serverURL
serverKey := c.serverKey
serverNoiseKey := c.serverNoiseKey
hi := c.hostinfo.Clone()
backendLogID := hi.BackendLogID
localPort := c.localPort
@@ -614,7 +678,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
}
request := &tailcfg.MapRequest{
Version: tailcfg.CurrentMapRequestVersion,
Version: tailcfg.CurrentCapabilityVersion,
KeepAlive: c.keepAlive,
NodeKey: persist.PrivateNodeKey.Public(),
DiscoKey: c.discoPubKey,
@@ -658,7 +722,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
request.ReadOnly = true
}
bodyData, err := encode(request, serverKey, machinePrivKey)
bodyData, err := encode(request, serverKey, serverNoiseKey, machinePrivKey)
if err != nil {
vlogf("netmap: encode: %v", err)
return err
@@ -669,14 +733,28 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
machinePubKey := machinePrivKey.Public()
t0 := time.Now()
u := fmt.Sprintf("%s/machine/%s/map", serverURL, machinePubKey.UntypedHexString())
req, err := http.NewRequestWithContext(ctx, "POST", u, bytes.NewReader(bodyData))
// Url and httpc are protocol specific.
var url string
var httpc httpClient
if serverNoiseKey.IsZero() {
httpc = c.httpc
url = fmt.Sprintf("%s/machine/%s/map", serverURL, machinePubKey.UntypedHexString())
} else {
httpc, err = c.getNoiseClient()
if err != nil {
return fmt.Errorf("getNoiseClient: %w", err)
}
url = fmt.Sprintf("%s/machine/map", serverURL)
url = strings.Replace(url, "http:", "https:", 1)
}
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyData))
if err != nil {
return err
}
res, err := c.httpc.Do(req)
res, err := httpc.Do(req)
if err != nil {
vlogf("netmap: Do: %v", err)
return err
@@ -772,6 +850,9 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
go answerPing(c.logf, c.httpc, pr)
}
if resp.ControlTime != nil && !resp.ControlTime.IsZero() {
c.logf.JSON(1, "controltime", resp.ControlTime.UTC())
}
if resp.KeepAlive {
vlogf("netmap: got keep-alive")
} else {
@@ -862,7 +943,9 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm
return nil
}
func decode(res *http.Response, v interface{}, serverKey key.MachinePublic, mkey key.MachinePrivate) error {
// decode JSON decodes the res.Body into v. If serverNoiseKey is not specified,
// it uses the serverKey and mkey to decode the message from the NaCl-crypto-box.
func decode(res *http.Response, v any, serverKey, serverNoiseKey key.MachinePublic, mkey key.MachinePrivate) error {
defer res.Body.Close()
msg, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<20))
if err != nil {
@@ -871,6 +954,9 @@ func decode(res *http.Response, v interface{}, serverKey key.MachinePublic, mkey
if res.StatusCode != 200 {
return fmt.Errorf("%d: %v", res.StatusCode, string(msg))
}
if !serverNoiseKey.IsZero() {
return json.Unmarshal(msg, v)
}
return decodeMsg(msg, v, serverKey, mkey)
}
@@ -881,14 +967,24 @@ var (
var jsonEscapedZero = []byte(`\u0000`)
func (c *Direct) decodeMsg(msg []byte, v interface{}, machinePrivKey key.MachinePrivate) error {
// decodeMsg is responsible for uncompressing msg and unmarshaling into v.
// If c.serverNoiseKey is not specified, it uses the c.serverKey and mkey
// to first the decrypt msg from the NaCl-crypto-box.
func (c *Direct) decodeMsg(msg []byte, v any, mkey key.MachinePrivate) error {
c.mu.Lock()
serverKey := c.serverKey
serverNoiseKey := c.serverNoiseKey
c.mu.Unlock()
decrypted, ok := machinePrivKey.OpenFrom(serverKey, msg)
if !ok {
return errors.New("cannot decrypt response")
var decrypted []byte
if serverNoiseKey.IsZero() {
var ok bool
decrypted, ok = mkey.OpenFrom(serverKey, msg)
if !ok {
return errors.New("cannot decrypt response")
}
} else {
decrypted = msg
}
var b []byte
if c.newDecompressor == nil {
@@ -920,7 +1016,7 @@ func (c *Direct) decodeMsg(msg []byte, v interface{}, machinePrivKey key.Machine
}
func decodeMsg(msg []byte, v interface{}, serverKey key.MachinePublic, machinePrivKey key.MachinePrivate) error {
func decodeMsg(msg []byte, v any, serverKey key.MachinePublic, machinePrivKey key.MachinePrivate) error {
decrypted, ok := machinePrivKey.OpenFrom(serverKey, msg)
if !ok {
return errors.New("cannot decrypt response")
@@ -934,7 +1030,9 @@ func decodeMsg(msg []byte, v interface{}, serverKey key.MachinePublic, machinePr
return nil
}
func encode(v interface{}, serverKey key.MachinePublic, mkey key.MachinePrivate) ([]byte, error) {
// encode JSON encodes v. If serverNoiseKey is not specified, it uses the serverKey and mkey to
// seal the message into a NaCl-crypto-box.
func encode(v any, serverKey, serverNoiseKey key.MachinePublic, mkey key.MachinePrivate) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
@@ -944,32 +1042,45 @@ func encode(v interface{}, serverKey key.MachinePublic, mkey key.MachinePrivate)
log.Printf("MapRequest: %s", b)
}
}
if !serverNoiseKey.IsZero() {
return b, nil
}
return mkey.SealTo(serverKey, b), nil
}
func loadServerKey(ctx context.Context, httpc *http.Client, serverURL string) (key.MachinePublic, error) {
req, err := http.NewRequest("GET", serverURL+"/key", nil)
func loadServerPubKeys(ctx context.Context, httpc *http.Client, serverURL string) (*tailcfg.OverTLSPublicKeyResponse, error) {
keyURL := fmt.Sprintf("%v/key?v=%d", serverURL, tailcfg.CurrentCapabilityVersion)
req, err := http.NewRequestWithContext(ctx, "GET", keyURL, nil)
if err != nil {
return key.MachinePublic{}, fmt.Errorf("create control key request: %v", err)
return nil, fmt.Errorf("create control key request: %v", err)
}
req = req.WithContext(ctx)
res, err := httpc.Do(req)
if err != nil {
return key.MachinePublic{}, fmt.Errorf("fetch control key: %v", err)
return nil, fmt.Errorf("fetch control key: %v", err)
}
defer res.Body.Close()
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 1<<16))
b, err := ioutil.ReadAll(io.LimitReader(res.Body, 64<<10))
if err != nil {
return key.MachinePublic{}, fmt.Errorf("fetch control key response: %v", err)
return nil, fmt.Errorf("fetch control key response: %v", err)
}
if res.StatusCode != 200 {
return key.MachinePublic{}, fmt.Errorf("fetch control key: %d: %s", res.StatusCode, string(b))
return nil, fmt.Errorf("fetch control key: %d", res.StatusCode)
}
var out tailcfg.OverTLSPublicKeyResponse
jsonErr := json.Unmarshal(b, &out)
if jsonErr == nil {
return &out, nil
}
// Some old control servers might not be updated to send the new format.
// Accept the old pre-JSON format too.
out = tailcfg.OverTLSPublicKeyResponse{}
k, err := key.ParseMachinePublicUntyped(mem.B(b))
if err != nil {
return key.MachinePublic{}, fmt.Errorf("fetch control key: %v", err)
return nil, multierr.New(jsonErr, err)
}
return k, nil
out.LegacyPublicKey = k
return &out, nil
}
// Debug contains temporary internal-only debug knobs.
@@ -1187,6 +1298,78 @@ func sleepAsRequested(ctx context.Context, logf logger.Logf, timeoutReset chan<-
}
}
// getNoiseClient returns the noise client, creating one if one doesn't exist.
func (c *Direct) getNoiseClient() (*noiseClient, error) {
c.mu.Lock()
serverNoiseKey := c.serverNoiseKey
nc := c.noiseClient
c.mu.Unlock()
if serverNoiseKey.IsZero() {
return nil, errors.New("zero serverNoiseKey")
}
if nc != nil {
return nc, nil
}
np, err, _ := c.sfGroup.Do("noise", func() (any, error) {
k, err := c.getMachinePrivKey()
if err != nil {
return nil, err
}
nc, err = newNoiseClient(k, serverNoiseKey, c.serverURL)
if err != nil {
return nil, err
}
c.mu.Lock()
defer c.mu.Unlock()
c.noiseClient = nc
return nc, nil
})
if err != nil {
return nil, err
}
return np.(*noiseClient), nil
}
// setDNSNoise sends the SetDNSRequest request to the control plane server over Noise,
// requesting a DNS record be created or updated.
func (c *Direct) setDNSNoise(ctx context.Context, req *tailcfg.SetDNSRequest) error {
newReq := *req
newReq.Version = tailcfg.CurrentCapabilityVersion
np, err := c.getNoiseClient()
if err != nil {
return err
}
bodyData, err := json.Marshal(newReq)
if err != nil {
return err
}
res, err := np.Post(fmt.Sprintf("https://%v/%v", np.serverHost, "machine/set-dns"), "application/json", bytes.NewReader(bodyData))
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode != 200 {
msg, _ := ioutil.ReadAll(res.Body)
return fmt.Errorf("set-dns response: %v, %.200s", res.Status, strings.TrimSpace(string(msg)))
}
var setDNSRes tailcfg.SetDNSResponse
if err := json.NewDecoder(res.Body).Decode(&setDNSRes); err != nil {
c.logf("error decoding SetDNSResponse: %v", err)
return fmt.Errorf("set-dns-response: %w", err)
}
return nil
}
// noiseConfigured reports whether the client can communicate with Control
// over Noise.
func (c *Direct) noiseConfigured() bool {
c.mu.Lock()
defer c.mu.Unlock()
return !c.serverNoiseKey.IsZero()
}
// SetDNS sends the SetDNSRequest request to the control plane server,
// requesting a DNS record be created or updated.
func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err error) {
@@ -1196,6 +1379,9 @@ func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err er
metricSetDNSError.Add(1)
}
}()
if c.noiseConfigured() {
return c.setDNSNoise(ctx, req)
}
c.mu.Lock()
serverKey := c.serverKey
c.mu.Unlock()
@@ -1211,7 +1397,9 @@ func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err er
return errors.New("getMachinePrivKey returned zero key")
}
bodyData, err := encode(req, serverKey, machinePrivKey)
// TODO(maisem): dedupe this codepath from SetDNSNoise.
var serverNoiseKey key.MachinePublic
bodyData, err := encode(req, serverKey, serverNoiseKey, machinePrivKey)
if err != nil {
return err
}
@@ -1231,15 +1419,23 @@ func (c *Direct) SetDNS(ctx context.Context, req *tailcfg.SetDNSRequest) (err er
msg, _ := ioutil.ReadAll(res.Body)
return fmt.Errorf("set-dns response: %v, %.200s", res.Status, strings.TrimSpace(string(msg)))
}
var setDNSRes struct{} // no fields yet
if err := decode(res, &setDNSRes, serverKey, machinePrivKey); err != nil {
var setDNSRes tailcfg.SetDNSResponse
if err := decode(res, &setDNSRes, serverKey, serverNoiseKey, machinePrivKey); err != nil {
c.logf("error decoding SetDNSResponse with server key %s and machine key %s: %v", serverKey, machinePrivKey.Public(), err)
return fmt.Errorf("set-dns-response: %v", err)
return fmt.Errorf("set-dns-response: %w", err)
}
return nil
}
func (c *Direct) DoNoiseRequest(req *http.Request) (*http.Response, error) {
nc, err := c.getNoiseClient()
if err != nil {
return nil, err
}
return nc.Do(req)
}
// tsmpPing sends a Ping to pr.IP, and sends an http request back to pr.URL
// with ping response data.
func tsmpPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, pinger Pinger) error {

View File

@@ -39,6 +39,7 @@ type mapSession struct {
lastDERPMap *tailcfg.DERPMap
lastUserProfile map[tailcfg.UserID]tailcfg.UserProfile
lastParsedPacketFilter []filter.Match
lastSSHPolicy *tailcfg.SSHPolicy
collectServices bool
previousPeers []*tailcfg.Node // for delta-purposes
lastDomain string
@@ -97,6 +98,9 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
if c := resp.DNSConfig; c != nil {
ms.lastDNSConfig = c
}
if p := resp.SSHPolicy; p != nil {
ms.lastSSHPolicy = p
}
if v, ok := resp.CollectServices.Get(); ok {
ms.collectServices = v
@@ -117,6 +121,7 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
Domain: ms.lastDomain,
DNS: *ms.lastDNSConfig,
PacketFilter: ms.lastParsedPacketFilter,
SSHPolicy: ms.lastSSHPolicy,
CollectServices: ms.collectServices,
DERPMap: ms.lastDERPMap,
Debug: resp.Debug,
@@ -133,7 +138,9 @@ func (ms *mapSession) netmapForResponse(resp *tailcfg.MapResponse) *netmap.Netwo
nm.Name = node.Name
nm.Addresses = filterSelfAddresses(node.Addresses)
nm.User = node.User
nm.Hostinfo = node.Hostinfo
if node.Hostinfo.Valid() {
nm.Hostinfo = *node.Hostinfo.AsStruct()
}
if node.MachineAuthorized {
nm.MachineStatus = tailcfg.MachineAuthorized
} else {

View File

@@ -0,0 +1,159 @@
// 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 controlclient
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"sync"
"time"
"golang.org/x/net/http2"
"tailscale.com/control/controlbase"
"tailscale.com/control/controlhttp"
"tailscale.com/types/key"
"tailscale.com/util/multierr"
)
// noiseConn is a wrapper around controlbase.Conn.
// It allows attaching an ID to a connection to allow
// cleaning up references in the pool when the connection
// is closed.
type noiseConn struct {
*controlbase.Conn
id int
pool *noiseClient
}
func (c *noiseConn) Close() error {
if err := c.Conn.Close(); err != nil {
return err
}
c.pool.connClosed(c.id)
return nil
}
// noiseClient provides a http.Client to connect to tailcontrol over
// the ts2021 protocol.
type noiseClient struct {
*http.Client // HTTP client used to talk to tailcontrol
privKey key.MachinePrivate
serverPubKey key.MachinePublic
serverHost string // the host:port part of serverURL
// mu only protects the following variables.
mu sync.Mutex
nextID int
connPool map[int]*noiseConn // active connections not yet closed; see noiseConn.Close
}
// newNoiseClient returns a new noiseClient for the provided server and machine key.
// serverURL is of the form https://<host>:<port> (no trailing slash).
func newNoiseClient(priKey key.MachinePrivate, serverPubKey key.MachinePublic, serverURL string) (*noiseClient, error) {
u, err := url.Parse(serverURL)
if err != nil {
return nil, err
}
var host string
if u.Port() != "" {
// If there is an explicit port specified use it.
host = u.Host
} else {
// Otherwise, controlhttp.Dial expects an http endpoint.
host = fmt.Sprintf("%v:80", u.Hostname())
}
np := &noiseClient{
serverPubKey: serverPubKey,
privKey: priKey,
serverHost: host,
}
// Create the HTTP/2 Transport using a net/http.Transport
// (which only does HTTP/1) because it's the only way to
// configure certain properties on the http2.Transport. But we
// never actually use the net/http.Transport for any HTTP/1
// requests.
h2Transport, err := http2.ConfigureTransports(&http.Transport{
IdleConnTimeout: time.Minute,
})
if err != nil {
return nil, err
}
// Let the HTTP/2 Transport think it's dialing out using TLS,
// but it's actually our Noise dialer:
h2Transport.DialTLS = np.dial
// ConfigureTransports assumes it's being used to wire up an HTTP/1
// and HTTP/2 Transport together, so its returned http2.Transport
// has a ConnPool already initialized that's configured to not dial
// (assuming it's only called from the HTTP/1 Transport). But we
// want it to dial, so nil it out before use. On first use it has
// a sync.Once that lazily initializes the ConnPool to its default
// one that dials.
h2Transport.ConnPool = nil
np.Client = &http.Client{Transport: h2Transport}
return np, nil
}
// connClosed removes the connection with the provided ID from the pool
// of active connections.
func (nc *noiseClient) connClosed(id int) {
nc.mu.Lock()
defer nc.mu.Unlock()
delete(nc.connPool, id)
}
// Close closes all the underlying noise connections.
// It is a no-op and returns nil if the connection is already closed.
func (nc *noiseClient) Close() error {
nc.mu.Lock()
conns := nc.connPool
nc.connPool = nil
nc.mu.Unlock()
var errors []error
for _, c := range conns {
if err := c.Close(); err != nil {
errors = append(errors, err)
}
}
return multierr.New(errors...)
}
// dial opens a new connection to tailcontrol, fetching the server noise key
// if not cached. It implements the signature needed by http2.Transport.DialTLS
// but ignores all params as it only dials out to the server the noiseClient was
// created for.
func (nc *noiseClient) dial(_, _ string, _ *tls.Config) (net.Conn, error) {
nc.mu.Lock()
connID := nc.nextID
if nc.connPool == nil {
nc.connPool = make(map[int]*noiseConn)
}
nc.nextID++
nc.mu.Unlock()
// Timeout is a little arbitrary, but plenty long enough for even the
// highest latency links.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
conn, err := controlhttp.Dial(ctx, nc.serverHost, nc.privKey, nc.serverPubKey)
if err != nil {
return nil, err
}
nc.mu.Lock()
defer nc.mu.Unlock()
ncc := &noiseConn{Conn: conn, id: connID, pool: nc}
nc.connPool[ncc.id] = ncc
return ncc, nil
}

View File

@@ -40,11 +40,19 @@ import (
"tailscale.com/types/key"
)
// upgradeHeader is the value of the Upgrade HTTP header used to
// indicate the Tailscale control protocol.
const (
upgradeHeaderValue = "tailscale-control-protocol"
// upgradeHeader is the value of the Upgrade HTTP header used to
// indicate the Tailscale control protocol.
upgradeHeaderValue = "tailscale-control-protocol"
// handshakeHeaderName is the HTTP request header that can
// optionally contain base64-encoded initial handshake
// payload, to save an RTT.
handshakeHeaderName = "X-Tailscale-Handshake"
// serverUpgradePath is where the server-side HTTP handler to
// to do the protocol switch is located.
serverUpgradePath = "/ts2021"
)
// Dial connects to the HTTP server at addr, requests to switch to the
@@ -53,6 +61,9 @@ const (
//
// If Dial fails to connect using addr, it also tries to tunnel over
// TLS to <addr's host>:443 as a compatibility fallback.
//
// The provided ctx is only used for the initial connection, until
// Dial returns. It does not affect the connection once established.
func Dial(ctx context.Context, addr string, machineKey key.MachinePrivate, controlKey key.MachinePublic) (*controlbase.Conn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
@@ -92,7 +103,7 @@ func (a *dialParams) dial() (*controlbase.Conn, error) {
u := &url.URL{
Scheme: "http",
Host: net.JoinHostPort(a.host, a.httpPort),
Path: "/switch",
Path: serverUpgradePath,
}
conn, httpErr := a.tryURL(u, init)
if httpErr == nil {

View File

@@ -453,6 +453,9 @@ func (s *Server) initMetacert() {
// Windows requires NotAfter and NotBefore set:
NotAfter: time.Now().Add(30 * 24 * time.Hour),
NotBefore: time.Now().Add(-30 * 24 * time.Hour),
// Per https://github.com/golang/go/issues/51759#issuecomment-1071147836,
// macOS requires BasicConstraints when subject == issuer:
BasicConstraintsValid: true,
}
cert, err := x509.CreateCertificate(crand.Reader, tmpl, tmpl, pub, priv)
if err != nil {
@@ -1641,8 +1644,8 @@ func (m multiForwarder) ForwardPacket(src, dst key.NodePublic, payload []byte) e
return fwd.ForwardPacket(src, dst, payload)
}
func (s *Server) expVarFunc(f func() interface{}) expvar.Func {
return expvar.Func(func() interface{} {
func (s *Server) expVarFunc(f func() any) expvar.Func {
return expvar.Func(func() any {
s.mu.Lock()
defer s.mu.Unlock()
return f()
@@ -1652,14 +1655,14 @@ func (s *Server) expVarFunc(f func() interface{}) expvar.Func {
// ExpVar returns an expvar variable suitable for registering with expvar.Publish.
func (s *Server) ExpVar() expvar.Var {
m := new(metrics.Set)
m.Set("gauge_memstats_sys0", expvar.Func(func() interface{} { return int64(s.memSys0) }))
m.Set("gauge_watchers", s.expVarFunc(func() interface{} { return len(s.watchers) }))
m.Set("gauge_current_file_descriptors", expvar.Func(func() interface{} { return metrics.CurrentFDs() }))
m.Set("gauge_memstats_sys0", expvar.Func(func() any { return int64(s.memSys0) }))
m.Set("gauge_watchers", s.expVarFunc(func() any { return len(s.watchers) }))
m.Set("gauge_current_file_descriptors", expvar.Func(func() any { return metrics.CurrentFDs() }))
m.Set("gauge_current_connections", &s.curClients)
m.Set("gauge_current_home_connections", &s.curHomeClients)
m.Set("gauge_clients_total", expvar.Func(func() interface{} { return len(s.clientsMesh) }))
m.Set("gauge_clients_local", expvar.Func(func() interface{} { return len(s.clients) }))
m.Set("gauge_clients_remote", expvar.Func(func() interface{} { return len(s.clientsMesh) - len(s.clients) }))
m.Set("gauge_clients_total", expvar.Func(func() any { return len(s.clientsMesh) }))
m.Set("gauge_clients_local", expvar.Func(func() any { return len(s.clients) }))
m.Set("gauge_clients_remote", expvar.Func(func() any { return len(s.clientsMesh) - len(s.clients) }))
m.Set("gauge_current_dup_client_keys", &s.dupClientKeys)
m.Set("gauge_current_dup_client_conns", &s.dupClientConns)
m.Set("counter_total_dup_client_conns", &s.dupClientConnTotal)
@@ -1683,7 +1686,7 @@ func (s *Server) ExpVar() expvar.Var {
m.Set("multiforwarder_created", &s.multiForwarderCreated)
m.Set("multiforwarder_deleted", &s.multiForwarderDeleted)
m.Set("packet_forwarder_delete_other_value", &s.removePktForwardOther)
m.Set("average_queue_duration_ms", expvar.Func(func() interface{} {
m.Set("average_queue_duration_ms", expvar.Func(func() any {
return math.Float64frombits(atomic.LoadUint64(s.avgQueueDuration))
}))
var expvarVersion expvar.String
@@ -1813,7 +1816,7 @@ func (s *Server) ServeDebugTraffic(w http.ResponseWriter, r *http.Request) {
}
var bufioWriterPool = &sync.Pool{
New: func() interface{} {
New: func() any {
return bufio.NewWriterSize(ioutil.Discard, 2<<10)
},
}

View File

@@ -9,6 +9,7 @@ import (
"bytes"
"context"
"crypto/x509"
"encoding/asn1"
"encoding/json"
"errors"
"expvar"
@@ -790,6 +791,17 @@ func TestMetaCert(t *testing.T) {
if g, w := cert.Subject.CommonName, fmt.Sprintf("derpkey%s", pub.UntypedHexString()); g != w {
t.Errorf("CommonName = %q; want %q", g, w)
}
if n := len(cert.Extensions); n != 1 {
t.Fatalf("got %d extensions; want 1", n)
}
// oidExtensionBasicConstraints is the Basic Constraints ID copied
// from the x509 package.
oidExtensionBasicConstraints := asn1.ObjectIdentifier{2, 5, 29, 19}
if id := cert.Extensions[0].Id; !id.Equal(oidExtensionBasicConstraints) {
t.Errorf("extension ID = %v; want %v", id, oidExtensionBasicConstraints)
}
}
type dummyNetConn struct {
@@ -802,7 +814,7 @@ func TestClientRecv(t *testing.T) {
tests := []struct {
name string
input []byte
want interface{}
want any
}{
{
name: "ping",

View File

@@ -46,7 +46,7 @@ func noteEnv(k, v string) {
// logf is logger.Logf, but logger depends on envknob, so for circular
// dependency reasons, make a type alias (so it's still assignable,
// but has nice docs here).
type logf = func(format string, args ...interface{})
type logf = func(format string, args ...any)
// LogCurrent logs the currently set environment knobs.
func LogCurrent(logf logf) {

42
go.mod
View File

@@ -1,12 +1,11 @@
module tailscale.com
go 1.17
go 1.18
require (
filippo.io/mkcert v1.4.3
github.com/akutz/memconn v0.1.0
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/aws/aws-sdk-go-v2 v1.11.2
github.com/aws/aws-sdk-go-v2/config v1.11.0
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.7.4
@@ -16,19 +15,19 @@ require (
github.com/creack/pty v1.1.17
github.com/dave/jennifer v1.4.1
github.com/frankban/quicktest v1.14.0
github.com/gliderlabs/ssh v0.3.3
github.com/go-ole/go-ole v1.2.6
github.com/godbus/dbus/v5 v5.0.6
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da
github.com/google/go-cmp v0.5.6
github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.3.0
github.com/goreleaser/nfpm v1.10.3
github.com/iancoleman/strcase v0.2.0
github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e
github.com/jsimonetti/rtnetlink v0.0.0-20211203074127-fd9a11f42291
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.13.6
github.com/mdlayher/netlink v1.4.2
github.com/mdlayher/genetlink v1.2.0
github.com/mdlayher/netlink v1.6.0
github.com/mdlayher/sdnotify v0.0.0-20210228150836-ea3ec207d697
github.com/miekg/dns v1.1.43
github.com/mitchellh/go-ps v1.0.0
@@ -36,28 +35,29 @@ require (
github.com/peterbourgon/ff/v3 v3.1.2
github.com/pkg/sftp v1.13.4
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/tailscale/certstore v0.0.0-20210528134328-066c94b793d3
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05
github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85
github.com/tailscale/ssh v0.3.4-0.20220313013419-be8b7add4057
github.com/tcnksm/go-httpstat v0.2.0
github.com/toqueteos/webbrowser v1.2.0
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6
github.com/u-root/u-root v0.8.0
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
go4.org/mem v0.0.0-20210711025021-927187094b94
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
golang.org/x/net v0.0.0-20211205041911-012df41ee64c
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
golang.org/x/tools v0.1.8
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45
golang.org/x/tools v0.1.10
golang.zx2c4.com/wireguard v0.0.0-20220317000134-95b48cdb3961
golang.zx2c4.com/wireguard/windows v0.4.10
gvisor.dev/gvisor v0.0.0-20220126021142-d8aa030b2591
honnef.co/go/tools v0.2.2
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
inet.af/wf v0.0.0-20211204062712-86aaea0a7310
@@ -79,6 +79,7 @@ require (
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/ashanbrown/forbidigo v1.2.0 // indirect
github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.0.0 // indirect
@@ -114,6 +115,7 @@ require (
github.com/fatih/structtag v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fzipp/gocyclo v0.3.1 // indirect
github.com/gliderlabs/ssh v0.3.3 // indirect
github.com/go-critic/go-critic v0.6.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
@@ -160,7 +162,7 @@ require (
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/julz/importas v0.0.0-20210922140945-27e0a5d4dee2 // indirect
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/kisielk/errcheck v1.6.0 // indirect
@@ -181,7 +183,7 @@ require (
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mbilski/exhaustivestruct v1.2.0 // indirect
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
github.com/mdlayher/socket v0.1.1 // indirect
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect
github.com/mgechev/revive v1.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -236,7 +238,8 @@ require (
github.com/timakin/bodyclose v0.0.0-20210704033933-f49887972144 // indirect
github.com/tomarrell/wrapcheck/v2 v2.4.0 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.4.0 // indirect
github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/ultraware/funlen v0.0.3 // indirect
github.com/ultraware/whitespace v0.0.4 // indirect
github.com/uudashr/gocognit v1.0.5 // indirect
@@ -245,7 +248,8 @@ require (
github.com/yeya24/promlinter v0.1.0 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect

239
go.sum
View File

@@ -1,6 +1,7 @@
4d63.com/gochecknoglobals v0.0.0-20201008074935-acfc0b28355a/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo=
4d63.com/gochecknoglobals v0.1.0 h1:zeZSRqj5yCg28tCkIV/z/lWbwvNm5qnKVS15PI8nhD0=
4d63.com/gochecknoglobals v0.1.0/go.mod h1:wfdC5ZjKSPr7CybKEcgJhUOgeAQW1+7WcyK8OvUilfo=
bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM=
bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@@ -58,6 +59,13 @@ github.com/Antonboom/errname v0.1.5 h1:IM+A/gz0pDhKmlt5KSNTVAvfLMb+65RxavBXpRtCU
github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
github.com/Antonboom/nilnil v0.1.0 h1:DLDavmg0a6G/F4Lt9t7Enrbgb3Oph6LnDE6YVsmTt74=
github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
@@ -80,16 +88,21 @@ github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZC
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.15/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3 h1:XcF0cTDJeiuZ5NU8w7WUDge0HRwwNRmxj/GGk6KSA6g=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
@@ -164,6 +177,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.11.1 h1:QKR7wy5e650q70PFKMfGF9sTo0rZ
github.com/aws/aws-sdk-go-v2/service/sts v1.11.1/go.mod h1:UV2N5HaPfdbDpkgkz4sRzWCvQswZjdO1FfqCWl0t7RA=
github.com/aws/smithy-go v1.9.0 h1:c7FUdEqrQA1/UVKKCNDFQPNKGp4FQg3YW4Ck5SLTG58=
github.com/aws/smithy-go v1.9.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/bazelbuild/rules_go v0.27.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -188,7 +203,10 @@ github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@@ -200,6 +218,8 @@ github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3/
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
@@ -208,8 +228,28 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.3.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg=
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -221,6 +261,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
@@ -229,6 +271,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/daixiang0/gci v0.2.4/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4=
@@ -237,6 +280,7 @@ github.com/daixiang0/gci v0.2.9 h1:iwJvwQpBZmMg31w+QQ6jsyZ54KEATn6/nfARbBNW294=
github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw=
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -246,9 +290,14 @@ github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7q
github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -258,12 +307,14 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/esimonov/ifshort v1.0.3 h1:JD6x035opqGec5fZ0TLjXeROD2p5H7oLGn8MKfy9HTM=
github.com/esimonov/ifshort v1.0.3/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE=
github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw=
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -283,6 +334,7 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
@@ -291,6 +343,7 @@ github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/gliderlabs/ssh v0.1.2-0.20181113160402-cbabf5414432/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA=
github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
@@ -318,10 +371,15 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
@@ -368,6 +426,7 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -376,9 +435,12 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gojuno/minimock/v3 v3.0.4/go.mod h1:HqeqnwV8mAABn3pO5hqF+RE7gjA0jsN8cbbSogoGrzI=
github.com/gojuno/minimock/v3 v3.0.8/go.mod h1:TPKxc8tiB8O83YH2//pOzxvEjaI3TMhd6ev/GmlMiYA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -398,6 +460,7 @@ github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -465,9 +528,16 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.2.1-0.20200615092505-5d8a91de9ae3/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm-tools v0.0.0-20190906225433-1614c142f845/go.mod h1:AVfHadzbdzHo54inR2x1v640jdi1YSi3NauM2DUsxk0=
github.com/google/goexpect v0.0.0-20191001010744-5b6988669ffa/go.mod h1:qtE5aAEkt0vOSA84DBh8aJsz6riL8ONfqfULY7lBjqc=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 h1:CVuJwN34x4xM2aT4sIKhmeib40NeBPhRihNjQmpJsA4=
github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -492,6 +562,7 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1 h1:BRIy5qQZKSC/nthA5ueW547F73BV5hMoIoxhPfhxa3k=
github.com/google/rpmpack v0.0.0-20201206194719-59e495f2b7e1/go.mod h1:+y9lKiqDhR4zkLl+V9h4q0rdyrYVsWWm6LLCQP33DIk=
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -502,9 +573,12 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
github.com/gordonklaus/ineffassign v0.0.0-20210225214923-2e10b2664254/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw=
@@ -519,6 +593,7 @@ github.com/goreleaser/nfpm v1.10.3/go.mod h1:EEC7YD5wi+ol0MiAshpgPANBOkjXDl7wqTL
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -541,6 +616,7 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -586,6 +662,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hexdigest/gowrap v1.1.7/go.mod h1:Z+nBFUDLa01iaNM+/jzoOA1JJ7sm51rnYFauKFUB5fs=
github.com/hexdigest/gowrap v1.1.8/go.mod h1:H/JiFmQMp//tedlV8qt2xBdGzmne6bpbaSuiHmygnMw=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
@@ -597,6 +675,7 @@ github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -604,8 +683,10 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd h1:jupbuQFZtwOBg/3EmK91/rGaYFkqCb9bwHOnwn7Cav0=
github.com/insomniacslk/dhcp v0.0.0-20211026125128-ad197bcd36fd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/insomniacslk/dhcp v0.0.0-20210817203519-d82598001386/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e h1:IQpunlq7T+NiJJMO7ODYV2YWBiv/KnObR3gofX0mWOo=
github.com/insomniacslk/dhcp v0.0.0-20211209223715-7d93572ebe8e/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E=
github.com/intel-go/cpuid v0.0.0-20200819041909-2aa72927c3e2/go.mod h1:RmeVYf9XrPRbRc3XIx0gLYA8qOFvNoPOfaEZduRlEp4=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
@@ -629,8 +710,9 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOvVRsHiKkeGCT6tYBNWyDVuzj9wAaBb5R9qamfw=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
@@ -645,6 +727,7 @@ github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmK
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
github.com/jsimonetti/rtnetlink v0.0.0-20211203074127-fd9a11f42291 h1:0J2ntV09uHLUHC79Z3YKJX2EnfOKL2QkMuHabu4L8JM=
github.com/jsimonetti/rtnetlink v0.0.0-20211203074127-fd9a11f42291/go.mod h1:J7jazXS6RFR/oZT8XdfdD2KQ1bl56ukeE1qt4w8UQaI=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -661,6 +744,7 @@ github.com/julz/importas v0.0.0-20210419104244-841f0c0fe66d/go.mod h1:oSFU2R4XK/
github.com/julz/importas v0.0.0-20210922140945-27e0a5d4dee2 h1:3sSu9gZvOTazWE4B4wsND7ofCsn75BD8Iz1OCBUZISs=
github.com/julz/importas v0.0.0-20210922140945-27e0a5d4dee2/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/kaey/framebuffer v0.0.0-20140402104929-7b385489a1ff/go.mod h1:tS4qtlcKqtt3tCIHUflVSqeP3CLH5Qtv2szX9X2SyhU=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
@@ -675,12 +759,14 @@ github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -693,6 +779,8 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@@ -725,6 +813,7 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ=
github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
@@ -734,6 +823,7 @@ github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -765,10 +855,10 @@ github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwg
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8=
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE=
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/genetlink v1.2.0 h1:4yrIkRV5Wfk1WfpWTcoOlGmsWgQj3OtQN9ZsbrE+XtU=
github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
@@ -779,16 +869,18 @@ github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnN
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
github.com/mdlayher/netlink v1.4.2 h1:3sbnJWe/LETovA7yRZIX3f9McVOWV3OySH6iIBxiFfI=
github.com/mdlayher/netlink v1.4.2/go.mod h1:13VaingaArGUTUxFLf/iEovKxXji32JAtF858jZYEug=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/sdnotify v0.0.0-20210228150836-ea3ec207d697 h1:PBb7ld5cQGfxHF2pKvb/ydtuPwdRaltGI4e0QSCuiNI=
github.com/mdlayher/sdnotify v0.0.0-20210228150836-ea3ec207d697/go.mod h1:HtjVsQfsrBm1GDcDTUFn4ZXhftxTwO/hxrvEiRc61U4=
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=
github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
github.com/mgechev/revive v1.1.2 h1:MiYA/o9M7REjvOF20QN43U8OtXDDHQFKLCtJnxLGLog=
@@ -829,20 +921,24 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4=
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
github.com/mozilla/scribe v0.0.0-20180711195314-fb71baf557c1/go.mod h1:FIczTrinKo8VaLxe6PWTPEXRXDIHz2QAwiaBaP5/4a8=
github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5/go.mod h1:FUqVoUPHSEdDR0MnFM3Dh8AU0pZHLXUD127SAJGER/s=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
@@ -866,13 +962,17 @@ github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXW
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
@@ -881,7 +981,15 @@ github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20211123151946-c2389c3cb60a/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/orangecms/go-framebuffer v0.0.0-20200613202404-a0700d90c330/go.mod h1:3Myb/UszJY32F2G7yGkUtcW/ejHpjlGfYLim7cv2uKA=
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
@@ -891,6 +999,7 @@ github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/getopt v1.1.0 h1:eJ3aFZroQqq0bWmraivjQNt6Dmm5M0h2JcDW38/Azb0=
github.com/pborman/getopt v1.1.0/go.mod h1:FxXoW1Re00sQG/+KIkuSqRL/LwQgSkv7uyac+STFsbk=
github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
@@ -904,6 +1013,7 @@ github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP
github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4/v4 v4.1.11/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -942,8 +1052,10 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
@@ -966,6 +1078,8 @@ github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:r
github.com/quasilyte/regex/syntax v0.0.0-20200805063351-8f842688393c/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU=
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/rck/unit v0.0.3/go.mod h1:jTOnzP4s1OjIP1vdxb4n76b23QPKS4EurYg7sYMr2DM=
github.com/rekby/gpt v0.0.0-20200219180433-a930afbc6edc/go.mod h1:scrOqOnnHVKCHENvFw8k9ajCb88uqLQDA4BvuJNJ2ew=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -986,6 +1100,7 @@ github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoL
github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20200218184317-f459e2d13664/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
@@ -1029,6 +1144,7 @@ github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0H
github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
@@ -1039,17 +1155,20 @@ github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
@@ -1062,6 +1181,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -1075,8 +1195,11 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/sylvia7788/contextcheck v1.0.4 h1:MsiVqROAdr0efZc/fOCt0c235qm9XJqHtWwM+2h2B04=
github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tailscale/certstore v0.0.0-20210528134328-066c94b793d3 h1:fEubocuQkrlcuYeXelhYq/YcKvVVe1Ah7saQEtj98Mo=
github.com/tailscale/certstore v0.0.0-20210528134328-066c94b793d3/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs=
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d h1:K3j02b5j2Iw1xoggN9B2DIEkhWGheqFOeDkdJdBrJI8=
github.com/tailscale/certstore v0.1.1-0.20220316223106-78d6e1c49d8d/go.mod h1:2P+hpOwd53e7JMX/L4f3VXkv1G+33ES6IWZSrkIeWNs=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502 h1:34icjjmqJ2HPjrSuJYEkdZ+0ItmGQAQ75cRHIiftIyE=
github.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/tailscale/goexpect v0.0.0-20210902213824-6e8c725cea41 h1:/V2rCMMWcsjYaYO2MeovLw+ClP63OtXgCF2Y1eb8+Ns=
@@ -1087,6 +1210,8 @@ github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83 h1:f7nwzdAHTUUOJj
github.com/tailscale/hujson v0.0.0-20211105212140-3a0adc019d83/go.mod h1:iTDXJsA6A2wNNjurgic2rk+is6uzU4U2NLm4T+edr6M=
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85 h1:zrsUcqrG2uQSPhaUPjUQwozcRdDdSxxqhNgNZ3drZFk=
github.com/tailscale/netlink v1.1.1-0.20211101221916-cabfb018fe85/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tailscale/ssh v0.3.4-0.20220313013419-be8b7add4057 h1:aN1DLGpS7j6LLQaM33w4Lo6Otvq8Rx60D2ciI/UC1KQ=
github.com/tailscale/ssh v0.3.4-0.20220313013419-be8b7add4057/go.mod h1:LC21Rp6xYOAc7NEeMhYN0xTXUB74MZAN60GRPegZLkg=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
@@ -1111,17 +1236,26 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tomarrell/wrapcheck v0.0.0-20200807122107-df9e8bcb914d/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0=
github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756 h1:zV5mu0ESwb+WnzqVaW2z1DdbAP0S46UtjY8DHQupQP4=
github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756/go.mod h1:yiFB6fFoV7saXirUGfuK+cPtUh4NX/Hf5y2WC2lehu0=
github.com/tomarrell/wrapcheck/v2 v2.4.0 h1:mU4H9KsqqPZUALOUbVOpjy8qNQbWLoLI9fV68/1tq30=
github.com/tomarrell/wrapcheck/v2 v2.4.0/go.mod h1:68bQ/eJg55BROaRTbMjC7vuhL2OgfoG8bLp9ZyoBfyY=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa h1:RC4maTWLKKwb7p1cnoygsbKIgNlJqSYBeAFON3Ar8As=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tommy-muehle/go-mnd/v2 v2.4.0 h1:1t0f8Uiaq+fqKteUR4N9Umr6E99R+lDnLnq7PwX2PPE=
github.com/tommy-muehle/go-mnd/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA=
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
github.com/twitchtv/twirp v5.8.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/u-root/iscsinl v0.1.1-0.20210528121423-84c32645822a/go.mod h1:RWIgJWqm9/0gjBZ0Hl8iR6MVGzZ+yAda2uqqLmetE2I=
github.com/u-root/u-root v0.8.0 h1:jqP7uPC2+0eRszYTrmdZ6UDyO1Dbuy0rpMo+BnPZ9cY=
github.com/u-root/u-root v0.8.0/go.mod h1:But1FHzS4Ua4ywx6kZOaRzZTucUKIDKOPOLEKOckQ68=
github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7 h1:XMAtQHwKjWHIRwg+8Nj/rzUomQY1q6cM3ncA0wP8GU4=
github.com/u-root/uio v0.0.0-20210528151154-e40b768296a7/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@@ -1129,6 +1263,7 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA=
@@ -1137,6 +1272,7 @@ github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFO
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/uudashr/gocognit v1.0.5 h1:rrSex7oHr3/pPLQ0xoWq108XMU8s678FJcQ+aSfOHa4=
github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA=
@@ -1148,11 +1284,16 @@ github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/V
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6 h1:167a2omrzz+nN9Of6lN/0yOB9itzw+IOioRThNZ30jA=
github.com/vishvananda/netlink v1.1.1-0.20211101163509-b10eb8fe5cf6/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vtolstov/go-ioctl v0.0.0-20151206205506-6be9cced4810/go.mod h1:dF0BBJ2YrV1+2eAIyEI+KeSidgA6HqoIP1u5XTlMq/o=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
@@ -1174,9 +1315,11 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/ziutek/telnet v0.0.0-20180329124119-c3b780dc415b/go.mod h1:IZpXDfkJ6tWD3PhBK5YzgQT+xJWh7OsdwiG8hA2MkO4=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
@@ -1215,6 +1358,7 @@ golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -1222,8 +1366,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
@@ -1236,8 +1382,10 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000 h1:SL+8VVnkqyshUSz5iNnXtrBQzvFF2SkROm6t5RczFAE=
golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1249,7 +1397,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1281,6 +1432,9 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1297,6 +1451,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1306,6 +1461,7 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1345,10 +1501,13 @@ golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c h1:7SfqwP5fxEtl/P02w5IhKc86ziJ+A25yFrkVgoy2FT8=
golang.org/x/net v0.0.0-20211205041911-012df41ee64c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1366,6 +1525,7 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1381,6 +1541,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1390,6 +1551,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1402,8 +1564,10 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1412,13 +1576,17 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1440,6 +1608,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1480,24 +1649,33 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211002104244-808efd93c36d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1518,6 +1696,7 @@ golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1535,6 +1714,7 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1628,6 +1808,8 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8-0.20211102182255-bb4add04ddef/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1638,6 +1820,8 @@ golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+D
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45 h1:mEVhdMPTuebD9IUXOUB5Q2sjZpcmzkahHWd6DrGpLHA=
golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45/go.mod h1:evxZIqfCetExY5piKXGAxJYwvXWkps9zTCkWpkoGFxw=
golang.zx2c4.com/wireguard v0.0.0-20220317000134-95b48cdb3961 h1:oIXcKhP1Ge6cRqdpQuldl0hf4mjIsNaXojabghlHuTs=
golang.zx2c4.com/wireguard v0.0.0-20220317000134-95b48cdb3961/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
golang.zx2c4.com/wireguard/windows v0.4.10 h1:HmjzJnb+G4NCdX+sfjsQlsxGPuYaThxRbZUZFLyR0/s=
golang.zx2c4.com/wireguard/windows v0.4.10/go.mod h1:v7w/8FC48tTBm1IzScDVPEEb0/GjLta+T0ybpP9UWRg=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -1693,6 +1877,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
@@ -1730,6 +1915,7 @@ google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxH
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
@@ -1738,6 +1924,7 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
@@ -1764,6 +1951,7 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0-dev.0.20211020220737-f00baa6c3c84/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1791,6 +1979,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -1817,6 +2006,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gvisor.dev/gvisor v0.0.0-20220126021142-d8aa030b2591 h1:acuXPUADpJMtawdLCUje9xKlQN/8utegCB/Hr/ZgEuY=
gvisor.dev/gvisor v0.0.0-20220126021142-d8aa030b2591/go.mod h1:vmN0Pug/s8TJmpnt30DvrEfZ5vDl52psGLU04tFuK2U=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1830,6 +2020,8 @@ honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzE
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2 h1:utiSabORbG/JeX7MlmKMdmsjwom2+v8zmdb6SoBe4UY=
honnef.co/go/tools v0.3.0-0.dev.0.20220306074811-23e1086441d2/go.mod h1:dZI0HmIvwDMW8owtLBJxTHoeX48yuF5p5pDy3y73jGU=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
@@ -1840,6 +2032,16 @@ inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1D
inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=
inet.af/wf v0.0.0-20211204062712-86aaea0a7310 h1:0jKHTf+W75kYRyg5bto1UT+r18QmAz2u/5pAs/fx4zo=
inet.af/wf v0.0.0-20211204062712-86aaea0a7310/go.mod h1:ViGMZRA6+RA318D7GCncrjv5gHUrPYrNDejjU12tikA=
k8s.io/api v0.16.13/go.mod h1:QWu8UWSTiuQZMMeYjwLs6ILu5O74qKSJ0c+4vrchDxs=
k8s.io/apimachinery v0.16.13/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
k8s.io/apimachinery v0.16.14-rc.0/go.mod h1:4HMHS3mDHtVttspuuhrJ1GGr/0S9B6iWYWZ57KnnZqQ=
k8s.io/client-go v0.16.13/go.mod h1:UKvVT4cajC2iN7DCjLgT0KVY/cbY6DGdUCyRiIfws5M=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200410163147-594e756bea31/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws=
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
@@ -1855,10 +2057,13 @@ mvdan.cc/unparam v0.0.0-20211002134041-24922b6997ca h1:xzXXnoG5a3NUnKAcVMpE2cs3+
mvdan.cc/unparam v0.0.0-20211002134041-24922b6997ca/go.mod h1:Mb96j26qXgU/+SOj6MSgC36X30UgAlRYaxckYuYyEmo=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
pack.ag/tftp v1.0.1-0.20181129014014-07909dfbde3c/go.mod h1:N1Pyo5YG+K90XHoR2vfLPhpRuE8ziqbgMn/r/SghZas=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
software.sslmate.com/src/go-pkcs12 v0.0.0-20180114231543-2291e8f0f237/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
src.elv.sh v0.16.3/go.mod h1:WxJAMoN8uQcg1ZwRvtjmbYAo6uKeJ8F7b25etVZ743w=

View File

@@ -1 +1 @@
tailscale.go1.17
tailscale.go1.18

View File

@@ -1 +1 @@
25fe91a25c9630a50138a135105af19ae7c7c3e7
68c97fb924bbcacd951dc01b292c679aaeff5802

View File

@@ -64,6 +64,9 @@ const (
// SysDNSOS is the name of the net/dns OSConfigurator subsystem.
SysDNSOS = Subsystem("dns-os")
// SysDNSManager is the name of the net/dns manager subsystem.
SysDNSManager = Subsystem("dns-manager")
// SysNetworkCategory is the name of the subsystem that sets
// the Windows network adapter's "category" (public, private, domain).
// If it's unhealthy, the Windows firewall rules won't match.
@@ -110,6 +113,10 @@ func DNSHealth() error { return get(SysDNS) }
// SetDNSOSHealth sets the state of the net/dns.OSConfigurator
func SetDNSOSHealth(err error) { set(SysDNSOS, err) }
// SetDNSManagerHealth sets the state of the Linux net/dns manager's
// discovery of the /etc/resolv.conf situation.
func SetDNSManagerHealth(err error) { set(SysDNSManager, err) }
// DNSOSHealth returns the net/dns.OSConfigurator error state.
func DNSOSHealth() error { return get(SysDNSOS) }

View File

@@ -10,7 +10,6 @@ import (
"bufio"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"sync/atomic"
@@ -32,13 +31,17 @@ func New() *tailcfg.Hostinfo {
Hostname: hostname,
OS: version.OS(),
OSVersion: GetOSVersion(),
Package: packageType(),
Package: packageTypeCached(),
GoArch: runtime.GOARCH,
DeviceModel: deviceModel(),
}
}
var osVersion func() string // non-nil on some platforms
// non-nil on some platforms
var (
osVersion func() string
packageType func() string
)
// GetOSVersion returns the OSVersion of current host if available.
func GetOSVersion() string {
@@ -51,28 +54,18 @@ func GetOSVersion() string {
return ""
}
func packageType() string {
func packageTypeCached() string {
if v, _ := packagingType.Load().(string); v != "" {
return v
}
switch runtime.GOOS {
case "windows":
if _, err := os.Stat(`C:\ProgramData\chocolatey\lib\tailscale`); err == nil {
return "choco"
}
case "darwin":
// Using tailscaled or IPNExtension?
exe, _ := os.Executable()
return filepath.Base(exe)
case "linux":
// Report whether this is in a snap.
// See https://snapcraft.io/docs/environment-variables
// We just look at two somewhat arbitrarily.
if os.Getenv("SNAP_NAME") != "" && os.Getenv("SNAP") != "" {
return "snap"
}
if packageType == nil {
return ""
}
return ""
v := packageType()
if v != "" {
SetPackage(v)
}
return v
}
// EnvType represents a known environment type.

View File

@@ -0,0 +1,23 @@
// 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 darwin
// +build darwin
package hostinfo
import (
"os"
"path/filepath"
)
func init() {
packageType = packageTypeDarwin
}
func packageTypeDarwin() string {
// Using tailscaled or IPNExtension?
exe, _ := os.Executable()
return filepath.Base(exe)
}

View File

@@ -21,6 +21,7 @@ import (
func init() {
osVersion = osVersionLinux
packageType = packageTypeLinux
if v := linuxDeviceModel(); v != "" {
SetDeviceModel(v)
@@ -112,6 +113,18 @@ func osVersionLinux() string {
return fmt.Sprintf("Synology %s%s", m["productversion"], attr)
case distro.OpenWrt:
return fmt.Sprintf("OpenWrt %s%s", m["DISTRIB_RELEASE"], attr)
case distro.Gokrazy:
return fmt.Sprintf("Gokrazy%s", attr)
}
return fmt.Sprintf("Other%s", attr)
}
func packageTypeLinux() string {
// Report whether this is in a snap.
// See https://snapcraft.io/docs/environment-variables
// We just look at two somewhat arbitrarily.
if os.Getenv("SNAP_NAME") != "" && os.Getenv("SNAP") != "" {
return "snap"
}
return ""
}

View File

@@ -6,14 +6,18 @@ package hostinfo
import (
"fmt"
"os"
"path/filepath"
"sync/atomic"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"tailscale.com/util/winutil"
)
func init() {
osVersion = osVersionWindows
packageType = packageTypeWindows
}
var winVerCache atomic.Value // of string
@@ -56,3 +60,25 @@ func getUBR() (uint32, error) {
return uint32(val), nil
}
func packageTypeWindows() string {
if _, err := os.Stat(`C:\ProgramData\chocolatey\lib\tailscale`); err == nil {
return "choco"
}
if msiSentinel := winutil.GetRegInteger("MSI", 0); msiSentinel == 1 {
return "msi"
}
exe, err := os.Executable()
if err != nil {
return ""
}
dir := filepath.Dir(exe)
nsisUninstaller := filepath.Join(dir, "Uninstall-Tailscale.exe")
_, err = os.Stat(nsisUninstaller)
if err == nil {
return "nsis"
}
// Atypical. Not worth trying to detect. Likely open
// source tailscaled or a developer running by hand.
return ""
}

View File

@@ -12,6 +12,7 @@ import (
"io"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"os/user"
@@ -48,6 +49,7 @@ import (
"tailscale.com/types/netmap"
"tailscale.com/types/persist"
"tailscale.com/types/preftype"
"tailscale.com/types/views"
"tailscale.com/util/deephash"
"tailscale.com/util/dnsname"
"tailscale.com/util/multierr"
@@ -136,6 +138,7 @@ type LocalBackend struct {
prevIfState *interfaces.State
peerAPIServer *peerAPIServer // or nil
peerAPIListeners []*peerAPIListener
loginFlags controlclient.LoginFlags
incomingFiles map[*incomingFile]bool
// directFileRoot, if non-empty, means to write received files
// directly to this directory, without staging them in an
@@ -166,13 +169,13 @@ type clientGen func(controlclient.Options) (controlclient.Client, error)
// but is not actually running.
//
// If dialer is nil, a new one is made.
func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, dialer *tsdial.Dialer, e wgengine.Engine) (*LocalBackend, error) {
func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, dialer *tsdial.Dialer, e wgengine.Engine, loginFlags controlclient.LoginFlags) (*LocalBackend, error) {
if e == nil {
panic("ipn.NewLocalBackend: engine must not be nil")
}
hi := hostinfo.New()
logf("Host: %s/%s, %s", hi.OS, hi.GoArch, hi.OSVersion)
logf.JSON(1, "Hostinfo", hi)
envknob.LogCurrent(logf)
if dialer == nil {
dialer = new(tsdial.Dialer)
@@ -199,6 +202,7 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, diale
state: ipn.NoState,
portpoll: portpoll,
gotPortPollRes: make(chan struct{}),
loginFlags: loginFlags,
}
// Default filter blocks everything and logs nothing, until Start() is called.
@@ -383,9 +387,14 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
}
}
if b.netMap != nil {
s.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...)
s.TailnetName = b.netMap.Domain
s.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
if s.CurrentTailnet == nil {
s.CurrentTailnet = &ipnstate.TailnetStatus{}
}
s.CurrentTailnet.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
s.CurrentTailnet.MagicDNSEnabled = b.netMap.DNS.Proxied
s.CurrentTailnet.Name = b.netMap.Domain
}
})
sb.MutateSelfStatus(func(ss *ipnstate.PeerStatus) {
@@ -436,19 +445,31 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
exitNodeOption := tsaddr.PrefixesContainsFunc(p.AllowedIPs, func(r netaddr.IPPrefix) bool {
return r.Bits() == 0
})
var tags *views.Slice[string]
var primaryRoutes *views.IPPrefixSlice
if p.Tags != nil {
v := views.SliceOf(p.Tags)
tags = &v
}
if p.PrimaryRoutes != nil {
v := views.IPPrefixSliceOf(p.PrimaryRoutes)
primaryRoutes = &v
}
sb.AddPeer(p.Key, &ipnstate.PeerStatus{
InNetworkMap: true,
ID: p.StableID,
UserID: p.User,
TailscaleIPs: tailscaleIPs,
HostName: p.Hostinfo.Hostname,
Tags: tags,
PrimaryRoutes: primaryRoutes,
HostName: p.Hostinfo.Hostname(),
DNSName: p.Name,
OS: p.Hostinfo.OS,
OS: p.Hostinfo.OS(),
KeepAlive: p.KeepAlive,
Created: p.Created,
LastSeen: lastSeen,
Online: p.Online != nil && *p.Online,
ShareeNode: p.Hostinfo.ShareeNode,
ShareeNode: p.Hostinfo.ShareeNode(),
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
ExitNodeOption: exitNodeOption,
})
@@ -889,7 +910,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
if b.inServerMode || runtime.GOOS == "windows" {
b.logf("Start: serverMode=%v", b.inServerMode)
}
applyPrefsToHostinfo(hostinfo, b.prefs)
b.applyPrefsToHostinfo(hostinfo, b.prefs)
b.setNetMapLocked(nil)
persistv := b.prefs.Persist
@@ -1523,23 +1544,16 @@ func (b *LocalBackend) loadStateLocked(key ipn.StateKey, prefs *ipn.Prefs) (err
return fmt.Errorf("PrefsFromBytes: %v", err)
}
// On mobile platforms, ignore any old stored preferences for
// https://login.tailscale.com as the control server that
// would override the new default of controlplane.tailscale.com.
// Ignore any old stored preferences for https://login.tailscale.com
// as the control server that would override the new default of
// controlplane.tailscale.com.
// This makes sure that mobile clients go through the new
// frontends where we're (2021-10-02) doing battery
// optimization work ahead of turning down the old backends.
// TODO(bradfitz): make this the default for all platforms
// later. But mobile is a relatively small chunk (compared to
// Linux, Windows, macOS) and moving mobile early for battery
// gains is nice.
switch runtime.GOOS {
case "android", "ios":
if b.prefs != nil && b.prefs.ControlURL != "" &&
b.prefs.ControlURL != ipn.DefaultControlURL &&
ipn.IsLoginServerSynonym(b.prefs.ControlURL) {
b.prefs.ControlURL = ""
}
if b.prefs != nil && b.prefs.ControlURL != "" &&
b.prefs.ControlURL != ipn.DefaultControlURL &&
ipn.IsLoginServerSynonym(b.prefs.ControlURL) {
b.prefs.ControlURL = ""
}
b.logf("using backend prefs for %q: %s", key, b.prefs.Pretty())
@@ -1564,13 +1578,14 @@ func (b *LocalBackend) InServerMode() bool {
}
// Login implements Backend.
// As of 2022-02-17, this is only exists for tests.
func (b *LocalBackend) Login(token *tailcfg.Oauth2Token) {
b.mu.Lock()
b.assertClientLocked()
cc := b.cc
b.mu.Unlock()
cc.Login(token, controlclient.LoginInteractive)
cc.Login(token, b.loginFlags|controlclient.LoginInteractive)
}
// StartLoginInteractive implements Backend. It requests a new
@@ -1589,15 +1604,7 @@ func (b *LocalBackend) StartLoginInteractive() {
if url != "" {
b.popBrowserAuthNow()
} else {
flags := controlclient.LoginInteractive
if runtime.GOOS == "js" {
// The js/wasm client has no state storage so for now
// treat all interactive logins as ephemeral.
// TODO(bradfitz): if we start using browser LocalStorage
// or something, then rethink this.
flags |= controlclient.LoginEphemeral
}
cc.Login(nil, flags)
cc.Login(nil, b.loginFlags|controlclient.LoginInteractive)
}
}
@@ -1734,7 +1741,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
oldHi := b.hostinfo
newHi := oldHi.Clone()
applyPrefsToHostinfo(newHi, newp)
b.applyPrefsToHostinfo(newHi, newp)
b.hostinfo = newHi
hostInfoChanged := !oldHi.Equal(newHi)
userID := b.userID
@@ -2439,13 +2446,23 @@ func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
return ret
}
func applyPrefsToHostinfo(hi *tailcfg.Hostinfo, prefs *ipn.Prefs) {
// Warning: b.mu might be held. Currently (2022-02-17) both callers hold it.
func (b *LocalBackend) applyPrefsToHostinfo(hi *tailcfg.Hostinfo, prefs *ipn.Prefs) {
if h := prefs.Hostname; h != "" {
hi.Hostname = h
}
hi.RoutableIPs = append(prefs.AdvertiseRoutes[:0:0], prefs.AdvertiseRoutes...)
hi.RequestTags = append(prefs.AdvertiseTags[:0:0], prefs.AdvertiseTags...)
hi.ShieldsUp = prefs.ShieldsUp
var sshHostKeys []string
if prefs.RunSSH {
// TODO(bradfitz): this is called with b.mu held. Not ideal.
// If the filesystem gets wedged or something we could block for
// a long time. But probably fine.
sshHostKeys = b.getSSHHostKeyPublicStrings()
}
hi.SSH_HostKeys = sshHostKeys
}
// enterState transitions the backend into newState, updating internal
@@ -2907,7 +2924,7 @@ func (b *LocalBackend) FileTargets() ([]*apitype.FileTarget, error) {
// friendly options to get HTTPS certs.
func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error {
req := &tailcfg.SetDNSRequest{
Version: 1,
Version: 1, // TODO(bradfitz,maisem): use tailcfg.CurrentCapabilityVersion when using the Noise transport
Type: "TXT",
Name: name,
Value: value,
@@ -2951,9 +2968,10 @@ func (b *LocalBackend) registerIncomingFile(inf *incomingFile, active bool) {
// It returns the empty string if the peer doesn't support the peerapi
// or there's no matching address family based on the netmap's own addresses.
func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
if nm == nil || peer == nil {
if nm == nil || peer == nil || !peer.Hostinfo.Valid() {
return ""
}
var have4, have6 bool
for _, a := range nm.Addresses {
if !a.IsSingleIP() {
@@ -2967,7 +2985,9 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
}
}
var p4, p6 uint16
for _, s := range peer.Hostinfo.Services {
svcs := peer.Hostinfo.Services()
for i, n := 0, svcs.Len(); i < n; i++ {
s := svcs.At(i)
switch s.Proto {
case tailcfg.PeerAPI4:
p4 = s.Port
@@ -3161,6 +3181,15 @@ func (b *LocalBackend) allowExitNodeDNSProxyToServeName(name string) bool {
return true
}
// SetExpiry updates the expiry of the current node key to t, as long as it's
// only sooner than the old expiry.
//
// If t is in the past, the key is expired immediately.
// If t is after the current expiry, an error is returned.
func (b *LocalBackend) SetExpirySooner(ctx context.Context, expiry time.Time) error {
return b.cc.SetExpirySooner(ctx, expiry)
}
// exitNodeCanProxyDNS reports the DoH base URL ("http://foo/dns-query") without query parameters
// to exitNodeID's DoH service, if available.
//
@@ -3173,7 +3202,9 @@ func exitNodeCanProxyDNS(nm *netmap.NetworkMap, exitNodeID tailcfg.StableNodeID)
if p.StableID != exitNodeID {
continue
}
for _, s := range p.Hostinfo.Services {
services := p.Hostinfo.Services()
for i, n := 0, services.Len(); i < n; i++ {
s := services.At(i)
if s.Proto == tailcfg.PeerAPIDNS && s.Port >= 1 {
return peerAPIBase(nm, p) + "/dns-query", true
}
@@ -3211,3 +3242,50 @@ func (b *LocalBackend) magicConn() (*magicsock.Conn, error) {
}
return mc, nil
}
// DoNoiseRequest sends a request to URL over the the control plane
// Noise connection.
func (b *LocalBackend) DoNoiseRequest(req *http.Request) (*http.Response, error) {
b.mu.Lock()
cc := b.cc
b.mu.Unlock()
if cc == nil {
return nil, errors.New("no client")
}
return cc.DoNoiseRequest(req)
}
// ProxyAPIRequestOverNoise sends Tailscale API request r over the
// Noise channel, authenticated as the current node+machine key, to
// the control plane and copies its response back to w.
func (b *LocalBackend) ProxyAPIRequestOverNoise(w http.ResponseWriter, r *http.Request) {
var nodePub key.NodePublic
b.mu.Lock()
if nm := b.netMap; nm != nil {
nodePub = nm.NodeKey
}
b.mu.Unlock()
if nodePub.IsZero() {
http.Error(w, "no node public key", http.StatusBadGateway)
return
}
outR := r.Clone(r.Context())
outR.RequestURI = ""
outR.URL.Scheme = "https"
outR.URL.Host = "unused"
outR.SetBasicAuth(url.QueryEscape(nodePub.String()), "")
res, err := b.DoNoiseRequest(outR)
if err != nil {
http.Error(w, "failed to make backend noise request: "+err.Error(), http.StatusBadGateway)
return
}
for k, vv := range res.Header {
for _, v := range vv {
w.Header().Add(k, v)
}
}
w.WriteHeader(res.StatusCode)
io.Copy(w, res.Body)
}

View File

@@ -14,6 +14,7 @@ import (
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/net/interfaces"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
@@ -346,12 +347,12 @@ func TestPeerAPIBase(t *testing.T) {
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: tailcfg.Hostinfo{
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
{Proto: "peerapi4", Port: 444},
{Proto: "peerapi6", Port: 666},
},
},
}).View(),
},
want: "http://100.64.1.2:444",
},
@@ -367,12 +368,12 @@ func TestPeerAPIBase(t *testing.T) {
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: tailcfg.Hostinfo{
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
{Proto: "peerapi4", Port: 444},
{Proto: "peerapi6", Port: 666},
},
},
}).View(),
},
want: "http://[fe70::2]:666",
},
@@ -389,11 +390,11 @@ func TestPeerAPIBase(t *testing.T) {
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: tailcfg.Hostinfo{
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
{Proto: "peerapi4", Port: 444},
},
},
}).View(),
},
want: "http://100.64.1.2:444",
},
@@ -410,11 +411,11 @@ func TestPeerAPIBase(t *testing.T) {
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: tailcfg.Hostinfo{
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
{Proto: "peerapi6", Port: 666},
},
},
}).View(),
},
want: "http://[fe70::2]:666",
},
@@ -457,13 +458,13 @@ func TestLazyMachineKeyGeneration(t *testing.T) {
panicOnMachineKeyGeneration = true
var logf logger.Logf = logger.Discard
store := new(ipn.MemoryStore)
store := new(mem.Store)
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
if err != nil {
t.Fatalf("NewFakeUserspaceEngine: %v", err)
}
t.Cleanup(eng.Close)
lb, err := NewLocalBackend(logf, "logid", store, nil, eng)
lb, err := NewLocalBackend(logf, "logid", store, nil, eng, 0)
if err != nil {
t.Fatalf("NewLocalBackend: %v", err)
}

View File

@@ -11,6 +11,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/ipn/store/mem"
"tailscale.com/logtail"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
@@ -47,14 +48,14 @@ func TestLocalLogLines(t *testing.T) {
idA := logid(0xaa)
// set up a LocalBackend, super bare bones. No functional data.
store := &ipn.MemoryStore{}
store := new(mem.Store)
e, err := wgengine.NewFakeUserspaceEngine(logf, 0)
if err != nil {
t.Fatal(err)
}
t.Cleanup(e.Close)
lb, err := NewLocalBackend(logf, idA.String(), store, nil, e)
lb, err := NewLocalBackend(logf, idA.String(), store, nil, e, 0)
if err != nil {
t.Fatal(err)
}

View File

@@ -411,7 +411,7 @@ func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net
}
// Make a best effort to pick a deterministic port number for
// the ip The lower three bytes are the same for IPv4 and IPv6
// the ip. The lower three bytes are the same for IPv4 and IPv6
// Tailscale addresses (at least currently), so we'll usually
// get the same port number on both address families for
// dev/debugging purposes, which is nice. But it's not so
@@ -507,7 +507,7 @@ func (pln *peerAPIListener) ServeConn(src netaddr.IPPort, c net.Conn) {
if addH2C != nil {
addH2C(httpServer)
}
go httpServer.Serve(netutil.NewOneConnListenerFrom(c, pln.ln))
go httpServer.Serve(netutil.NewOneConnListener(c, pln.ln.Addr()))
}
// peerAPIHandler serves the Peer API for a source specific client.
@@ -519,7 +519,7 @@ type peerAPIHandler struct {
peerUser tailcfg.UserProfile // profile of peerNode
}
func (h *peerAPIHandler) logf(format string, a ...interface{}) {
func (h *peerAPIHandler) logf(format string, a ...any) {
h.ps.b.logf("peerapi: "+format, a...)
}

142
ipn/ipnlocal/ssh.go Normal file
View File

@@ -0,0 +1,142 @@
// 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 linux || (darwin && !ios)
// +build linux darwin,!ios
package ipnlocal
import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"golang.org/x/crypto/ssh"
"tailscale.com/envknob"
)
var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
// keyTypes are the SSH key types that we either try to read from the
// system's OpenSSH keys or try to generate for ourselves when not
// running as root.
var keyTypes = []string{"rsa", "ecdsa", "ed25519"}
func (b *LocalBackend) GetSSH_HostKeys() (keys []ssh.Signer, err error) {
if os.Geteuid() == 0 {
keys, err = b.getSystemSSH_HostKeys()
if err != nil || len(keys) > 0 {
return keys, err
}
// Otherwise, perhaps they don't have OpenSSH etc installed.
// Generate our own keys...
}
return b.getTailscaleSSH_HostKeys()
}
func (b *LocalBackend) getTailscaleSSH_HostKeys() (keys []ssh.Signer, err error) {
root := b.TailscaleVarRoot()
if root == "" {
return nil, errors.New("no var root for ssh keys")
}
keyDir := filepath.Join(root, "ssh")
if err := os.MkdirAll(keyDir, 0700); err != nil {
return nil, err
}
for _, typ := range keyTypes {
hostKey, err := b.hostKeyFileOrCreate(keyDir, typ)
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil {
return nil, err
}
keys = append(keys, signer)
}
return keys, nil
}
var keyGenMu sync.Mutex
func (b *LocalBackend) hostKeyFileOrCreate(keyDir, typ string) ([]byte, error) {
keyGenMu.Lock()
defer keyGenMu.Unlock()
path := filepath.Join(keyDir, "ssh_host_"+typ+"_key")
v, err := ioutil.ReadFile(path)
if err == nil {
return v, nil
}
if !os.IsNotExist(err) {
return nil, err
}
var priv any
switch typ {
default:
return nil, fmt.Errorf("unsupported key type %q", typ)
case "ed25519":
_, priv, err = ed25519.GenerateKey(rand.Reader)
case "ecdsa":
// curve is arbitrary. We pick whatever will at
// least pacify clients as the actual encryption
// doesn't matter: it's all over WireGuard anyway.
curve := elliptic.P256()
priv, err = ecdsa.GenerateKey(curve, rand.Reader)
case "rsa":
// keySize is arbitrary. We pick whatever will at
// least pacify clients as the actual encryption
// doesn't matter: it's all over WireGuard anyway.
const keySize = 2048
priv, err = rsa.GenerateKey(rand.Reader, keySize)
}
if err != nil {
return nil, err
}
mk, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, err
}
pemGen := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: mk})
err = os.WriteFile(path, pemGen, 0700)
return pemGen, err
}
func (b *LocalBackend) getSystemSSH_HostKeys() (ret []ssh.Signer, err error) {
// TODO(bradfitz): cache this?
for _, typ := range keyTypes {
hostKey, err := ioutil.ReadFile("/etc/ssh/ssh_host_" + typ + "_key")
if os.IsNotExist(err) {
continue
}
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil {
return nil, err
}
ret = append(ret, signer)
}
return ret, nil
}
func (b *LocalBackend) getSSHHostKeyPublicStrings() (ret []string) {
signers, _ := b.GetSSH_HostKeys()
for _, signer := range signers {
ret = append(ret, strings.TrimSpace(string(ssh.MarshalAuthorizedKey(signer.PublicKey()))))
}
return ret
}

11
ipn/ipnlocal/ssh_stub.go Normal file
View File

@@ -0,0 +1,11 @@
// 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 ios || (!linux && !darwin)
package ipnlocal
func (b *LocalBackend) getSSHHostKeyPublicStrings() []string {
return nil
}

42
ipn/ipnlocal/ssh_test.go Normal file
View File

@@ -0,0 +1,42 @@
// 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 linux
// +build linux
package ipnlocal
import (
"reflect"
"testing"
)
func TestSSHKeyGen(t *testing.T) {
dir := t.TempDir()
lb := &LocalBackend{varRoot: dir}
keys, err := lb.getTailscaleSSH_HostKeys()
if err != nil {
t.Fatal(err)
}
got := map[string]bool{}
for _, k := range keys {
got[k.PublicKey().Type()] = true
}
want := map[string]bool{
"ssh-rsa": true,
"ecdsa-sha2-nistp256": true,
"ssh-ed25519": true,
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("keys = %v; want %v", got, want)
}
keys2, err := lb.getTailscaleSSH_HostKeys()
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(keys, keys2) {
t.Errorf("got different keys on second call")
}
}

View File

@@ -6,6 +6,7 @@ package ipnlocal
import (
"context"
"net/http"
"sync"
"testing"
"time"
@@ -14,6 +15,7 @@ import (
"tailscale.com/control/controlclient"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/types/empty"
@@ -105,7 +107,7 @@ func newMockControl(tb testing.TB) *mockControl {
}
}
func (cc *mockControl) logf(format string, args ...interface{}) {
func (cc *mockControl) logf(format string, args ...any) {
if cc.preventLog.Get() || cc.logfActual == nil {
return
}
@@ -222,6 +224,12 @@ func (cc *mockControl) Logout(ctx context.Context) error {
return nil
}
func (cc *mockControl) SetExpirySooner(context.Context, time.Time) error {
cc.logf("SetExpirySooner")
cc.called("SetExpirySooner")
return nil
}
func (cc *mockControl) SetPaused(paused bool) {
cc.logf("SetPaused=%v", paused)
if paused {
@@ -259,6 +267,10 @@ func (*mockControl) SetDNS(context.Context, *tailcfg.SetDNSRequest) error {
panic("unexpected SetDNS call")
}
func (*mockControl) DoNoiseRequest(*http.Request) (*http.Response, error) {
panic("unexpected DoNoiseRequest call")
}
// A very precise test of the sequence of function calls generated by
// ipnlocal.Local into its controlclient instance, and the events it
// produces upstream into the UI.
@@ -293,7 +305,7 @@ func TestStateMachine(t *testing.T) {
cc := newMockControl(t)
t.Cleanup(func() { cc.preventLog.Set(true) }) // hacky way to pacify issue 3020
b, err := NewLocalBackend(logf, "logid", store, nil, e)
b, err := NewLocalBackend(logf, "logid", store, nil, e, 0)
if err != nil {
t.Fatalf("NewLocalBackend: %v", err)
}
@@ -922,7 +934,7 @@ func TestStateMachine(t *testing.T) {
}
type testStateStorage struct {
mem ipn.MemoryStore
mem mem.Store
written syncs.AtomicBool
}
@@ -954,7 +966,7 @@ func TestWGEngineStatusRace(t *testing.T) {
eng, err := wgengine.NewFakeUserspaceEngine(logf, 0)
c.Assert(err, qt.IsNil)
t.Cleanup(eng.Close)
b, err := NewLocalBackend(logf, "logid", new(ipn.MemoryStore), nil, eng)
b, err := NewLocalBackend(logf, "logid", new(mem.Store), nil, eng, 0)
c.Assert(err, qt.IsNil)
cc := newMockControl(t)

View File

@@ -36,12 +36,10 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
"tailscale.com/ipn/localapi"
"tailscale.com/ipn/store/aws"
"tailscale.com/logtail/backoff"
"tailscale.com/net/netstat"
"tailscale.com/net/netutil"
"tailscale.com/net/tsdial"
"tailscale.com/paths"
"tailscale.com/safesocket"
"tailscale.com/smallzstd"
"tailscale.com/types/logger"
@@ -85,6 +83,9 @@ type Options struct {
// the actual definition of "disconnect" is when the
// connection count transitions from 1 to 0.
SurviveDisconnects bool
// LoginFlags specifies the LoginFlags to pass to the client.
LoginFlags controlclient.LoginFlags
}
// Server is an IPN backend and its set of 0 or more active localhost
@@ -309,7 +310,7 @@ func (s *Server) serveConn(ctx context.Context, c net.Conn, logf logger.Logf) {
ErrorLog: logger.StdLogger(logf),
Handler: s.localhostHandler(ci),
}
httpServer.Serve(netutil.NewOneConnListener(&protoSwitchConn{s: s, br: br, Conn: c}))
httpServer.Serve(netutil.NewOneConnListener(&protoSwitchConn{s: s, br: br, Conn: c}, nil))
return
}
@@ -455,6 +456,29 @@ func (s *Server) localAPIPermissions(ci connIdentity) (read, write bool) {
return false, false
}
// userIDFromString maps from either a numeric user id in string form
// ("998") or username ("caddy") to its string userid ("998").
// It returns the empty string on error.
func userIDFromString(v string) string {
if v == "" || isAllDigit(v) {
return v
}
u, err := user.Lookup(v)
if err != nil {
return ""
}
return u.Uid
}
func isAllDigit(s string) bool {
for i := 0; i < len(s); i++ {
if b := s[i]; b < '0' || b > '9' {
return false
}
}
return true
}
// connCanFetchCerts reports whether ci is allowed to fetch HTTPS
// certs from this server when it wouldn't otherwise be able to.
//
@@ -468,7 +492,7 @@ func (s *Server) localAPIPermissions(ci connIdentity) (read, write bool) {
func (s *Server) connCanFetchCerts(ci connIdentity) bool {
if ci.IsUnixSock && ci.Creds != nil {
connUID, ok := ci.Creds.UserID()
if ok && connUID == envknob.String("TS_PERMIT_CERT_UID") {
if ok && connUID == userIDFromString(envknob.String("TS_PERMIT_CERT_UID")) {
return true
}
}
@@ -631,70 +655,6 @@ func (s *Server) writeToClients(n ipn.Notify) {
}
}
// tryWindowsAppDataMigration attempts to copy the Windows state file
// from its old location to the new location. (Issue 2856)
//
// Tailscale 1.14 and before stored state under %LocalAppData%
// (usually "C:\WINDOWS\system32\config\systemprofile\AppData\Local"
// when tailscaled.exe is running as a non-user system service).
// However it is frequently cleared for almost any reason: Windows
// updates, System Restore, even various System Cleaner utilities.
//
// Returns a string of the path to use for the state file.
// This will be a fallback %LocalAppData% path if migration fails,
// a %ProgramData% path otherwise.
func tryWindowsAppDataMigration(logf logger.Logf, path string) string {
if path != paths.DefaultTailscaledStateFile() {
// If they're specifying a non-default path, just trust that they know
// what they are doing.
return path
}
oldFile := paths.LegacyStateFilePath()
return paths.TryConfigFileMigration(logf, oldFile, path)
}
// StateStore returns a StateStore from path.
//
// The path should be an absolute path to a file.
//
// Special cases:
//
// * empty string means to use an in-memory store
// * if the string begins with "kube:", the suffix
// is a Kubernetes secret name
// * if the string begins with "arn:", the value is
// an AWS ARN for an SSM.
func StateStore(path string, logf logger.Logf) (ipn.StateStore, error) {
if path == "" {
return &ipn.MemoryStore{}, nil
}
const kubePrefix = "kube:"
const arnPrefix = "arn:"
switch {
case strings.HasPrefix(path, kubePrefix):
secretName := strings.TrimPrefix(path, kubePrefix)
store, err := ipn.NewKubeStore(secretName)
if err != nil {
return nil, fmt.Errorf("ipn.NewKubeStore(%q): %v", secretName, err)
}
return store, nil
case strings.HasPrefix(path, arnPrefix):
store, err := aws.NewStore(path)
if err != nil {
return nil, fmt.Errorf("aws.NewStore(%q): %v", path, err)
}
return store, nil
}
if runtime.GOOS == "windows" {
path = tryWindowsAppDataMigration(logf, path)
}
store, err := ipn.NewFileStore(path)
if err != nil {
return nil, fmt.Errorf("ipn.NewFileStore(%q): %v", path, err)
}
return store, nil
}
// Run runs a Tailscale backend service.
// The getEngine func is called repeatedly, once per connection, until it returns an engine successfully.
//
@@ -797,7 +757,7 @@ func Run(ctx context.Context, logf logger.Logf, ln net.Listener, store ipn.State
//
// To start it, use the Server.Run method.
func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engine, dialer *tsdial.Dialer, serverModeUser *user.User, opts Options) (*Server, error) {
b, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng)
b, err := ipnlocal.NewLocalBackend(logf, logid, store, dialer, eng, opts.LoginFlags)
if err != nil {
return nil, fmt.Errorf("NewLocalBackend: %v", err)
}
@@ -1089,6 +1049,10 @@ func (s *Server) localhostHandler(ci connIdentity) http.Handler {
lah.ServeHTTP(w, r)
return
}
if strings.HasPrefix(r.URL.Path, "/api/") {
s.b.ProxyAPIRequestOverNoise(w, r)
return
}
if ci.NotWindows {
io.WriteString(w, "<html><title>Tailscale</title><body><h1>Tailscale</h1>This is the local Tailscale daemon.")
return
@@ -1219,7 +1183,7 @@ func loadExtraEnv() (env []string, err error) {
if line == "" || line[0] == '#' {
continue
}
k, v, ok := stringsCut(line, "=")
k, v, ok := strings.Cut(line, "=")
if !ok || k == "" {
continue
}
@@ -1236,12 +1200,3 @@ func loadExtraEnv() (env []string, err error) {
}
return env, nil
}
// stringsCut is Go 1.18's strings.Cut.
// TODO(bradfitz): delete this when we depend on Go 1.18.
func stringsCut(s, sep string) (before, after string, found bool) {
if i := strings.Index(s, sep); i >= 0 {
return s[:i], s[i+len(sep):], true
}
return s, "", false
}

View File

@@ -13,6 +13,7 @@ import (
"tailscale.com/ipn"
"tailscale.com/ipn/ipnserver"
"tailscale.com/ipn/store/mem"
"tailscale.com/net/tsdial"
"tailscale.com/safesocket"
"tailscale.com/wgengine"
@@ -27,7 +28,7 @@ func TestRunMultipleAccepts(t *testing.T) {
td := t.TempDir()
socketPath := filepath.Join(td, "tailscale.sock")
logf := func(format string, args ...interface{}) {
logf := func(format string, args ...any) {
format = strings.TrimRight(format, "\n")
println(fmt.Sprintf(format, args...))
t.Logf(format, args...)
@@ -51,7 +52,7 @@ func TestRunMultipleAccepts(t *testing.T) {
}
}
logTriggerTestf := func(format string, args ...interface{}) {
logTriggerTestf := func(format string, args ...any) {
logf(format, args...)
if strings.HasPrefix(format, "Listening on ") {
connect()
@@ -66,7 +67,7 @@ func TestRunMultipleAccepts(t *testing.T) {
opts := ipnserver.Options{}
t.Logf("pre-Run")
store := new(ipn.MemoryStore)
store := new(mem.Store)
ln, _, err := safesocket.Listen(socketPath, 0)
if err != nil {

View File

@@ -20,6 +20,7 @@ import (
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/views"
"tailscale.com/util/dnsname"
)
@@ -33,10 +34,6 @@ type Status struct {
// "Starting", "Running".
BackendState string
// TailnetName is the name of the network that's currently in
// use.
TailnetName string
AuthURL string // current URL provided by control to authorize client
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
Self *PeerStatus
@@ -46,13 +43,15 @@ type Status struct {
// problems are detected)
Health []string
// MagicDNSSuffix is the network's MagicDNS suffix for nodes
// in the network such as "userfoo.tailscale.net".
// There are no surrounding dots.
// MagicDNSSuffix should be populated regardless of whether a domain
// has MagicDNS enabled.
// This field is the legacy name of CurrentTailnet.MagicDNSSuffix.
//
// Deprecated: use CurrentTailnet.MagicDNSSuffix instead.
MagicDNSSuffix string
// CurrentTailnet is information about the tailnet that the node
// is currently connected to. When not connected, this field is nil.
CurrentTailnet *TailnetStatus
// CertDomains are the set of DNS names for which the control
// plane server will assist with provisioning TLS
// certificates. See SetDNSRequest for dns-01 ACME challenges
@@ -64,6 +63,24 @@ type Status struct {
User map[tailcfg.UserID]tailcfg.UserProfile
}
// TailnetStatus is information about a Tailscale network ("tailnet").
type TailnetStatus struct {
// Name is the name of the network that's currently in use.
Name string
// MagicDNSSuffix is the network's MagicDNS suffix for nodes
// in the network such as "userfoo.tailscale.net".
// There are no surrounding dots.
// MagicDNSSuffix should be populated regardless of whether a domain
// has MagicDNS enabled.
MagicDNSSuffix string
// MagicDNSEnabled is whether or not the network has MagicDNS enabled.
// Note that the current device may still not support MagicDNS if
// `--accept-dns=false` was used.
MagicDNSEnabled bool
}
func (s *Status) Peers() []key.NodePublic {
kk := make([]key.NodePublic, 0, len(s.Peer))
for k := range s.Peer {
@@ -85,15 +102,23 @@ type PeerStatusLite struct {
}
type PeerStatus struct {
ID tailcfg.StableNodeID
PublicKey key.NodePublic
HostName string // HostInfo's Hostname (not a DNS name or necessarily unique)
DNSName string
OS string // HostInfo.OS
UserID tailcfg.UserID
ID tailcfg.StableNodeID
PublicKey key.NodePublic
HostName string // HostInfo's Hostname (not a DNS name or necessarily unique)
DNSName string
OS string // HostInfo.OS
UserID tailcfg.UserID
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
// Tags are the list of ACL tags applied to this node.
// See tailscale.com/tailcfg#Node.Tags for more information.
Tags *views.Slice[string] `json:",omitempty"`
// PrimaryRoutes are the routes this node is currently the primary
// subnet router for, as determined by the control plane. It does
// not include the IPs in TailscaleIPs.
PrimaryRoutes *views.IPPrefixSlice `json:",omitempty"`
// Endpoints:
Addrs []string
CurAddr string // one of Addrs, or unique if roaming
@@ -250,6 +275,12 @@ func (sb *StatusBuilder) AddPeer(peer key.NodePublic, st *PeerStatus) {
if v := st.TailscaleIPs; v != nil {
e.TailscaleIPs = v
}
if v := st.PrimaryRoutes; v != nil && !v.IsNil() {
e.PrimaryRoutes = v
}
if v := st.Tags; v != nil && !v.IsNil() {
e.Tags = v
}
if v := st.OS; v != "" {
e.OS = st.OS
}
@@ -311,7 +342,7 @@ type StatusUpdater interface {
}
func (st *Status) WriteHTML(w io.Writer) {
f := func(format string, args ...interface{}) { fmt.Fprintf(w, format, args...) }
f := func(format string, args ...any) { fmt.Fprintf(w, format, args...) }
f(`<!DOCTYPE html>
<html lang="en">

View File

@@ -85,7 +85,7 @@ func (h *Handler) serveCert(w http.ResponseWriter, r *http.Request) {
now := time.Now()
logf := logger.WithPrefix(h.logf, fmt.Sprintf("cert(%q): ", domain))
traceACME := func(v interface{}) {
traceACME := func(v any) {
if !acmeDebug {
return
}
@@ -164,7 +164,7 @@ func (h *Handler) getCertPEMCached(dir, domain string, now time.Time) (p *keyPai
return nil, false
}
func (h *Handler) getCertPEM(ctx context.Context, logf logger.Logf, traceACME func(interface{}), dir, domain string, now time.Time) (*keyPair, error) {
func (h *Handler) getCertPEM(ctx context.Context, logf logger.Logf, traceACME func(any), dir, domain string, now time.Time) (*keyPair, error) {
acmeMu.Lock()
defer acmeMu.Unlock()
@@ -239,6 +239,7 @@ func (h *Handler) getCertPEM(ctx context.Context, logf logger.Logf, traceACME fu
}
}
if !ok {
logf("starting SetDNS call...")
err = h.b.SetDNS(ctx, key, rec)
if err != nil {
return nil, fmt.Errorf("SetDNS %q => %q: %w", key, rec, err)
@@ -256,26 +257,18 @@ func (h *Handler) getCertPEM(ctx context.Context, logf logger.Logf, traceACME fu
}
}
wait0 := time.Now()
orderURI := order.URI
for {
order, err = ac.WaitOrder(ctx, orderURI)
if err == nil {
break
order, err = ac.WaitOrder(ctx, orderURI)
if err != nil {
if ctx.Err() != nil {
return nil, ctx.Err()
}
if oe, ok := err.(*acme.OrderError); ok && oe.Status == acme.StatusInvalid {
if time.Since(wait0) > 2*time.Minute {
return nil, errors.New("timeout waiting for order to not be invalid")
}
log.Printf("order invalid; waiting...")
select {
case <-time.After(5 * time.Second):
continue
case <-ctx.Done():
return nil, ctx.Err()
}
if oe, ok := err.(*acme.OrderError); ok {
logf("acme: WaitOrder: OrderError status %q", oe.Status)
} else {
logf("acme: WaitOrder error: %v", err)
}
return nil, fmt.Errorf("WaitOrder: %v", err)
return nil, err
}
traceACME(order)
@@ -296,10 +289,12 @@ func (h *Handler) getCertPEM(ctx context.Context, logf logger.Logf, traceACME fu
return nil, err
}
logf("requesting cert...")
der, _, err := ac.CreateOrderCert(ctx, order.FinalizeURL, csr, true)
if err != nil {
return nil, fmt.Errorf("CreateOrder: %v", err)
}
logf("got cert")
var certPEM bytes.Buffer
for _, b := range der {

View File

@@ -122,6 +122,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.serveMetrics(w, r)
case "/localapi/v0/debug":
h.serveDebug(w, r)
case "/localapi/v0/set-expiry-sooner":
h.serveSetExpirySooner(w, r)
case "/":
io.WriteString(w, "tailscaled\n")
default:
@@ -446,12 +448,12 @@ func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
}
upath := strings.TrimPrefix(r.URL.EscapedPath(), "/localapi/v0/file-put/")
slash := strings.Index(upath, "/")
if slash == -1 {
stableIDStr, filenameEscaped, ok := strings.Cut(upath, "/")
if !ok {
http.Error(w, "bogus URL", 400)
return
}
stableID, filenameEscaped := tailcfg.StableNodeID(upath[:slash]), upath[slash+1:]
stableID := tailcfg.StableNodeID(stableIDStr)
var ft *apitype.FileTarget
for _, x := range fts {
@@ -511,6 +513,35 @@ func (h *Handler) serveDERPMap(w http.ResponseWriter, r *http.Request) {
e.Encode(h.b.DERPMap())
}
// serveSetExpirySooner sets the expiry date on the current machine, specified
// by an `expiry` unix timestamp as POST or query param.
func (h *Handler) serveSetExpirySooner(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "POST required", http.StatusMethodNotAllowed)
return
}
var expiryTime time.Time
if v := r.FormValue("expiry"); v != "" {
expiryInt, err := strconv.ParseInt(v, 10, 64)
if err != nil {
http.Error(w, "can't parse expiry time, expects a unix timestamp", http.StatusBadRequest)
return
}
expiryTime = time.Unix(expiryInt, 0)
} else {
http.Error(w, "missing 'expiry' parameter, a unix timestamp", http.StatusBadRequest)
return
}
err := h.b.SetExpirySooner(r.Context(), expiryTime)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "text/plain")
io.WriteString(w, "done\n")
}
func defBool(a string, def bool) bool {
if a == "" {
return def
@@ -526,7 +557,7 @@ func defBool(a string, def bool) bool {
// (currently only a slice or a map) and makes sure it's non-nil for
// JSON serialization. (In particular, JavaScript clients usually want
// the field to be defined after they decode the JSON.)
func makeNonNil(ptr interface{}) {
func makeNonNil(ptr any) {
if ptr == nil {
panic("nil interface")
}

View File

@@ -85,10 +85,10 @@ func TestClientServer(t *testing.T) {
clientToServer := func(b []byte) {
bs.GotCommandMsg(context.TODO(), b)
}
slogf := func(fmt string, args ...interface{}) {
slogf := func(fmt string, args ...any) {
t.Logf("s: "+fmt, args...)
}
clogf := func(fmt string, args ...interface{}) {
clogf := func(fmt string, args ...any) {
t.Logf("c: "+fmt, args...)
}
bs = NewBackendServer(slogf, b, serverToClient)

View File

@@ -7,6 +7,7 @@ package ipn
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
@@ -18,9 +19,12 @@ import (
"inet.af/netaddr"
"tailscale.com/atomicfile"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/persist"
"tailscale.com/types/preftype"
"tailscale.com/util/dnsname"
)
//go:generate go run tailscale.com/cmd/cloner -type=Prefs -output=prefs_clone.go
@@ -30,9 +34,15 @@ import (
// The default control plane is the hosted version run by Tailscale.com.
const DefaultControlURL = "https://controlplane.tailscale.com"
var (
// ErrExitNodeIDAlreadySet is returned from (*Prefs).SetExitNodeIP when the
// Prefs.ExitNodeID field is already set.
ErrExitNodeIDAlreadySet = errors.New("cannot set ExitNodeIP when ExitNodeID is already set")
)
// IsLoginServerSynonym reports whether a URL is a drop-in replacement
// for the primary Tailscale login server.
func IsLoginServerSynonym(val interface{}) bool {
func IsLoginServerSynonym(val any) bool {
return val == "https://login.tailscale.com" || val == "https://controlplane.tailscale.com"
}
@@ -442,18 +452,7 @@ func (p *Prefs) AdvertisesExitNode() bool {
if p == nil {
return false
}
var v4, v6 bool
for _, r := range p.AdvertiseRoutes {
if r.Bits() != 0 {
continue
}
if r.IP().Is4() {
v4 = true
} else if r.IP().Is6() {
v6 = true
}
}
return v4 && v6
return tsaddr.ContainsExitRoutes(p.AdvertiseRoutes)
}
// SetAdvertiseExitNode mutates p (if non-nil) to add or remove the two
@@ -477,6 +476,109 @@ func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
netaddr.IPPrefixFrom(netaddr.IPv6Unspecified(), 0))
}
// peerWithTailscaleIP returns the peer in st with the provided
// Tailscale IP.
func peerWithTailscaleIP(st *ipnstate.Status, ip netaddr.IP) (ps *ipnstate.PeerStatus, ok bool) {
for _, ps := range st.Peer {
for _, ip2 := range ps.TailscaleIPs {
if ip == ip2 {
return ps, true
}
}
}
return nil, false
}
func isRemoteIP(st *ipnstate.Status, ip netaddr.IP) bool {
for _, selfIP := range st.TailscaleIPs {
if ip == selfIP {
return false
}
}
return true
}
// ClearExitNode sets the ExitNodeID and ExitNodeIP to their zero values.
func (p *Prefs) ClearExitNode() {
p.ExitNodeID = ""
p.ExitNodeIP = netaddr.IP{}
}
// ExitNodeLocalIPError is returned when the requested IP address for an exit
// node belongs to the local machine.
type ExitNodeLocalIPError struct {
hostOrIP string
}
func (e ExitNodeLocalIPError) Error() string {
return fmt.Sprintf("cannot use %s as an exit node as it is a local IP address to this machine", e.hostOrIP)
}
func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netaddr.IP, err error) {
if s == "" {
return ip, os.ErrInvalid
}
ip, err = netaddr.ParseIP(s)
if err == nil {
// If we're online already and have a netmap, double check that the IP
// address specified is valid.
if st.BackendState == "Running" {
ps, ok := peerWithTailscaleIP(st, ip)
if !ok {
return ip, fmt.Errorf("no node found in netmap with IP %v", ip)
}
if !ps.ExitNodeOption {
return ip, fmt.Errorf("node %v is not advertising an exit node", ip)
}
}
if !isRemoteIP(st, ip) {
return ip, ExitNodeLocalIPError{s}
}
return ip, nil
}
match := 0
for _, ps := range st.Peer {
baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix)
if !strings.EqualFold(s, baseName) {
continue
}
match++
if len(ps.TailscaleIPs) == 0 {
return ip, fmt.Errorf("node %q has no Tailscale IP?", s)
}
if !ps.ExitNodeOption {
return ip, fmt.Errorf("node %q is not advertising an exit node", s)
}
ip = ps.TailscaleIPs[0]
}
switch match {
case 0:
return ip, fmt.Errorf("invalid value %q for --exit-node; must be IP or unique node name", s)
case 1:
if !isRemoteIP(st, ip) {
return ip, ExitNodeLocalIPError{s}
}
return ip, nil
default:
return ip, fmt.Errorf("ambiguous exit node name %q", s)
}
}
// SetExitNodeIP validates and sets the ExitNodeIP from a user-provided string
// specifying either an IP address or a MagicDNS base name ("foo", as opposed to
// "foo.bar.beta.tailscale.net"). This method does not mutate ExitNodeID and
// will fail if ExitNodeID is already set.
func (p *Prefs) SetExitNodeIP(s string, st *ipnstate.Status) error {
if !p.ExitNodeID.IsZero() {
return ErrExitNodeIDAlreadySet
}
ip, err := exitNodeIPOfArg(s, st)
if err == nil {
p.ExitNodeIP = ip
}
return err
}
// PrefsFromBytes deserializes Prefs from a JSON blob. If
// enforceDefaults is true, Prefs.RouteAll and Prefs.AllowSingleHosts
// are forced on.

View File

@@ -17,6 +17,7 @@ import (
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/types/key"
@@ -679,3 +680,133 @@ func TestPrefsExitNode(t *testing.T) {
t.Errorf("routes = %d; want %d", got, want)
}
}
func TestExitNodeIPOfArg(t *testing.T) {
mustIP := netaddr.MustParseIP
tests := []struct {
name string
arg string
st *ipnstate.Status
want netaddr.IP
wantErr string
}{
{
name: "ip_while_stopped_okay",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Stopped",
},
want: mustIP("1.2.3.4"),
},
{
name: "ip_not_found",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
},
wantErr: `no node found in netmap with IP 1.2.3.4`,
},
{
name: "ip_not_exit",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
},
},
},
wantErr: `node 1.2.3.4 is not advertising an exit node`,
},
{
name: "ip",
arg: "1.2.3.4",
st: &ipnstate.Status{
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
ExitNodeOption: true,
},
},
},
want: mustIP("1.2.3.4"),
},
{
name: "no_match",
arg: "unknown",
st: &ipnstate.Status{MagicDNSSuffix: ".foo"},
wantErr: `invalid value "unknown" for --exit-node; must be IP or unique node name`,
},
{
name: "name",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},
},
want: mustIP("1.0.0.2"),
},
{
name: "name_not_exit",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
},
},
},
wantErr: `node "skippy" is not advertising an exit node`,
},
{
name: "ambiguous",
arg: "skippy",
st: &ipnstate.Status{
MagicDNSSuffix: ".foo",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
key.NewNode().Public(): {
DNSName: "SKIPPY.foo.",
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},
},
wantErr: `ambiguous exit node name "skippy"`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := exitNodeIPOfArg(tt.arg, tt.st)
if err != nil {
if err.Error() == tt.wantErr {
return
}
if tt.wantErr == "" {
t.Fatal(err)
}
t.Fatalf("error = %#q; want %#q", err, tt.wantErr)
}
if tt.wantErr != "" {
t.Fatalf("got %v; want error %#q", got, tt.wantErr)
}
if got != tt.want {
t.Fatalf("got %v; want %v", got, tt.want)
}
})
}
}

View File

@@ -5,21 +5,7 @@
package ipn
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"sync"
"time"
"tailscale.com/atomicfile"
"tailscale.com/kube"
"tailscale.com/paths"
)
// ErrStateNotExist is returned by StateStore.ReadState when the
@@ -58,203 +44,3 @@ type StateStore interface {
// WriteState saves bs as the state associated with ID.
WriteState(id StateKey, bs []byte) error
}
// KubeStore is a StateStore that uses a Kubernetes Secret for persistence.
type KubeStore struct {
client *kube.Client
secretName string
}
// NewKubeStore returns a new KubeStore that persists to the named secret.
func NewKubeStore(secretName string) (*KubeStore, error) {
c, err := kube.New()
if err != nil {
return nil, err
}
return &KubeStore{
client: c,
secretName: secretName,
}, nil
}
func (s *KubeStore) String() string { return "KubeStore" }
// ReadState implements the StateStore interface.
func (s *KubeStore) ReadState(id StateKey) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
secret, err := s.client.GetSecret(ctx, s.secretName)
if err != nil {
if st, ok := err.(*kube.Status); ok && st.Code == 404 {
return nil, ErrStateNotExist
}
return nil, err
}
b, ok := secret.Data[string(id)]
if !ok {
return nil, ErrStateNotExist
}
return b, nil
}
// WriteState implements the StateStore interface.
func (s *KubeStore) WriteState(id StateKey, bs []byte) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
secret, err := s.client.GetSecret(ctx, s.secretName)
if err != nil {
if st, ok := err.(*kube.Status); ok && st.Code == 404 {
return s.client.CreateSecret(ctx, &kube.Secret{
TypeMeta: kube.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: kube.ObjectMeta{
Name: s.secretName,
},
Data: map[string][]byte{
string(id): bs,
},
})
}
return err
}
secret.Data[string(id)] = bs
if err := s.client.UpdateSecret(ctx, secret); err != nil {
return err
}
return err
}
// MemoryStore is a store that keeps state in memory only.
type MemoryStore struct {
mu sync.Mutex
cache map[StateKey][]byte
}
func (s *MemoryStore) String() string { return "MemoryStore" }
// ReadState implements the StateStore interface.
func (s *MemoryStore) ReadState(id StateKey) ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
bs, ok := s.cache[id]
if !ok {
return nil, ErrStateNotExist
}
return bs, nil
}
// WriteState implements the StateStore interface.
func (s *MemoryStore) WriteState(id StateKey, bs []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.cache == nil {
s.cache = map[StateKey][]byte{}
}
s.cache[id] = append([]byte(nil), bs...)
return nil
}
// LoadFromJSON attempts to unmarshal json content into the
// in-memory cache.
func (s *MemoryStore) LoadFromJSON(data []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
return json.Unmarshal(data, &s.cache)
}
// ExportToJSON exports the content of the cache to
// JSON formatted []byte.
func (s *MemoryStore) ExportToJSON() ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
if len(s.cache) == 0 {
// Avoid "null" serialization.
return []byte("{}"), nil
}
return json.MarshalIndent(s.cache, "", " ")
}
// FileStore is a StateStore that uses a JSON file for persistence.
type FileStore struct {
path string
mu sync.RWMutex
cache map[StateKey][]byte
}
// Path returns the path that NewFileStore was called with.
func (s *FileStore) Path() string { return s.path }
func (s *FileStore) String() string { return fmt.Sprintf("FileStore(%q)", s.path) }
// NewFileStore returns a new file store that persists to path.
func NewFileStore(path string) (*FileStore, error) {
// We unconditionally call this to ensure that our perms are correct
if err := paths.MkStateDir(filepath.Dir(path)); err != nil {
return nil, fmt.Errorf("creating state directory: %w", err)
}
bs, err := ioutil.ReadFile(path)
// Treat an empty file as a missing file.
// (https://github.com/tailscale/tailscale/issues/895#issuecomment-723255589)
if err == nil && len(bs) == 0 {
log.Printf("ipn.NewFileStore(%q): file empty; treating it like a missing file [warning]", path)
err = os.ErrNotExist
}
if err != nil {
if os.IsNotExist(err) {
// Write out an initial file, to verify that we can write
// to the path.
if err = atomicfile.WriteFile(path, []byte("{}"), 0600); err != nil {
return nil, err
}
return &FileStore{
path: path,
cache: map[StateKey][]byte{},
}, nil
}
return nil, err
}
ret := &FileStore{
path: path,
cache: map[StateKey][]byte{},
}
if err := json.Unmarshal(bs, &ret.cache); err != nil {
return nil, err
}
return ret, nil
}
// ReadState implements the StateStore interface.
func (s *FileStore) ReadState(id StateKey) ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
bs, ok := s.cache[id]
if !ok {
return nil, ErrStateNotExist
}
return bs, nil
}
// WriteState implements the StateStore interface.
func (s *FileStore) WriteState(id StateKey, bs []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
if bytes.Equal(s.cache[id], bs) {
return nil
}
s.cache[id] = append([]byte(nil), bs...)
bs, err := json.MarshalIndent(s.cache, "", " ")
if err != nil {
return err
}
return atomicfile.WriteFile(s.path, bs, 0600)
}

View File

@@ -5,8 +5,8 @@
//go:build linux
// +build linux
// Package aws contains an AWS SSM StateStore implementation.
package aws
// Package awsstore contains an ipn.StateStore implementation using AWS SSM.
package awsstore
import (
"context"
@@ -20,6 +20,8 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ssm"
ssmTypes "github.com/aws/aws-sdk-go-v2/service/ssm/types"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/types/logger"
)
const (
@@ -46,12 +48,12 @@ type awsStore struct {
ssmClient awsSSMClient
ssmARN arn.ARN
memory ipn.MemoryStore
memory mem.Store
}
// NewStore returns a new ipn.StateStore using the AWS SSM storage
// New returns a new ipn.StateStore using the AWS SSM storage
// location given by ssmARN.
func NewStore(ssmARN string) (ipn.StateStore, error) {
func New(_ logger.Logf, ssmARN string) (ipn.StateStore, error) {
return newStore(ssmARN, nil)
}

View File

@@ -5,7 +5,7 @@
//go:build !linux
// +build !linux
package aws
package awsstore
import (
"fmt"

View File

@@ -5,7 +5,7 @@
//go:build linux
// +build linux
package aws
package awsstore
import (
"context"

View File

@@ -0,0 +1,85 @@
// 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 kubestore contains an ipn.StateStore implementation using Kubernetes Secrets.
package kubestore
import (
"context"
"time"
"tailscale.com/ipn"
"tailscale.com/kube"
"tailscale.com/types/logger"
)
// Store is an ipn.StateStore that uses a Kubernetes Secret for persistence.
type Store struct {
client *kube.Client
secretName string
}
// New returns a new Store that persists to the named secret.
func New(_ logger.Logf, secretName string) (*Store, error) {
c, err := kube.New()
if err != nil {
return nil, err
}
return &Store{
client: c,
secretName: secretName,
}, nil
}
func (s *Store) String() string { return "kube.Store" }
// ReadState implements the StateStore interface.
func (s *Store) ReadState(id ipn.StateKey) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
secret, err := s.client.GetSecret(ctx, s.secretName)
if err != nil {
if st, ok := err.(*kube.Status); ok && st.Code == 404 {
return nil, ipn.ErrStateNotExist
}
return nil, err
}
b, ok := secret.Data[string(id)]
if !ok {
return nil, ipn.ErrStateNotExist
}
return b, nil
}
// WriteState implements the StateStore interface.
func (s *Store) WriteState(id ipn.StateKey, bs []byte) error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
secret, err := s.client.GetSecret(ctx, s.secretName)
if err != nil {
if st, ok := err.(*kube.Status); ok && st.Code == 404 {
return s.client.CreateSecret(ctx, &kube.Secret{
TypeMeta: kube.TypeMeta{
APIVersion: "v1",
Kind: "Secret",
},
ObjectMeta: kube.ObjectMeta{
Name: s.secretName,
},
Data: map[string][]byte{
string(id): bs,
},
})
}
return err
}
secret.Data[string(id)] = bs
if err := s.client.UpdateSecret(ctx, secret); err != nil {
return err
}
return err
}

View File

@@ -0,0 +1,69 @@
// 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 mem provides an in-memory ipn.StateStore implementation.
package mem
import (
"encoding/json"
"sync"
"tailscale.com/ipn"
"tailscale.com/types/logger"
)
// New returns a new Store.
func New(logger.Logf, string) (ipn.StateStore, error) {
return new(Store), nil
}
// Store is an ipn.StateStore that keeps state in memory only.
type Store struct {
mu sync.Mutex
cache map[ipn.StateKey][]byte
}
func (s *Store) String() string { return "mem.Store" }
// ReadState implements the StateStore interface.
func (s *Store) ReadState(id ipn.StateKey) ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
bs, ok := s.cache[id]
if !ok {
return nil, ipn.ErrStateNotExist
}
return bs, nil
}
// WriteState implements the StateStore interface.
func (s *Store) WriteState(id ipn.StateKey, bs []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.cache == nil {
s.cache = map[ipn.StateKey][]byte{}
}
s.cache[id] = append([]byte(nil), bs...)
return nil
}
// LoadFromJSON attempts to unmarshal json content into the
// in-memory cache.
func (s *Store) LoadFromJSON(data []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
return json.Unmarshal(data, &s.cache)
}
// ExportToJSON exports the content of the cache to
// JSON formatted []byte.
func (s *Store) ExportToJSON() ([]byte, error) {
s.mu.Lock()
defer s.mu.Unlock()
if len(s.cache) == 0 {
// Avoid "null" serialization.
return []byte("{}"), nil
}
return json.MarshalIndent(s.cache, "", " ")
}

192
ipn/store/stores.go Normal file
View File

@@ -0,0 +1,192 @@
// 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 store provides various implementation of ipn.StateStore.
package store
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"tailscale.com/atomicfile"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/paths"
"tailscale.com/types/logger"
)
// Provider returns a StateStore for the provided path.
// The arg is of the form "prefix:rest", where prefix was previously registered with Register.
type Provider func(logf logger.Logf, arg string) (ipn.StateStore, error)
var regOnce sync.Once
var registerAvailableExternalStores func()
func registerDefaultStores() {
Register("mem:", mem.New)
if registerAvailableExternalStores != nil {
registerAvailableExternalStores()
}
}
var knownStores map[string]Provider
// New returns a StateStore based on the provided arg
// and registered stores.
// The arg is of the form "prefix:rest", where prefix was previously
// registered with Register.
//
// By default the following stores are registered:
//
// * if the string begins with "mem:", the suffix
// is ignored and an in-memory store is used.
// * (Linux-only) if the string begins with "arn:",
// the suffix an AWS ARN for an SSM.
// * (Linux-only) if the string begins with "kube:",
// the suffix is a Kubernetes secret name
// * In all other cases, the path is treated as a filepath.
func New(logf logger.Logf, path string) (ipn.StateStore, error) {
regOnce.Do(registerDefaultStores)
for prefix, sf := range knownStores {
if strings.HasPrefix(path, prefix) {
// We can't strip the prefix here as some NewStoreFunc (like arn:)
// expect the prefix.
return sf(logf, path)
}
}
if runtime.GOOS == "windows" {
path = TryWindowsAppDataMigration(logf, path)
}
return NewFileStore(logf, path)
}
// Register registers a prefix to be used for
// NewStore. It panics if the prefix is empty, or if the
// prefix is already registered.
// The provided fn is called with the path passed to NewStore;
// the prefix is not stripped.
func Register(prefix string, fn Provider) {
if len(prefix) == 0 {
panic("prefix is empty")
}
if _, ok := knownStores[prefix]; ok {
panic(fmt.Sprintf("%q already registered", prefix))
}
if knownStores == nil {
knownStores = make(map[string]Provider)
}
knownStores[prefix] = fn
}
// TryWindowsAppDataMigration attempts to copy the Windows state file
// from its old location to the new location. (Issue 2856)
//
// Tailscale 1.14 and before stored state under %LocalAppData%
// (usually "C:\WINDOWS\system32\config\systemprofile\AppData\Local"
// when tailscaled.exe is running as a non-user system service).
// However it is frequently cleared for almost any reason: Windows
// updates, System Restore, even various System Cleaner utilities.
//
// Returns a string of the path to use for the state file.
// This will be a fallback %LocalAppData% path if migration fails,
// a %ProgramData% path otherwise.
func TryWindowsAppDataMigration(logf logger.Logf, path string) string {
if path != paths.DefaultTailscaledStateFile() {
// If they're specifying a non-default path, just trust that they know
// what they are doing.
return path
}
oldFile := paths.LegacyStateFilePath()
return paths.TryConfigFileMigration(logf, oldFile, path)
}
// FileStore is a StateStore that uses a JSON file for persistence.
type FileStore struct {
path string
mu sync.RWMutex
cache map[ipn.StateKey][]byte
}
// Path returns the path that NewFileStore was called with.
func (s *FileStore) Path() string { return s.path }
func (s *FileStore) String() string { return fmt.Sprintf("FileStore(%q)", s.path) }
// NewFileStore returns a new file store that persists to path.
func NewFileStore(logf logger.Logf, path string) (ipn.StateStore, error) {
// We unconditionally call this to ensure that our perms are correct
if err := paths.MkStateDir(filepath.Dir(path)); err != nil {
return nil, fmt.Errorf("creating state directory: %w", err)
}
bs, err := ioutil.ReadFile(path)
// Treat an empty file as a missing file.
// (https://github.com/tailscale/tailscale/issues/895#issuecomment-723255589)
if err == nil && len(bs) == 0 {
logf("store.NewFileStore(%q): file empty; treating it like a missing file [warning]", path)
err = os.ErrNotExist
}
if err != nil {
if os.IsNotExist(err) {
// Write out an initial file, to verify that we can write
// to the path.
if err = atomicfile.WriteFile(path, []byte("{}"), 0600); err != nil {
return nil, err
}
return &FileStore{
path: path,
cache: map[ipn.StateKey][]byte{},
}, nil
}
return nil, err
}
ret := &FileStore{
path: path,
cache: map[ipn.StateKey][]byte{},
}
if err := json.Unmarshal(bs, &ret.cache); err != nil {
return nil, err
}
return ret, nil
}
// ReadState implements the StateStore interface.
func (s *FileStore) ReadState(id ipn.StateKey) ([]byte, error) {
s.mu.RLock()
defer s.mu.RUnlock()
bs, ok := s.cache[id]
if !ok {
return nil, ipn.ErrStateNotExist
}
return bs, nil
}
// WriteState implements the StateStore interface.
func (s *FileStore) WriteState(id ipn.StateKey, bs []byte) error {
s.mu.Lock()
defer s.mu.Unlock()
if bytes.Equal(s.cache[id], bs) {
return nil
}
s.cache[id] = append([]byte(nil), bs...)
bs, err := json.MarshalIndent(s.cache, "", " ")
if err != nil {
return err
}
return atomicfile.WriteFile(s.path, bs, 0600)
}

26
ipn/store/stores_linux.go Normal file
View File

@@ -0,0 +1,26 @@
// 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 store
import (
"strings"
"tailscale.com/ipn"
"tailscale.com/ipn/store/awsstore"
"tailscale.com/ipn/store/kubestore"
"tailscale.com/types/logger"
)
func init() {
registerAvailableExternalStores = registerExternalStores
}
func registerExternalStores() {
Register("kube:", func(logf logger.Logf, path string) (ipn.StateStore, error) {
secretName := strings.TrimPrefix(path, "kube:")
return kubestore.New(logf, secretName)
})
Register("arn:", awsstore.New)
}

View File

@@ -1,24 +1,84 @@
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// 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 ipn
package store
import (
"path/filepath"
"testing"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/tstest"
"tailscale.com/types/logger"
)
func testStoreSemantics(t *testing.T, store StateStore) {
func TestNewStore(t *testing.T) {
regOnce.Do(registerDefaultStores)
t.Cleanup(func() {
knownStores = map[string]Provider{}
registerDefaultStores()
})
knownStores = map[string]Provider{}
type store1 struct {
ipn.StateStore
path string
}
type store2 struct {
ipn.StateStore
path string
}
Register("arn:", func(_ logger.Logf, path string) (ipn.StateStore, error) {
return &store1{new(mem.Store), path}, nil
})
Register("kube:", func(_ logger.Logf, path string) (ipn.StateStore, error) {
return &store2{new(mem.Store), path}, nil
})
Register("mem:", func(_ logger.Logf, path string) (ipn.StateStore, error) {
return new(mem.Store), nil
})
path := "mem:abcd"
if s, err := New(t.Logf, path); err != nil {
t.Fatalf("%q: %v", path, err)
} else if _, ok := s.(*mem.Store); !ok {
t.Fatalf("%q: got: %T, want: %T", path, s, new(mem.Store))
}
path = "arn:foo"
if s, err := New(t.Logf, path); err != nil {
t.Fatalf("%q: %v", path, err)
} else if _, ok := s.(*store1); !ok {
t.Fatalf("%q: got: %T, want: %T", path, s, new(store1))
}
path = "kube:abcd"
if s, err := New(t.Logf, path); err != nil {
t.Fatalf("%q: %v", path, err)
} else if _, ok := s.(*store2); !ok {
t.Fatalf("%q: got: %T, want: %T", path, s, new(store2))
}
path = filepath.Join(t.TempDir(), "state")
if s, err := New(t.Logf, path); err != nil {
t.Fatalf("%q: %v", path, err)
} else if _, ok := s.(*FileStore); !ok {
t.Fatalf("%q: got: %T, want: %T", path, s, new(FileStore))
}
}
func testStoreSemantics(t *testing.T, store ipn.StateStore) {
t.Helper()
tests := []struct {
// if true, data is data to write. If false, data is expected
// output of read.
write bool
id StateKey
id ipn.StateKey
data string
// If write=false, true if we expect a not-exist error.
notExists bool
@@ -63,7 +123,7 @@ func testStoreSemantics(t *testing.T, store StateStore) {
} else {
bs, err := store.ReadState(test.id)
if err != nil {
if test.notExists && err == ErrStateNotExist {
if test.notExists && err == ipn.ErrStateNotExist {
continue
}
t.Errorf("reading %q: %v", test.id, err)
@@ -79,7 +139,7 @@ func testStoreSemantics(t *testing.T, store StateStore) {
func TestMemoryStore(t *testing.T) {
tstest.PanicOnLog()
store := &MemoryStore{}
store := new(mem.Store)
testStoreSemantics(t, store)
}
@@ -89,7 +149,7 @@ func TestFileStore(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "test-file-store.conf")
store, err := NewFileStore(path)
store, err := NewFileStore(nil, path)
if err != nil {
t.Fatalf("creating file store failed: %v", err)
}
@@ -98,12 +158,12 @@ func TestFileStore(t *testing.T) {
// Build a brand new file store and check that both IDs written
// above are still there.
store, err = NewFileStore(path)
store, err = NewFileStore(nil, path)
if err != nil {
t.Fatalf("creating second file store failed: %v", err)
}
expected := map[StateKey]string{
expected := map[ipn.StateKey]string{
"foo": "bar",
"baz": "quux",
}

View File

@@ -110,7 +110,7 @@ func getError(resp *http.Response) error {
return st
}
func (c *Client) doRequest(ctx context.Context, method, url string, in, out interface{}) error {
func (c *Client) doRequest(ctx context.Context, method, url string, in, out any) error {
tk, err := c.getOrRenewToken()
if err != nil {
return err

View File

@@ -77,7 +77,7 @@ func dayOf(t time.Time) civilDay {
return civilDay{t.Year(), t.Month(), t.Day()}
}
func (w *logFileWriter) Logf(format string, a ...interface{}) {
func (w *logFileWriter) Logf(format string, a ...any) {
w.mu.Lock()
defer w.mu.Unlock()

View File

@@ -416,7 +416,7 @@ func New(collection string) *Policy {
console := log.New(stderrWriter{}, "", lflags)
var earlyErrBuf bytes.Buffer
earlyLogf := func(format string, a ...interface{}) {
earlyLogf := func(format string, a ...any) {
fmt.Fprintf(&earlyErrBuf, format, a...)
earlyErrBuf.WriteByte('\n')
}

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