Compare commits

...

195 Commits

Author SHA1 Message Date
Juan Font
cdc8bab7d9 Merge pull request #768 from kazauwa/feature/json-logs
toggle json logging via config
2022-09-20 23:32:10 +02:00
Juan Font
397754753f Merge branch 'main' into feature/json-logs 2022-09-20 23:11:29 +02:00
Juan Font
42ef71bff9 Merge pull request #811 from kradalby/primary-routes
Fix subnet routers
2022-09-18 21:59:25 +02:00
Kristoffer Dalby
f2da1a1665 Add comment and update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 12:14:49 +02:00
Kristoffer Dalby
356b76fc56 Format
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 11:37:38 +02:00
Kristoffer Dalby
33ae56acfa Add primary routes to node
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 11:36:35 +02:00
Juan Font
9923adcb8b Merge branch 'main' into feature/json-logs 2022-09-15 00:22:18 +02:00
Igor Perepilitsyn
874d6aaf6b Make styling fixes 2022-09-11 21:44:28 +02:00
Igor Perepilitsyn
ae4f2cc4b5 Update changelog 2022-09-11 21:37:38 +02:00
Igor Perepilitsyn
dd155dca97 Create a distinct log section in config 2022-09-11 21:37:23 +02:00
Juan Font
a0a463494b Merge pull request #797 from madjam002/patch-1
Remove --rm flag from Docker example
2022-09-07 17:13:14 +02:00
Jamie Greeff
07dca79b20 Remove --rm flag from Docker example
It appears to be causing confusion for users on Discord when copying/pasting from the example here, if Headscale crashes on launch then the container will be removed and logs can't be viewed with `docker logs`.
2022-09-07 14:16:04 +01:00
Juan Font
7247302f45 Merge branch 'main' into feature/json-logs 2022-09-07 00:05:38 +02:00
Juan Font
1a5a5b12b7 Merge pull request #795 from stefanvanburen/svanburen/buf-mod-update
Run `buf mod update` in protos/
2022-09-06 23:49:32 +02:00
Stefan VanBuren
0099dd1724 Run buf mod update 2022-09-06 14:52:09 -04:00
Juan Font
1f131c6729 Merge branch 'main' into feature/json-logs 2022-09-06 20:18:35 +02:00
Juan Font
449a135b94 Merge pull request #791 from kradalby/add-logo
Add logo files and add to readme
2022-09-05 15:34:00 +02:00
Kristoffer Dalby
002d484abe use logo in readme correcly 2022-09-05 15:31:03 +02:00
Kristoffer Dalby
9823ef2af5 use logo in readme 2022-09-05 15:30:19 +02:00
Kristoffer Dalby
641c6fd439 Add logo files 2022-09-05 15:29:30 +02:00
Juan Font
302a88bfdb Merge branch 'main' into feature/json-logs 2022-09-04 22:32:58 +02:00
Juan Font
af60ffb7fa Merge pull request #788 from juanfont/warn-websockets-requirement
Warn when Headscale is running behind an improperly configured proxy
2022-09-04 16:44:54 +02:00
Juan Font Alonso
c28e559da4 Updated changelog 2022-09-04 16:23:46 +02:00
Juan Font Alonso
5c59255b41 Also warn in DERP server if Websockets are not properly working 2022-09-04 16:13:48 +02:00
Juan Font Alonso
a377ee14b4 Minor message change 2022-09-04 16:13:30 +02:00
Juan Font Alonso
2262188d8a Warn when Headscale is running behind a wrongly configured proxy 2022-09-04 16:05:21 +02:00
Juan Font
7c49c752a9 Merge pull request #786 from juanfont/update-deps-20220904
Update dependencies 20220904
2022-09-04 16:01:52 +02:00
Juan Font Alonso
e29726cc50 Updated nix sum 2022-09-04 15:45:35 +02:00
Juan Font Alonso
3c73cbe92b Merge branch 'main' into update-deps-20220904 2022-09-04 15:37:25 +02:00
Juan Font Alonso
cc357062be Missing go.sum 2022-09-04 15:36:59 +02:00
Juan Font Alonso
17c06f7167 Upgrade direct dependencies 2022-09-04 15:36:00 +02:00
Juan Font
d12e0156c3 Merge pull request #784 from juanfont/fix-go-1.19-lint
Fix linting broken after Go 1.19
2022-09-04 15:31:41 +02:00
Juan Font Alonso
204dedaa49 Only pass the context in pollmap, no req needed 2022-09-04 15:14:12 +02:00
Juan Font Alonso
52073ce7c9 Pass context in OIDC helpers 2022-09-04 15:02:18 +02:00
Juan Font Alonso
434747e007 Use timeout in lets encrypt http server 2022-09-04 11:47:05 +02:00
Juan Font Alonso
7a78314d9d Remove nolint directives 2022-09-04 11:44:24 +02:00
Juan Font Alonso
f23e9dc235 Pass the req context when pinging the DB 2022-09-04 11:43:09 +02:00
Juan Font Alonso
4527801d48 More unused parameters removed in protocol functions 2022-09-04 11:41:31 +02:00
Juan Font Alonso
e0857f0226 Removed unused parameters in protocol functions 2022-09-04 11:40:14 +02:00
Juan Font Alonso
0d074b1da6 setLastStateChangeToNow was always receiving nil 2022-09-04 11:37:49 +02:00
Juan Font Alonso
f2fda4f906 Return error on marshaling issues 2022-09-04 11:36:03 +02:00
Juan Font Alonso
c1c36036ae Add timeouts for the Noise server 2022-09-04 11:35:39 +02:00
Juan Font Alonso
9a1438d2e3 Use inherited context 2022-09-04 11:35:13 +02:00
Juan Font Alonso
582122851d Go do not like underscores in packages 2022-09-04 11:34:23 +02:00
Juan Font Alonso
f4d197485c Use library const for HTTP verbs 2022-09-04 11:33:00 +02:00
Juan Font Alonso
68305df9b2 Applied gofumpt 2022-09-04 11:32:29 +02:00
Juan Font Alonso
ca0be81833 Target the latest version for golint 2022-09-04 11:31:06 +02:00
Juan Font
380fbfe438 Merge pull request #780 from juanfont/bump-ts1.30
Target Tailscale version to v1.30.0 (and replace inet.af/netaddr with net/netip)
2022-09-04 09:24:42 +02:00
Juan Font Alonso
32d68a40d5 Update flake sha 2022-09-04 00:00:23 +02:00
Juan Font Alonso
198e92c08f Remove dependency on netaddr 2022-09-03 23:46:14 +02:00
Juan Font Alonso
38b26f5285 Merge branch 'main' into bump-ts1.30 2022-09-03 23:33:09 +02:00
Juan Font
096a009685 Merge pull request #781 from juanfont/switch-to-nix-unstable
Switch to Nix unstable for Go 1.19
2022-09-03 23:32:51 +02:00
Juan Font Alonso
30c0fdb38d Update changelog 2022-09-03 23:19:07 +02:00
Juan Font Alonso
663dbf7395 Use go 1.19 in Nix 2022-09-03 23:06:21 +02:00
Juan Font Alonso
373db0dc5e Switch to nix unstable 2022-09-03 23:05:34 +02:00
Juan Font Alonso
2733fb30cc Minor change in go.mod 2022-09-03 16:23:36 +02:00
Juan Font Alonso
d29411408b Merge branch 'main' into bump-tailscale-v1.30 2022-09-03 16:20:25 +02:00
Juan Font
24bafdf2bb Merge pull request #778 from juanfont/bump-go-1.19
Target Go 1.19 for Headscale
2022-09-03 13:16:48 +02:00
Juan Font
a9ede6a2bc Merge branch 'main' into feature/json-logs 2022-09-03 12:39:04 +02:00
Juan Font Alonso
2c5bf6982c Updated changelog 2022-09-03 12:24:22 +02:00
Juan Font Alonso
dd3ec84000 Minor doc change 2022-09-03 12:22:03 +02:00
Juan Font Alonso
84044e236d Release using go 1.19 2022-09-03 12:21:54 +02:00
Juan Font Alonso
2ddf7ab515 Use Go 1.19 in Dockerfiles 2022-09-03 12:21:35 +02:00
Juan Font Alonso
f519c513c2 Target go 1.19 in go.mod 2022-09-03 12:21:04 +02:00
Juan Font Alonso
d5cc5b2bc8 Move integration tests to net/netip 2022-09-02 09:22:34 +02:00
Juan Font Alonso
51abf90db6 Use net/netip in derp server 2022-09-02 09:16:19 +02:00
Juan Font Alonso
71410cb6da Port dns to net/netip 2022-09-02 09:15:05 +02:00
Juan Font Alonso
efb12f208c Move db to net/netip 2022-09-02 09:13:50 +02:00
Juan Font Alonso
64ede5dbef Move namespaces unit tests to net/netip 2022-09-02 09:13:07 +02:00
Juan Font Alonso
7af78152a4 Migrate routes to net/netip 2022-09-02 00:06:19 +02:00
Juan Font Alonso
290ec8bb19 Migrate ACLs to net/netip 2022-09-02 00:05:43 +02:00
Juan Font Alonso
cdf48b1216 Migrate utils to net/netip 2022-09-02 00:05:18 +02:00
Juan Font Alonso
a24710a961 Migrate machine to net/netip 2022-09-02 00:04:31 +02:00
Juan Font Alonso
197da8afcb Migrate config.go to net/netip 2022-09-02 00:04:04 +02:00
Juan Font Alonso
12385d4357 Target Tailscale v1.30.0 2022-09-01 20:50:56 +02:00
Juan Font
e7f8bb866f Merge pull request #772 from juanfont/enable-1.30-in-tests
Add Tailscale v1.30.0 to the integration test roaster
2022-09-01 00:03:21 +02:00
Juan Font Alonso
1ad19a3bd8 Add 1.30.0 to the version roaster 2022-08-31 22:17:13 +02:00
Igor Perepilitsyn
bb6b07dedc FIXES #768 add new config entry to the old itegration tests 2022-08-26 13:43:25 +02:00
Igor Perepilitsyn
2403c0e198 toggle json logging via config 2022-08-26 13:10:51 +02:00
Kristoffer Dalby
cc0bec15ef Merge pull request #760 from juanfont/update-contributors 2022-08-23 21:21:50 +02:00
github-actions[bot]
20970b580a docs(README): update contributors 2022-08-22 12:47:42 +00:00
Juan Font
53857d418a Merge pull request #756 from huskyii/env_config
Env config
2022-08-22 14:47:01 +02:00
Jiang Zhu
a81a4d274f Update CHANGELOG.md 2022-08-22 20:20:20 +08:00
Jiang Zhu
ce4a1cf447 1. add noise key to config file
2. lower node check interval
2022-08-22 00:35:08 +08:00
Jiang Zhu
35dd9209b9 update CHANGELOG.md 2022-08-21 23:51:04 +08:00
Jiang Zhu
81f91f03b4 add env var to specify config location 2022-08-21 23:51:04 +08:00
Juan Font
84a5edf345 Merge pull request #738 from juanfont/hs2021-v2
Implement TS2021 protocol in headscale
2022-08-21 16:02:28 +02:00
Juan Font Alonso
4aafe6c9d1 Added line in CHANGELOG 2022-08-21 12:32:01 +02:00
Juan Font
3ab1487641 Merge branch 'main' into hs2021-v2 2022-08-21 11:57:33 +02:00
Kristoffer Dalby
0c7f1eac82 Merge pull request #757 from juanfont/changelog-0.16.4 2022-08-21 11:15:30 +02:00
Juan Font Alonso
6fe895fd22 Updated changelog for 0.16.4 2022-08-21 10:51:58 +02:00
Juan Font Alonso
71d22dc994 Added missing files 2022-08-21 10:47:45 +02:00
Juan Font Alonso
4424a9abc0 Noise private key now a nested field in config 2022-08-21 10:42:23 +02:00
Juan Font Alonso
e20e818a42 Integrate expiration fixes (#754) in TS2021 branch 2022-08-20 11:46:44 +02:00
Juan Font
061e2fe4b4 Merge pull request #754 from Aluxima/expired-machine-registration
Fix cli registration of expired machines
2022-08-20 11:41:15 +02:00
Juan Font Alonso
f0a8a2857b Clarified why we have a different key 2022-08-20 00:23:33 +02:00
Juan Font Alonso
175dfa1ede Update flake.nix sum 2022-08-20 00:15:46 +02:00
Juan Font Alonso
04e4fa785b Updated dependencies 2022-08-20 00:11:07 +02:00
Juan Font Alonso
6aec520889 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-20 00:06:58 +02:00
Juan Font Alonso
e9906b522f Use upstream AcceptHTTP for the Noise upgrade 2022-08-20 00:06:26 +02:00
Juan Font
2f554133c5 Move comment up
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-19 23:49:06 +02:00
Juan Font Alonso
922b8b5365 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-19 16:32:18 +02:00
Juan Font Alonso
c894db3dd4 Use common core for noise registration 2022-08-19 16:29:04 +02:00
Laurent Marchaud
e85562268d Switch to using nodeKey instead of machineKey for expired machines registration
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 15:48:35 +02:00
Laurent Marchaud
fca33aacbe Fix rebased errors scope in machine.go
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 15:07:01 +02:00
Juan Font
e43713a866 Merge branch 'main' into hs2021-v2 2022-08-19 15:02:01 +02:00
Juan Font Alonso
b6e3cd81c6 Fixed minor linting things 2022-08-19 14:27:40 +02:00
Juan Font Alonso
43ad0d4416 Removed unused method 2022-08-19 14:24:43 +02:00
Juan Font Alonso
a33b5a5c00 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-19 14:20:55 +02:00
Juan Font Alonso
e2bffd4f5a Make legacy protocol use common methods for client registration 2022-08-19 14:20:24 +02:00
Juan Font Alonso
a87a9636e3 Expanded response marshal methods to support legacy and Noise 2022-08-19 14:19:29 +02:00
Laurent Marchaud
a31432ee7b Fix changelog
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:14:30 +02:00
Laurent Marchaud
0c66590108 Update changelog
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:11:19 +02:00
Laurent Marchaud
c6ea9b4b80 Fix cli registration of expired machines
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:11:13 +02:00
Juan Font
19455399f4 Merge pull request #752 from juanfont/add-code-of-conduct
Create CODE_OF_CONDUCT.md
2022-08-19 00:38:01 +02:00
Juan Font
43ba1fb176 Prettier 2022-08-18 22:32:53 +00:00
Juan Font
a6f56b4285 Create CODE_OF_CONDUCT.md 2022-08-18 22:08:33 +02:00
Juan Font
9d430d3c72 Update noise.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-18 21:33:56 +02:00
Juan Font Alonso
f9a2a2b57a Add docker DNS IP to the remaining files 2022-08-18 18:07:15 +02:00
Juan Font Alonso
e4d961cfad Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-18 17:57:06 +02:00
Juan Font
67ffebc30a Merge branch 'main' into hs2021-v2 2022-08-18 17:56:56 +02:00
Juan Font Alonso
cf731fafab Catch retry error in taildrop send 2022-08-18 17:56:01 +02:00
Juan Font Alonso
f43a83aad7 Find out IPv4 for taildrop 2022-08-18 17:53:36 +02:00
Juan Font Alonso
7185f8dfea Only use released versions in public integration tests 2022-08-18 17:53:25 +02:00
Juan Font Alonso
8a707de5f1 Add local Docker DNS server (makes resolving http://headscale more reliable) 2022-08-18 17:53:04 +02:00
Juan Font
61bb6292b7 Merge pull request #746 from gozssky/patch-1
Fix charset typo in swagger.go
2022-08-18 12:23:45 +02:00
Juan Font
40e0ae99da Merge branch 'main' into patch-1 2022-08-18 11:49:15 +02:00
Juan Font
2dd615a4ef Merge pull request #745 from 617a7a/main
feat: add support for TLS with Postgres
2022-08-18 11:48:33 +02:00
Azz
7e06abdca2 chore: azz forgot how to write code 2022-08-17 20:12:45 +01:00
Azz
c316f53e23 fix: ci happy now? 2022-08-17 19:32:20 +01:00
Azz
b6d324be69 Merge branch 'main' of https://github.com/juanfont/headscale
# Conflicts:
#	CHANGELOG.md
2022-08-17 19:31:26 +01:00
Juan Font
f7380312d3 Merge pull request #747 from juanfont/fix-oidc
Fix error decoding OIDC claims (#744)
2022-08-17 18:30:50 +02:00
Juan Font
287309b65c Update changelog 2022-08-17 15:08:29 +00:00
Juan Font
cc3de7e723 Fix error decoding claims (#744) 2022-08-17 15:03:10 +00:00
Yujie Xia
e03b3029e3 Fix charset typo in swagger.go 2022-08-17 12:27:58 +08:00
Juan Font Alonso
ba07bac46a Use IPv4 in the tests 2022-08-16 18:42:22 +02:00
Juan Font Alonso
b71a881d0e Retry magicdns tests 2022-08-16 18:19:04 +02:00
Juan Font Alonso
ce53bb0eee Minor changes to HEAD Dockerfile 2022-08-16 17:52:59 +02:00
Juan Font Alonso
c0fe1abf4d Use node_key to find peers 2022-08-16 17:51:43 +02:00
Juan Font Alonso
0db7fc5ab7 Mark all namespaces to lastChange now 2022-08-16 13:39:15 +02:00
azz
701ad3e017 chore: update CHANGELOG.md 2022-08-16 09:09:28 +01:00
azz
0cc14d0aca feat: added db_ssl to config-example.yaml 2022-08-16 09:02:51 +01:00
Azz
3f5ea7998f Merge branch 'main' into main 2022-08-16 08:56:36 +01:00
azz
4c7f54020b feat: add support for TLS with Postgres 2022-08-16 08:50:30 +01:00
Juan Font Alonso
eb461d0713 Enable HEAD and unstable in integration tests 2022-08-16 00:18:02 +02:00
Juan Font Alonso
128ec6717c Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-15 23:35:24 +02:00
Juan Font Alonso
b3cf5289f8 Use CapVer to offer Noise only to supported clients 2022-08-15 23:35:06 +02:00
Juan Font
c701f9e817 Merge branch 'main' into hs2021-v2 2022-08-15 22:56:39 +02:00
Juan Font
e1a95e2057 Merge pull request #734 from vtrf/postgres-connection-string
Add ability to connect to PostgreSQL via unix socket
2022-08-15 19:20:01 +02:00
Victor Freire
0a5db52855 Add ability to connect to PostgreSQL via unix socket 2022-08-15 11:55:38 -03:00
Juan Font
7197ade4b4 Merge branch 'main' into postgres-connection-string 2022-08-15 13:37:09 +02:00
Juan Font Alonso
865f1ffb3c Fix issues with DERP integration tests due to tailscale/tailscale#4323 2022-08-15 11:25:47 +02:00
Juan Font Alonso
8db7629edf Fix config file in integration tests for Noise 2022-08-15 10:53:06 +02:00
Juan Font Alonso
b8980b9ed3 More minor logging stuff 2022-08-15 10:44:22 +02:00
Juan Font Alonso
5cf9eedf42 Minor logging corrections 2022-08-15 10:43:39 +02:00
Juan Font
193b4213b3 Merge pull request #739 from juanfont/updated-changelog-0.16.2
Added changelog entries for 0.16.x
2022-08-14 23:27:27 +02:00
Juan Font Alonso
8557bcedae Added changelog entries for 0.16.x 2022-08-14 23:22:41 +02:00
Juan Font Alonso
f599bea216 Fixed issue when not using compression 2022-08-14 23:15:41 +02:00
Juan Font Alonso
704a19b0a5 Removed legacy method to generate MapResponse 2022-08-14 23:13:07 +02:00
Juan Font Alonso
e29b344e0f Move Noise poll to new file, and use common poll 2022-08-14 23:12:18 +02:00
Juan Font Alonso
7cc227d01e Added Noise field to logging 2022-08-14 23:11:33 +02:00
Juan Font Alonso
df8ecdb603 Working on common codebase for poll, starting with legacy 2022-08-14 22:57:03 +02:00
Juan Font Alonso
f4bab6b290 Created common methods for keep and map poll responses 2022-08-14 22:50:39 +02:00
Juan Font Alonso
35f3dee1d0 Move Noise API to new file 2022-08-14 21:19:52 +02:00
Juan Font Alonso
db89fdea23 Added file for legacy protocol 2022-08-14 21:16:29 +02:00
Juan Font Alonso
d0898ecabc Move common parts of the protocol to dedicated file 2022-08-14 21:15:58 +02:00
Juan Font Alonso
e640c6df05 Fixes in Noise poll (clients should work now) 2022-08-14 21:10:08 +02:00
Juan Font Alonso
ab18c721bb Support for Noise machines in getPeers 2022-08-14 21:07:29 +02:00
Juan Font Alonso
aaa33cf093 Minor change in router 2022-08-14 21:07:05 +02:00
Juan Font Alonso
0f09e19e38 Updated go.mod checksum 2022-08-14 17:09:14 +02:00
Juan Font Alonso
b301405f24 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-14 17:06:03 +02:00
Juan Font Alonso
1f3032ad21 Merge branch 'main' into hs2021-v2 2022-08-14 17:05:51 +02:00
Juan Font Alonso
c10142f767 Added noise poll handler 2022-08-14 17:05:04 +02:00
Juan Font Alonso
0d0042b7e6 Added zstd constant for linting 2022-08-14 17:04:07 +02:00
Juan Font Alonso
78a179c971 Minor update in docs 2022-08-14 16:53:54 +02:00
Juan Font Alonso
cab828c9d4 Fixed unit tests to load config 2022-08-14 16:52:57 +02:00
Juan Font Alonso
ff46f3ff49 Move reusable method to common api file 2022-08-14 16:13:17 +02:00
Juan Font
b67cff50f5 Merge branch 'main' into hs2021-v2 2022-08-14 13:44:12 +02:00
Juan Font Alonso
20d2615081 Check json encoder errors 2022-08-14 12:47:04 +02:00
Juan Font Alonso
eb8d8f142c And more linting stuff 2022-08-14 12:44:07 +02:00
Juan Font Alonso
3bea20850a Some linting fixes 2022-08-14 12:40:22 +02:00
Juan Font Alonso
ade1b73779 Output an error when a user runs headscale without noise_private_key_path defined 2022-08-14 12:35:14 +02:00
Juan Font Alonso
281ae59b5a Update integration tests to work with Noise protocol 2022-08-14 12:18:33 +02:00
Juan Font Alonso
9994fce9d5 Fixed some linting errors 2022-08-14 12:00:43 +02:00
Juan Font Alonso
39b85b02bb Move getMapResponse into reusable function by TS2019 and TS2021 2022-08-14 03:20:53 +02:00
Juan Font Alonso
7a91c82cda Merge branch 'main' into hs2021-v2 2022-08-14 03:07:43 +02:00
Juan Font Alonso
c7cea9ef16 updated paths 2022-08-14 03:07:28 +02:00
Juan Font Alonso
1880035f6f Add registration handler over Noise protocol 2022-08-13 21:12:19 +02:00
Juan Font Alonso
fdd0c50402 Added helper method to fetch machines by any nodekey + tests 2022-08-13 21:03:02 +02:00
Juan Font Alonso
be24bacb79 Add noise mux and Noise path to base router 2022-08-13 20:55:37 +02:00
Juan Font Alonso
b261d19cfe Added Noise upgrade handler and Noise mux 2022-08-13 20:52:11 +02:00
Victor Freire
ec5acf7be2 Add ability to connect to PostgreSQL via unix socket 2022-08-13 11:34:12 -03:00
Juan Font Alonso
014e7abc68 Make private key errors constants 2022-08-13 14:46:23 +02:00
Juan Font Alonso
3e8f0e9984 Added support for Noise clients in /key handler 2022-08-13 11:24:05 +02:00
Juan Font Alonso
6e8e2bf508 Generate and read the Noise private key 2022-08-13 11:14:38 +02:00
65 changed files with 2621 additions and 1315 deletions

View File

@@ -26,7 +26,7 @@ jobs:
if: steps.changed-files.outputs.any_changed == 'true'
uses: golangci/golangci-lint-action@v2
with:
version: v1.46.1
version: v1.49.0
# Only block PRs on new problems.
# If this is not enabled, we will end up having PRs

View File

@@ -18,7 +18,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18.0
go-version: 1.19.0
- name: Install dependencies
run: |

View File

@@ -1,7 +1,7 @@
---
before:
hooks:
- go mod tidy -compat=1.18
- go mod tidy -compat=1.19
release:
prerelease: auto

View File

@@ -1,10 +1,48 @@
# CHANGELOG
## 0.17.0 (2022-xx-xx)
## 0.17.0 (2022-XX-XX)
### BREAKING
- Log level option `log_level` was moved to a distinct `log` config section and renamed to `level` [#768](https://github.com/juanfont/headscale/pull/768)
### Changes
- Added support for Tailscale TS2021 protocol [#738](https://github.com/juanfont/headscale/pull/738)
- Add ability to specify config location via env var `HEADSCALE_CONFIG` [#674](https://github.com/juanfont/headscale/issues/674)
- Target Go 1.19 for Headscale [#778](https://github.com/juanfont/headscale/pull/778)
- Target Tailscale v1.30.0 to build Headscale [#780](https://github.com/juanfont/headscale/pull/780)
- Give a warning when running Headscale with reverse proxy improperly configured for WebSockets [#788](https://github.com/juanfont/headscale/pull/788)
- Fix subnet routers with Primary Routes [#811](https://github.com/juanfont/headscale/pull/811)
- Added support for JSON logs [#653](https://github.com/juanfont/headscale/issues/653)
## 0.16.4 (2022-08-21)
### Changes
- Add ability to connect to PostgreSQL over TLS/SSL [#745](https://github.com/juanfont/headscale/pull/745)
- Fix CLI registration of expired machines [#754](https://github.com/juanfont/headscale/pull/754)
## 0.16.3 (2022-08-17)
### Changes
- Fix issue with OIDC authentication [#747](https://github.com/juanfont/headscale/pull/747)
## 0.16.2 (2022-08-14)
### Changes
- Fixed bugs in the client registration process after migration to NodeKey [#735](https://github.com/juanfont/headscale/pull/735)
## 0.16.1 (2022-08-12)
### Changes
- Updated dependencies (including the library that lacked armhf support) [#722](https://github.com/juanfont/headscale/pull/722)
- Fix missing group expansion in function `excludeCorretlyTaggedNodes` [#563](https://github.com/juanfont/headscale/issues/563)
- Improve registration protocol implementation and switch to NodeKey as main identifier [#725](https://github.com/juanfont/headscale/pull/725)
- Add ability to connect to PostgreSQL via unix socket [#734](https://github.com/juanfont/headscale/pull/734)
## 0.16.0 (2022-07-25)
@@ -114,7 +152,7 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh
- OpenID Connect users will be mapped per namespaces
- Each user will get its own namespace, created if it does not exist
- `oidc.domain_map` option has been removed
- `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml))
- `strip_email_domain` option has been added (see [config-example.yaml](./config-example.yaml))
### Changes

134
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,134 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation
in our community a harassment-free experience for everyone, regardless
of age, body size, visible or invisible disability, ethnicity, sex
characteristics, gender identity and expression, level of experience,
education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open,
welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for
our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our
mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- Trolling, insulting or derogatory comments, and personal or
political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in
a professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our
standards of acceptable behavior and will take appropriate and fair
corrective action in response to any behavior that they deem
inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit,
or reject comments, commits, code, wiki edits, issues, and other
contributions that are not aligned to this Code of Conduct, and will
communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also
applies when an individual is officially representing the community in
public spaces. Examples of representing our community include using an
official e-mail address, posting via an official social media account,
or acting as an appointed representative at an online or offline
event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported to the community leaders responsible for enforcement
at our Discord channel. All complaints
will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and
security of the reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in
determining the consequences for any action they deem in violation of
this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior
deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders,
providing clarity around the nature of the violation and an
explanation of why the behavior was inappropriate. A public apology
may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued
behavior. No interaction with the people involved, including
unsolicited interaction with those enforcing the Code of Conduct, for
a specified period of time. This includes avoiding interactions in
community spaces as well as external channels like social
media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards,
including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or
public communication with the community for a specified period of
time. No public or private interaction with the people involved,
including unsolicited interaction with those enforcing the Code of
Conduct, is allowed during this period. Violating these terms may lead
to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of
community standards, including sustained inappropriate behavior,
harassment of an individual, or aggression toward or disparagement of
classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction
within the community.
## Attribution
This Code of Conduct is adapted from the [Contributor
Covenant][homepage], version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of
conduct enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the
FAQ at https://www.contributor-covenant.org/faq. Translations are
available at https://www.contributor-covenant.org/translations.

View File

@@ -1,5 +1,5 @@
# Builder image
FROM docker.io/golang:1.18.0-bullseye AS build
FROM docker.io/golang:1.19.0-bullseye AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale

View File

@@ -1,5 +1,5 @@
# Builder image
FROM docker.io/golang:1.18.0-alpine AS build
FROM docker.io/golang:1.19.0-alpine AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale

View File

@@ -1,5 +1,5 @@
# Builder image
FROM docker.io/golang:1.18.0-bullseye AS build
FROM docker.io/golang:1.19.0-bullseye AS build
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale

View File

@@ -7,7 +7,9 @@ RUN apt-get update \
RUN git clone https://github.com/tailscale/tailscale.git
WORKDIR tailscale
WORKDIR /go/tailscale
RUN git checkout main
RUN sh build_dist.sh tailscale.com/cmd/tailscale
RUN sh build_dist.sh tailscale.com/cmd/tailscaled

View File

@@ -1,4 +1,4 @@
# headscale
![headscale logo](./docs/logo/headscale3_header_stacked_left.png)
![ci](https://github.com/juanfont/headscale/actions/workflows/test.yml/badge.svg)
@@ -269,6 +269,13 @@ make build
<sub style="font-size:14px"><b>Eugen Biegler</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/617a7a>
<img src=https://avatars.githubusercontent.com/u/67651251?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Azz/>
<br />
<sub style="font-size:14px"><b>Azz</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/iSchluff>
<img src=https://avatars.githubusercontent.com/u/1429641?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Anton Schubert/>
@@ -283,6 +290,15 @@ make build
<sub style="font-size:14px"><b>Aaron Bieber</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/Aluxima>
<img src=https://avatars.githubusercontent.com/u/16262531?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Laurent Marchaud/>
<br />
<sub style="font-size:14px"><b>Laurent Marchaud</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/fdelucchijr>
<img src=https://avatars.githubusercontent.com/u/69133647?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Fernando De Lucchi/>
@@ -290,8 +306,6 @@ make build
<sub style="font-size:14px"><b>Fernando De Lucchi</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/hdhoang>
<img src=https://avatars.githubusercontent.com/u/12537?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Hoàng Đức Hiếu/>
@@ -320,6 +334,8 @@ make build
<sub style="font-size:14px"><b>ChibangLW</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/mevansam>
<img src=https://avatars.githubusercontent.com/u/403630?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Mevan Samaratunga/>
@@ -334,8 +350,6 @@ make build
<sub style="font-size:14px"><b>Michael G.</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/ptman>
<img src=https://avatars.githubusercontent.com/u/24669?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Paul Tötterman/>
@@ -364,6 +378,8 @@ make build
<sub style="font-size:14px"><b>Artem Klevtsov</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/cmars>
<img src=https://avatars.githubusercontent.com/u/23741?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Casey Marshall/>
@@ -378,8 +394,6 @@ make build
<sub style="font-size:14px"><b>Pavlos Vinieratos</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/SilverBut>
<img src=https://avatars.githubusercontent.com/u/6560655?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Silver Bullet/>
@@ -387,6 +401,13 @@ make build
<sub style="font-size:14px"><b>Silver Bullet</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/vtrf>
<img src=https://avatars.githubusercontent.com/u/25647735?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Victor Freire/>
<br />
<sub style="font-size:14px"><b>Victor Freire</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/lachy2849>
<img src=https://avatars.githubusercontent.com/u/98844035?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=lachy2849/>
@@ -401,6 +422,8 @@ make build
<sub style="font-size:14px"><b>thomas</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/aberoham>
<img src=https://avatars.githubusercontent.com/u/586805?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Abraham Ingersoll/>
@@ -422,8 +445,6 @@ make build
<sub style="font-size:14px"><b>Aofei Sheng</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/awoimbee>
<img src=https://avatars.githubusercontent.com/u/22431493?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Arthur Woimbée/>
@@ -445,6 +466,8 @@ make build
<sub style="font-size:14px"><b> Carson Yang</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/kundel>
<img src=https://avatars.githubusercontent.com/u/10158899?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=kundel/>
@@ -466,8 +489,6 @@ make build
<sub style="font-size:14px"><b>Felix Yan</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/JJGadgets>
<img src=https://avatars.githubusercontent.com/u/5709019?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=JJGadgets/>
@@ -489,6 +510,8 @@ make build
<sub style="font-size:14px"><b>Jim Tittsler</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/piec>
<img src=https://avatars.githubusercontent.com/u/781471?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pierre Carru/>
@@ -510,8 +533,6 @@ make build
<sub style="font-size:14px"><b>rcursaru</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/renovate-bot>
<img src=https://avatars.githubusercontent.com/u/25180681?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=WhiteSource Renovate/>
@@ -533,6 +554,8 @@ make build
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/sophware>
<img src=https://avatars.githubusercontent.com/u/41669?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=sophware/>
@@ -554,8 +577,6 @@ make build
<sub style="font-size:14px"><b>Teteros</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/gitter-badger>
<img src=https://avatars.githubusercontent.com/u/8518239?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=The Gitter Badger/>
@@ -577,6 +598,8 @@ make build
<sub style="font-size:14px"><b>Tjerk Woudsma</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/y0ngb1n>
<img src=https://avatars.githubusercontent.com/u/25719408?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Yang Bin/>
@@ -584,6 +607,13 @@ make build
<sub style="font-size:14px"><b>Yang Bin</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/gozssky>
<img src=https://avatars.githubusercontent.com/u/17199941?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Yujie Xia/>
<br />
<sub style="font-size:14px"><b>Yujie Xia</b></sub>
</a>
</td>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/zekker6>
<img src=https://avatars.githubusercontent.com/u/1367798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Zakhar Bessarab/>
@@ -598,8 +628,6 @@ make build
<sub style="font-size:14px"><b>Ziyuan Han</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/derelm>
<img src=https://avatars.githubusercontent.com/u/465155?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=derelm/>
@@ -614,6 +642,8 @@ make build
<sub style="font-size:14px"><b>henning mueller</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/ignoramous>
<img src=https://avatars.githubusercontent.com/u/852289?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=ignoramous/>
@@ -642,8 +672,6 @@ make build
<sub style="font-size:14px"><b>Wakeful-Cloud</b></sub>
</a>
</td>
</tr>
<tr>
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
<a href=https://github.com/xpzouying>
<img src=https://avatars.githubusercontent.com/u/3946563?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=zy/>

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"net/netip"
"os"
"path/filepath"
"strconv"
@@ -13,7 +14,6 @@ import (
"github.com/rs/zerolog/log"
"github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr"
"tailscale.com/tailcfg"
)
@@ -394,13 +394,13 @@ func expandAlias(
}
// if alias is an IP
ip, err := netaddr.ParseIP(alias)
ip, err := netip.ParseAddr(alias)
if err == nil {
return []string{ip.String()}, nil
}
// if alias is an CIDR
cidr, err := netaddr.ParseIPPrefix(alias)
cidr, err := netip.ParsePrefix(alias)
if err == nil {
return []string{cidr.String()}, nil
}

View File

@@ -2,11 +2,11 @@ package headscale
import (
"errors"
"net/netip"
"reflect"
"testing"
"gopkg.in/check.v1"
"inet.af/netaddr"
"tailscale.com/tailcfg"
)
@@ -131,7 +131,7 @@ func (s *Suite) TestValidExpandTagOwnersInSources(c *check.C) {
NodeKey: "bar",
DiscoKey: "faa",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
@@ -181,7 +181,7 @@ func (s *Suite) TestValidExpandTagOwnersInDestinations(c *check.C) {
NodeKey: "bar",
DiscoKey: "faa",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
@@ -231,7 +231,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
NodeKey: "bar",
DiscoKey: "faa",
Hostname: "testmachine",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
@@ -280,7 +280,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
NodeKey: "bar",
DiscoKey: "faa",
Hostname: "webserver",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
@@ -299,7 +299,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
NodeKey: "bar2",
DiscoKey: "faab",
Hostname: "user",
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
@@ -825,7 +825,6 @@ func Test_listMachinesInNamespace(t *testing.T) {
}
}
// nolint
func Test_expandAlias(t *testing.T) {
type args struct {
machines []Machine
@@ -844,10 +843,10 @@ func Test_expandAlias(t *testing.T) {
args: args{
alias: "*",
machines: []Machine{
{IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")}},
{IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")}},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.78.84.227"),
netip.MustParseAddr("100.78.84.227"),
},
},
},
@@ -864,25 +863,25 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -902,25 +901,25 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -951,7 +950,7 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{},
aclPolicy: ACLPolicy{
Hosts: Hosts{
"homeNetwork": netaddr.MustParseIPPrefix("192.168.1.0/24"),
"homeNetwork": netip.MustParsePrefix("192.168.1.0/24"),
},
},
stripEmailDomain: true,
@@ -988,7 +987,7 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -999,7 +998,7 @@ func Test_expandAlias(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1010,13 +1009,13 @@ func Test_expandAlias(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1036,25 +1035,25 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -1077,27 +1076,27 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
ForcedTags: []string{"tag:hr-webserver"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
ForcedTags: []string{"tag:hr-webserver"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -1115,14 +1114,14 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
ForcedTags: []string{"tag:hr-webserver"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1133,13 +1132,13 @@ func Test_expandAlias(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -1161,7 +1160,7 @@ func Test_expandAlias(t *testing.T) {
machines: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1172,7 +1171,7 @@ func Test_expandAlias(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1183,13 +1182,13 @@ func Test_expandAlias(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "marc"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1245,7 +1244,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
nodes: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1256,7 +1255,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1267,7 +1266,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1277,7 +1276,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
want: []Machine{
{
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")},
Namespace: Namespace{Name: "joe"},
},
},
@@ -1296,7 +1295,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
nodes: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1307,7 +1306,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1318,7 +1317,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1328,7 +1327,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
want: []Machine{
{
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")},
Namespace: Namespace{Name: "joe"},
},
},
@@ -1342,7 +1341,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
nodes: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1353,14 +1352,14 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
ForcedTags: []string{"tag:accountant-webserver"},
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1370,7 +1369,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
want: []Machine{
{
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.4")},
Namespace: Namespace{Name: "joe"},
},
},
@@ -1384,7 +1383,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
nodes: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1395,7 +1394,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1406,7 +1405,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -1417,7 +1416,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
want: []Machine{
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1428,7 +1427,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "joe"},
HostInfo: HostInfo{
@@ -1439,7 +1438,7 @@ func Test_excludeCorrectlyTaggedNodes(t *testing.T) {
},
{
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.4"),
netip.MustParseAddr("100.64.0.4"),
},
Namespace: Namespace{Name: "joe"},
},

View File

@@ -2,11 +2,11 @@ package headscale
import (
"encoding/json"
"net/netip"
"strings"
"github.com/tailscale/hujson"
"gopkg.in/yaml.v3"
"inet.af/netaddr"
)
// ACLPolicy represents a Tailscale ACL Policy.
@@ -30,7 +30,7 @@ type ACL struct {
type Groups map[string][]string
// Hosts are alias for IP addresses or subnets.
type Hosts map[string]netaddr.IPPrefix
type Hosts map[string]netip.Prefix
// TagOwners specify what users (namespaces?) are allow to use certain tags.
type TagOwners map[string][]string
@@ -42,7 +42,7 @@ type ACLTest struct {
Deny []string `json:"deny,omitempty" yaml:"deny,omitempty"`
}
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects.
// UnmarshalJSON allows to parse the Hosts directly into netip objects.
func (hosts *Hosts) UnmarshalJSON(data []byte) error {
newHosts := Hosts{}
hostIPPrefixMap := make(map[string]string)
@@ -60,7 +60,7 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error {
if !strings.Contains(prefixStr, "/") {
prefixStr += "/32"
}
prefix, err := netaddr.ParseIPPrefix(prefixStr)
prefix, err := netip.ParsePrefix(prefixStr)
if err != nil {
return err
}
@@ -71,7 +71,7 @@ func (hosts *Hosts) UnmarshalJSON(data []byte) error {
return nil
}
// UnmarshalYAML allows to parse the Hosts directly into netaddr objects.
// UnmarshalYAML allows to parse the Hosts directly into netip objects.
func (hosts *Hosts) UnmarshalYAML(data []byte) error {
newHosts := Hosts{}
hostIPPrefixMap := make(map[string]string)
@@ -81,7 +81,7 @@ func (hosts *Hosts) UnmarshalYAML(data []byte) error {
return err
}
for host, prefixStr := range hostIPPrefixMap {
prefix, err := netaddr.ParseIPPrefix(prefixStr)
prefix, err := netip.ParsePrefix(prefixStr)
if err != nil {
return err
}

779
api.go
View File

@@ -2,22 +2,13 @@ package headscale
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"html/template"
"io"
"net/http"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/klauspost/compress/zstd"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
const (
@@ -61,7 +52,7 @@ func (h *Headscale) HealthHandler(
}
}
if err := h.pingDB(); err != nil {
if err := h.pingDB(req.Context()); err != nil {
respond(err)
return
@@ -70,23 +61,6 @@ func (h *Headscale) HealthHandler(
respond(nil)
}
// KeyHandler provides the Headscale pub key
// Listens in /key.
func (h *Headscale) KeyHandler(
writer http.ResponseWriter,
req *http.Request,
) {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err := writer.Write([]byte(MachinePublicKeyStripPrefix(h.privateKey.Public())))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
type registerWebAPITemplateConfig struct {
Key string
}
@@ -164,754 +138,3 @@ func (h *Headscale) RegisterWebAPI(
Msg("Failed to write response")
}
}
// RegistrationHandler handles the actual registration process of a machine
// Endpoint /machine/:mkey.
func (h *Headscale) RegistrationHandler(
writer http.ResponseWriter,
req *http.Request,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "RegistrationHandler").
Msg("No machine ID in request")
http.Error(writer, "No machine ID in request", http.StatusBadRequest)
return
}
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr)))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse machine key")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot parse machine key", http.StatusBadRequest)
return
}
registerRequest := tailcfg.RegisterRequest{}
err = decode(body, &registerRequest, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot decode message")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
now := time.Now().UTC()
machine, err := h.GetMachineByMachineKey(machineKey)
if errors.Is(err, gorm.ErrRecordNotFound) {
machineKeyStr := MachinePublicKeyStripPrefix(machineKey)
// If the machine has AuthKey set, handle registration via PreAuthKeys
if registerRequest.Auth.AuthKey != "" {
h.handleAuthKey(writer, req, machineKey, registerRequest)
return
}
// Check if the node is waiting for interactive login.
//
// TODO(juan): We could use this field to improve our protocol implementation,
// and hold the request until the client closes it, or the interactive
// login is completed (i.e., the user registers the machine).
// This is not implemented yet, as it is no strictly required. The only side-effect
// is that the client will hammer headscale with requests until it gets a
// successful RegisterResponse.
if registerRequest.Followup != "" {
if _, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(registerRequest.NodeKey)); ok {
log.Debug().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Msg("Machine is waiting for interactive login")
ticker := time.NewTicker(registrationHoldoff)
select {
case <-req.Context().Done():
return
case <-ticker.C:
h.handleMachineRegistrationNew(writer, req, machineKey, registerRequest)
return
}
}
}
log.Info().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Msg("New machine not yet in the database")
givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err)
return
}
// The machine did not have a key to authenticate, which means
// that we rely on a method that calls back some how (OpenID or CLI)
// We create the machine and then keep it around until a callback
// happens
newMachine := Machine{
MachineKey: machineKeyStr,
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
NodeKey: NodePublicKeyStripPrefix(registerRequest.NodeKey),
LastSeen: &now,
Expiry: &time.Time{},
}
if !registerRequest.Expiry.IsZero() {
log.Trace().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Time("expiry", registerRequest.Expiry).
Msg("Non-zero expiry time requested")
newMachine.Expiry = &registerRequest.Expiry
}
h.registrationCache.Set(
newMachine.NodeKey,
newMachine,
registerCacheExpiration,
)
h.handleMachineRegistrationNew(writer, req, machineKey, registerRequest)
return
}
// The machine is already registered, so we need to pass through reauth or key update.
if machine != nil {
// If the NodeKey stored in headscale is the same as the key presented in a registration
// request, then we have a node that is either:
// - Trying to log out (sending a expiry in the past)
// - A valid, registered machine, looking for the node map
// - Expired machine wanting to reauthenticate
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.NodeKey) {
// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
// https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
if !registerRequest.Expiry.IsZero() &&
registerRequest.Expiry.UTC().Before(now) {
h.handleMachineLogOut(writer, req, machineKey, *machine)
return
}
// If machine is not expired, and is register, we have a already accepted this machine,
// let it proceed with a valid registration
if !machine.isExpired() {
h.handleMachineValidRegistration(writer, req, machineKey, *machine)
return
}
}
// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.OldNodeKey) &&
!machine.isExpired() {
h.handleMachineRefreshKey(
writer,
req,
machineKey,
registerRequest,
*machine,
)
return
}
// The machine has expired
h.handleMachineExpired(writer, req, machineKey, registerRequest, *machine)
return
}
}
func (h *Headscale) getMapResponse(
machineKey key.MachinePublic,
mapRequest tailcfg.MapRequest,
machine *Machine,
) ([]byte, error) {
log.Trace().
Str("func", "getMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
Msg("Creating Map response")
node, err := machine.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Caller().
Str("func", "getMapResponse").
Err(err).
Msg("Cannot convert to node")
return nil, err
}
peers, err := h.getValidPeers(machine)
if err != nil {
log.Error().
Caller().
Str("func", "getMapResponse").
Err(err).
Msg("Cannot fetch peers")
return nil, err
}
profiles := getMapResponseUserProfiles(*machine, peers)
nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Caller().
Str("func", "getMapResponse").
Err(err).
Msg("Failed to convert peers to Tailscale nodes")
return nil, err
}
dnsConfig := getMapResponseDNSConfig(
h.cfg.DNSConfig,
h.cfg.BaseDomain,
*machine,
peers,
)
resp := tailcfg.MapResponse{
KeepAlive: false,
Node: node,
Peers: nodePeers,
DNSConfig: dnsConfig,
Domain: h.cfg.BaseDomain,
PacketFilter: h.aclRules,
DERPMap: h.DERPMap,
UserProfiles: profiles,
Debug: &tailcfg.Debug{
DisableLogTail: !h.cfg.LogTail.Enabled,
RandomizeClientPort: h.cfg.RandomizeClientPort,
},
}
log.Trace().
Str("func", "getMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
// Interface("payload", resp).
Msgf("Generated map response: %s", tailMapResponseToString(resp))
var respBody []byte
if mapRequest.Compress == "zstd" {
src, err := json.Marshal(resp)
if err != nil {
log.Error().
Caller().
Str("func", "getMapResponse").
Err(err).
Msg("Failed to marshal response for the client")
return nil, err
}
encoder, _ := zstd.NewWriter(nil)
srcCompressed := encoder.EncodeAll(src, nil)
respBody = h.privateKey.SealTo(machineKey, srcCompressed)
} else {
respBody, err = encode(resp, &machineKey, h.privateKey)
if err != nil {
return nil, err
}
}
// declare the incoming size on the first 4 bytes
data := make([]byte, reservedResponseHeaderSize)
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
data = append(data, respBody...)
return data, nil
}
func (h *Headscale) getMapKeepAliveResponse(
machineKey key.MachinePublic,
mapRequest tailcfg.MapRequest,
) ([]byte, error) {
mapResponse := tailcfg.MapResponse{
KeepAlive: true,
}
var respBody []byte
var err error
if mapRequest.Compress == "zstd" {
src, err := json.Marshal(mapResponse)
if err != nil {
log.Error().
Caller().
Str("func", "getMapKeepAliveResponse").
Err(err).
Msg("Failed to marshal keepalive response for the client")
return nil, err
}
encoder, _ := zstd.NewWriter(nil)
srcCompressed := encoder.EncodeAll(src, nil)
respBody = h.privateKey.SealTo(machineKey, srcCompressed)
} else {
respBody, err = encode(mapResponse, &machineKey, h.privateKey)
if err != nil {
return nil, err
}
}
data := make([]byte, reservedResponseHeaderSize)
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
data = append(data, respBody...)
return data, nil
}
func (h *Headscale) handleMachineLogOut(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
machine Machine,
) {
resp := tailcfg.RegisterResponse{}
log.Info().
Str("machine", machine.Hostname).
Msg("Client requested logout")
err := h.ExpireMachine(&machine)
if err != nil {
log.Error().
Caller().
Str("func", "handleMachineLogOut").
Err(err).
Msg("Failed to expire machine")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.AuthURL = ""
resp.MachineAuthorized = false
resp.User = *machine.Namespace.toUser()
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
func (h *Headscale) handleMachineValidRegistration(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
machine Machine,
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is valid, respond with redirect to /map
log.Debug().
Str("machine", machine.Hostname).
Msg("Client is registered and we have the current NodeKey. All clear to /map")
resp.AuthURL = ""
resp.MachineAuthorized = true
resp.User = *machine.Namespace.toUser()
resp.Login = *machine.Namespace.toLogin()
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
func (h *Headscale) handleMachineExpired(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest,
machine Machine,
) {
resp := tailcfg.RegisterResponse{}
// The client has registered before, but has expired
log.Debug().
Str("machine", machine.Hostname).
Msg("Machine registration has expired. Sending a authurl to register")
if registerRequest.Auth.AuthKey != "" {
h.handleAuthKey(writer, req, machineKey, registerRequest)
return
}
if h.cfg.OIDC.Issuer != "" {
resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
func (h *Headscale) handleMachineRefreshKey(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest,
machine Machine,
) {
resp := tailcfg.RegisterResponse{}
log.Debug().
Str("machine", machine.Hostname).
Msg("We have the OldNodeKey in the database. This is a key refresh")
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
if err := h.db.Save(&machine).Error; err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to update machine key in the database")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.AuthURL = ""
resp.User = *machine.Namespace.toUser()
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
func (h *Headscale) handleMachineRegistrationNew(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest,
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is new, redirect the client to the registration URL
log.Debug().
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("The node seems to be new, sending auth url")
if h.cfg.OIDC.Issuer != "" {
resp.AuthURL = fmt.Sprintf(
"%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey),
)
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
// TODO: check if any locks are needed around IP allocation.
func (h *Headscale) handleAuthKey(
writer http.ResponseWriter,
req *http.Request,
machineKey key.MachinePublic,
registerRequest tailcfg.RegisterRequest,
) {
machineKeyStr := MachinePublicKeyStripPrefix(machineKey)
log.Debug().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{}
pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey)
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusUnauthorized)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
log.Error().
Caller().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Failed authentication via AuthKey")
if pak != nil {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
} else {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc()
}
return
}
log.Debug().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Authentication key was valid, proceeding to acquire IP addresses")
nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
// retrieve machine information if it exist
// The error is not important, because if it does not
// exist, then this is a new machine and we will move
// on to registration.
machine, _ := h.GetMachineByMachineKey(machineKey)
if machine != nil {
log.Trace().
Caller().
Str("machine", machine.Hostname).
Msg("machine already registered, refreshing with new auth key")
machine.NodeKey = nodeKey
machine.AuthKeyID = uint(pak.ID)
err := h.RefreshMachine(machine, registerRequest.Expiry)
if err != nil {
log.Error().
Caller().
Str("machine", machine.Hostname).
Err(err).
Msg("Failed to refresh machine")
return
}
} else {
now := time.Now().UTC()
givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err)
return
}
machineToRegister := Machine{
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
NamespaceID: pak.Namespace.ID,
MachineKey: machineKeyStr,
RegisterMethod: RegisterMethodAuthKey,
Expiry: &registerRequest.Expiry,
NodeKey: nodeKey,
LastSeen: &now,
AuthKeyID: uint(pak.ID),
}
machine, err = h.RegisterMachine(
machineToRegister,
)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("could not register machine")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
}
err = h.UsePreAuthKey(pak)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to use pre-auth key")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser()
respBody, err := encode(resp, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
log.Info().
Str("func", "handleAuthKey").
Str("machine", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")).
Msg("Successfully authenticated via AuthKey")
}

80
api_common.go Normal file
View File

@@ -0,0 +1,80 @@
package headscale
import (
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
)
func (h *Headscale) generateMapResponse(
mapRequest tailcfg.MapRequest,
machine *Machine,
) (*tailcfg.MapResponse, error) {
log.Trace().
Str("func", "generateMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
Msg("Creating Map response")
node, err := machine.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Caller().
Str("func", "generateMapResponse").
Err(err).
Msg("Cannot convert to node")
return nil, err
}
peers, err := h.getValidPeers(machine)
if err != nil {
log.Error().
Caller().
Str("func", "generateMapResponse").
Err(err).
Msg("Cannot fetch peers")
return nil, err
}
profiles := getMapResponseUserProfiles(*machine, peers)
nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Caller().
Str("func", "generateMapResponse").
Err(err).
Msg("Failed to convert peers to Tailscale nodes")
return nil, err
}
dnsConfig := getMapResponseDNSConfig(
h.cfg.DNSConfig,
h.cfg.BaseDomain,
*machine,
peers,
)
resp := tailcfg.MapResponse{
KeepAlive: false,
Node: node,
Peers: nodePeers,
DNSConfig: dnsConfig,
Domain: h.cfg.BaseDomain,
PacketFilter: h.aclRules,
DERPMap: h.DERPMap,
UserProfiles: profiles,
Debug: &tailcfg.Debug{
DisableLogTail: !h.cfg.LogTail.Enabled,
RandomizeClientPort: h.cfg.RandomizeClientPort,
},
}
log.Trace().
Str("func", "generateMapResponse").
Str("machine", mapRequest.Hostinfo.Hostname).
// Interface("payload", resp).
Msgf("Generated map response: %s", tailMapResponseToString(resp))
return &resp, nil
}

105
app.go
View File

@@ -18,7 +18,7 @@ import (
"github.com/coreos/go-oidc/v3/oidc"
"github.com/gorilla/mux"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/patrickmn/go-cache"
@@ -51,6 +51,10 @@ const (
errUnsupportedLetsEncryptChallengeType = Error(
"unknown value for Lets Encrypt challenge type",
)
ErrFailedPrivateKey = Error("failed to read or create private key")
ErrFailedNoisePrivateKey = Error("failed to read or create Noise protocol private key")
ErrSamePrivateKeys = Error("private key and noise private key are the same")
)
const (
@@ -72,12 +76,15 @@ const (
// Headscale represents the base app of the service.
type Headscale struct {
cfg *Config
db *gorm.DB
dbString string
dbType string
dbDebug bool
privateKey *key.MachinePrivate
cfg *Config
db *gorm.DB
dbString string
dbType string
dbDebug bool
privateKey *key.MachinePrivate
noisePrivateKey *key.MachinePrivate
noiseMux *mux.Router
DERPMap *tailcfg.DERPMap
DERPServer *DERPServer
@@ -120,22 +127,42 @@ func LookupTLSClientAuthMode(mode string) (tls.ClientAuthType, bool) {
}
func NewHeadscale(cfg *Config) (*Headscale, error) {
privKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
privateKey, err := readOrCreatePrivateKey(cfg.PrivateKeyPath)
if err != nil {
return nil, fmt.Errorf("failed to read or create private key: %w", err)
return nil, ErrFailedPrivateKey
}
// TS2021 requires to have a different key from the legacy protocol.
noisePrivateKey, err := readOrCreatePrivateKey(cfg.NoisePrivateKeyPath)
if err != nil {
return nil, ErrFailedNoisePrivateKey
}
if privateKey.Equal(*noisePrivateKey) {
return nil, ErrSamePrivateKeys
}
var dbString string
switch cfg.DBtype {
case Postgres:
dbString = fmt.Sprintf(
"host=%s port=%d dbname=%s user=%s password=%s sslmode=disable",
"host=%s dbname=%s user=%s",
cfg.DBhost,
cfg.DBport,
cfg.DBname,
cfg.DBuser,
cfg.DBpass,
)
if !cfg.DBssl {
dbString += " sslmode=disable"
}
if cfg.DBport != 0 {
dbString += fmt.Sprintf(" port=%d", cfg.DBport)
}
if cfg.DBpass != "" {
dbString += fmt.Sprintf(" password=%s", cfg.DBpass)
}
case Sqlite:
dbString = cfg.DBpath
default:
@@ -151,7 +178,8 @@ func NewHeadscale(cfg *Config) (*Headscale, error) {
cfg: cfg,
dbType: cfg.DBtype,
dbString: dbString,
privateKey: privKey,
privateKey: privateKey,
noisePrivateKey: noisePrivateKey,
aclRules: tailcfg.FilterAllowAll, // default allowall
registrationCache: registrationCache,
pollNetMapStreamWG: sync.WaitGroup{},
@@ -247,7 +275,7 @@ func (h *Headscale) expireEphemeralNodesWorker() {
}
if expiredFound {
h.setLastStateChangeToNow(namespace.Name)
h.setLastStateChangeToNow()
}
}
}
@@ -415,6 +443,8 @@ func (h *Headscale) ensureUnixSocketIsAbsent() error {
func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
router := mux.NewRouter()
router.HandleFunc(ts2021UpgradePath, h.NoiseUpgradeHandler).Methods(http.MethodPost)
router.HandleFunc("/health", h.HealthHandler).Methods(http.MethodGet)
router.HandleFunc("/key", h.KeyHandler).Methods(http.MethodGet)
router.HandleFunc("/register/{nkey}", h.RegisterWebAPI).Methods(http.MethodGet)
@@ -444,6 +474,15 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
return router
}
func (h *Headscale) createNoiseMux() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/machine/register", h.NoiseRegistrationHandler).Methods(http.MethodPost)
router.HandleFunc("/machine/map", h.NoisePollNetMapHandler)
return router
}
// Serve launches a GIN server with the Headscale API.
func (h *Headscale) Serve() error {
var err error
@@ -562,7 +601,7 @@ func (h *Headscale) Serve() error {
grpcOptions := []grpc.ServerOption{
grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
grpcMiddleware.ChainUnaryServer(
h.grpcAuthenticationInterceptor,
zerolog.NewUnaryServerInterceptor(),
),
@@ -597,9 +636,16 @@ func (h *Headscale) Serve() error {
//
// HTTP setup
//
// This is the regular router that we expose
// over our main Addr. It also serves the legacy Tailcale API
router := h.createRouter(grpcGatewayMux)
// This router is served only over the Noise connection, and exposes only the new API.
//
// The HTTP2 server that exposes this router is created for
// a single hijacked connection from /ts2021, using netutil.NewOneConnListener
h.noiseMux = h.createNoiseMux()
httpServer := &http.Server{
Addr: h.cfg.Addr,
Handler: router,
@@ -774,10 +820,19 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
// Configuration via autocert with HTTP-01. This requires listening on
// port 80 for the certificate validation in addition to the headscale
// service, which can be configured to run on any other port.
server := &http.Server{
Addr: h.cfg.TLS.LetsEncrypt.Listen,
Handler: certManager.HTTPHandler(http.HandlerFunc(h.redirect)),
ReadTimeout: HTTPReadTimeout,
}
err := server.ListenAndServe()
go func() {
log.Fatal().
Caller().
Err(http.ListenAndServe(h.cfg.TLS.LetsEncrypt.Listen, certManager.HTTPHandler(http.HandlerFunc(h.redirect)))).
Err(err).
Msg("failed to set up a HTTP server")
}()
@@ -814,19 +869,17 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
}
}
func (h *Headscale) setLastStateChangeToNow(namespaces ...string) {
func (h *Headscale) setLastStateChangeToNow() {
var err error
now := time.Now().UTC()
if len(namespaces) == 0 {
namespaces, err = h.ListNamespacesStr()
if err != nil {
log.Error().
Caller().
Err(err).
Msg("failed to fetch all namespaces, failing to update last changed state.")
}
namespaces, err := h.ListNamespacesStr()
if err != nil {
log.Error().
Caller().
Err(err).
Msg("failed to fetch all namespaces, failing to update last changed state.")
}
for _, namespace := range namespaces {

View File

@@ -1,11 +1,11 @@
package headscale
import (
"net/netip"
"os"
"testing"
"gopkg.in/check.v1"
"inet.af/netaddr"
)
func Test(t *testing.T) {
@@ -39,8 +39,8 @@ func (s *Suite) ResetDB(c *check.C) {
c.Fatal(err)
}
cfg := Config{
IPPrefixes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.27.0.0/23"),
IPPrefixes: []netip.Prefix{
netip.MustParsePrefix("10.27.0.0/23"),
},
}

View File

@@ -3,6 +3,7 @@ package cli
import (
"fmt"
"log"
"net/netip"
"strconv"
"strings"
"time"
@@ -13,7 +14,6 @@ import (
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"google.golang.org/grpc/status"
"inet.af/netaddr"
"tailscale.com/types/key"
)
@@ -557,7 +557,7 @@ func nodesToPtables(
var IPV4Address string
var IPV6Address string
for _, addr := range machine.IpAddresses {
if netaddr.MustParseIP(addr).Is4() {
if netip.MustParseAddr(addr).Is4() {
IPV4Address = addr
} else {
IPV6Address = addr

View File

@@ -25,15 +25,18 @@ func init() {
}
func initConfig() {
if cfgFile == "" {
cfgFile = os.Getenv("HEADSCALE_CONFIG")
}
if cfgFile != "" {
err := headscale.LoadConfig(cfgFile, true)
if err != nil {
log.Fatal().Caller().Err(err)
log.Fatal().Caller().Err(err).Msgf("Error loading config file %s", cfgFile)
}
} else {
err := headscale.LoadConfig("", false)
if err != nil {
log.Fatal().Caller().Err(err)
log.Fatal().Caller().Err(err).Msgf("Error loading config")
}
}
@@ -44,7 +47,7 @@ func initConfig() {
machineOutput := HasMachineOutputFlag()
zerolog.SetGlobalLevel(cfg.LogLevel)
zerolog.SetGlobalLevel(cfg.Log.Level)
// If the user has requested a "machine" readable format,
// then disable login so the output remains valid.
@@ -52,6 +55,10 @@ func initConfig() {
zerolog.SetGlobalLevel(zerolog.Disabled)
}
if cfg.Log.Format == headscale.JSONLogFormat {
log.Logger = log.Output(os.Stdout)
}
if !cfg.DisableUpdateCheck && !machineOutput {
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") &&
Version != "dev" {

View File

@@ -163,10 +163,12 @@ func (*Suite) TestTLSConfigValidation(c *check.C) {
c.Fatal(err)
}
// defer os.RemoveAll(tmpDir)
configYaml := []byte(
"---\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"\"\ntls_cert_path: \"abc.pem\"",
)
configYaml := []byte(`---
tls_letsencrypt_hostname: example.com
tls_letsencrypt_challenge_type: ""
tls_cert_path: abc.pem
noise:
private_key_path: noise_private.key`)
writeConfig(c, tmpDir, configYaml)
// Check configuration validation errors (1)
@@ -191,9 +193,13 @@ func (*Suite) TestTLSConfigValidation(c *check.C) {
)
// Check configuration validation errors (2)
configYaml = []byte(
"---\nserver_url: \"http://127.0.0.1:8080\"\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"TLS-ALPN-01\"",
)
configYaml = []byte(`---
noise:
private_key_path: noise_private.key
server_url: http://127.0.0.1:8080
tls_letsencrypt_hostname: example.com
tls_letsencrypt_challenge_type: TLS-ALPN-01
`)
writeConfig(c, tmpDir, configYaml)
err = headscale.LoadConfig(tmpDir, false)
c.Assert(err, check.IsNil)

View File

@@ -41,6 +41,15 @@ grpc_allow_insecure: false
# autogenerated if it's missing
private_key_path: /var/lib/headscale/private.key
# The Noise section includes specific configuration for the
# TS2021 Noise procotol
noise:
# The Noise private key is used to encrypt the
# traffic between headscale and Tailscale clients when
# using the new Noise-based protocol. It must be different
# from the legacy private key.
private_key_path: /var/lib/headscale/noise_private.key
# List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash.
@@ -114,12 +123,14 @@ db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
# # Postgres config
# If using a Unix socket to connect to Postgres, set the socket path in the 'host' field and leave 'port' blank.
# db_type: postgres
# db_host: localhost
# db_port: 5432
# db_name: headscale
# db_user: foo
# db_pass: bar
# db_ssl: false
### TLS configuration
#
@@ -161,7 +172,10 @@ tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
log_level: info
log:
# Output formatting for logs: text or json
format: text
level: info
# Path to a file containg ACL policies.
# ACLs can be defined as YAML or HUJSON.

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io/fs"
"net/netip"
"net/url"
"strings"
"time"
@@ -13,7 +14,7 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"inet.af/netaddr"
"go4.org/netipx"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
)
@@ -21,6 +22,9 @@ import (
const (
tlsALPN01ChallengeType = "TLS-ALPN-01"
http01ChallengeType = "HTTP-01"
JSONLogFormat = "json"
TextLogFormat = "text"
)
// Config contains the initial Headscale configuration.
@@ -32,10 +36,11 @@ type Config struct {
GRPCAllowInsecure bool
EphemeralNodeInactivityTimeout time.Duration
NodeUpdateCheckInterval time.Duration
IPPrefixes []netaddr.IPPrefix
IPPrefixes []netip.Prefix
PrivateKeyPath string
NoisePrivateKeyPath string
BaseDomain string
LogLevel zerolog.Level
Log LogConfig
DisableUpdateCheck bool
DERP DERPConfig
@@ -47,6 +52,7 @@ type Config struct {
DBname string
DBuser string
DBpass string
DBssl bool
TLS TLSConfig
@@ -121,6 +127,11 @@ type ACLConfig struct {
PolicyPath string
}
type LogConfig struct {
Format string
Level zerolog.Level
}
func LoadConfig(path string, isFile bool) error {
if isFile {
viper.SetConfigFile(path)
@@ -144,7 +155,8 @@ func LoadConfig(path string, isFile bool) error {
viper.SetDefault("tls_letsencrypt_challenge_type", http01ChallengeType)
viper.SetDefault("tls_client_auth_mode", "relaxed")
viper.SetDefault("log_level", "info")
viper.SetDefault("log.level", "info")
viper.SetDefault("log.format", TextLogFormat)
viper.SetDefault("dns_config", nil)
@@ -183,6 +195,10 @@ func LoadConfig(path string, isFile bool) error {
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
}
if !viper.IsSet("noise") || viper.GetString("noise.private_key_path") == "" {
errorText += "Fatal config error: headscale now requires a new `noise.private_key_path` field in the config file for the Tailscale v2 protocol\n"
}
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
(viper.GetString("tls_letsencrypt_challenge_type") == tlsALPN01ChallengeType) &&
(!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
@@ -327,6 +343,34 @@ func GetACLConfig() ACLConfig {
}
}
func GetLogConfig() LogConfig {
logLevelStr := viper.GetString("log.level")
logLevel, err := zerolog.ParseLevel(logLevelStr)
if err != nil {
logLevel = zerolog.DebugLevel
}
logFormatOpt := viper.GetString("log.format")
var logFormat string
switch logFormatOpt {
case "json":
logFormat = JSONLogFormat
case "text":
logFormat = TextLogFormat
case "":
logFormat = TextLogFormat
default:
log.Error().
Str("func", "GetLogConfig").
Msgf("Could not parse log format: %s. Valid choices are 'json' or 'text'", logFormatOpt)
}
return LogConfig{
Format: logFormat,
Level: logLevel,
}
}
func GetDNSConfig() (*tailcfg.DNSConfig, string) {
if viper.IsSet("dns_config") {
dnsConfig := &tailcfg.DNSConfig{}
@@ -334,11 +378,11 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
if viper.IsSet("dns_config.nameservers") {
nameserversStr := viper.GetStringSlice("dns_config.nameservers")
nameservers := make([]netaddr.IP, len(nameserversStr))
nameservers := make([]netip.Addr, len(nameserversStr))
resolvers := make([]*dnstype.Resolver, len(nameserversStr))
for index, nameserverStr := range nameserversStr {
nameserver, err := netaddr.ParseIP(nameserverStr)
nameserver, err := netip.ParseAddr(nameserverStr)
if err != nil {
log.Error().
Str("func", "getDNSConfig").
@@ -368,7 +412,7 @@ func GetDNSConfig() (*tailcfg.DNSConfig, string) {
len(restrictedNameservers),
)
for index, nameserverStr := range restrictedNameservers {
nameserver, err := netaddr.ParseIP(nameserverStr)
nameserver, err := netip.ParseAddr(nameserverStr)
if err != nil {
log.Error().
Str("func", "getDNSConfig").
@@ -421,13 +465,7 @@ func GetHeadscaleConfig() (*Config, error) {
randomizeClientPort := viper.GetBool("randomize_client_port")
configuredPrefixes := viper.GetStringSlice("ip_prefixes")
parsedPrefixes := make([]netaddr.IPPrefix, 0, len(configuredPrefixes)+1)
logLevelStr := viper.GetString("log_level")
logLevel, err := zerolog.ParseLevel(logLevelStr)
if err != nil {
logLevel = zerolog.DebugLevel
}
parsedPrefixes := make([]netip.Prefix, 0, len(configuredPrefixes)+1)
legacyPrefixField := viper.GetString("ip_prefix")
if len(legacyPrefixField) > 0 {
@@ -438,7 +476,7 @@ func GetHeadscaleConfig() (*Config, error) {
"use of 'ip_prefix' for configuration is deprecated",
"please see 'ip_prefixes' in the shipped example.",
)
legacyPrefix, err := netaddr.ParseIPPrefix(legacyPrefixField)
legacyPrefix, err := netip.ParsePrefix(legacyPrefixField)
if err != nil {
panic(fmt.Errorf("failed to parse ip_prefix: %w", err))
}
@@ -446,19 +484,19 @@ func GetHeadscaleConfig() (*Config, error) {
}
for i, prefixInConfig := range configuredPrefixes {
prefix, err := netaddr.ParseIPPrefix(prefixInConfig)
prefix, err := netip.ParsePrefix(prefixInConfig)
if err != nil {
panic(fmt.Errorf("failed to parse ip_prefixes[%d]: %w", i, err))
}
parsedPrefixes = append(parsedPrefixes, prefix)
}
prefixes := make([]netaddr.IPPrefix, 0, len(parsedPrefixes))
prefixes := make([]netip.Prefix, 0, len(parsedPrefixes))
{
// dedup
normalizedPrefixes := make(map[string]int, len(parsedPrefixes))
for i, p := range parsedPrefixes {
normalized, _ := p.Range().Prefix()
normalized, _ := netipx.RangeOfPrefix(p).Prefix()
normalizedPrefixes[normalized.String()] = i
}
@@ -469,7 +507,7 @@ func GetHeadscaleConfig() (*Config, error) {
}
if len(prefixes) < 1 {
prefixes = append(prefixes, netaddr.MustParseIPPrefix("100.64.0.0/10"))
prefixes = append(prefixes, netip.MustParsePrefix("100.64.0.0/10"))
log.Warn().
Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes)
}
@@ -481,12 +519,14 @@ func GetHeadscaleConfig() (*Config, error) {
GRPCAddr: viper.GetString("grpc_listen_addr"),
GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"),
DisableUpdateCheck: viper.GetBool("disable_check_updates"),
LogLevel: logLevel,
IPPrefixes: prefixes,
PrivateKeyPath: AbsolutePathFromConfigPath(
viper.GetString("private_key_path"),
),
NoisePrivateKeyPath: AbsolutePathFromConfigPath(
viper.GetString("noise.private_key_path"),
),
BaseDomain: baseDomain,
DERP: derpConfig,
@@ -506,6 +546,7 @@ func GetHeadscaleConfig() (*Config, error) {
DBname: viper.GetString("db_name"),
DBuser: viper.GetString("db_user"),
DBpass: viper.GetString("db_pass"),
DBssl: viper.GetBool("db_ssl"),
TLS: GetTLSConfig(),
@@ -539,5 +580,7 @@ func GetHeadscaleConfig() (*Config, error) {
},
ACL: GetACLConfig(),
Log: GetLogConfig(),
}, nil
}

8
db.go
View File

@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/netip"
"time"
"github.com/glebarez/sqlite"
@@ -13,7 +14,6 @@ import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"inet.af/netaddr"
"tailscale.com/tailcfg"
)
@@ -221,8 +221,8 @@ func (h *Headscale) setValue(key string, value string) error {
return nil
}
func (h *Headscale) pingDB() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
func (h *Headscale) pingDB(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
db, err := h.db.DB()
if err != nil {
@@ -259,7 +259,7 @@ func (hi HostInfo) Value() (driver.Value, error) {
return string(bytes), err
}
type IPPrefixes []netaddr.IPPrefix
type IPPrefixes []netip.Prefix
func (i *IPPrefixes) Scan(destination interface{}) error {
switch value := destination.(type) {

View File

@@ -34,7 +34,7 @@ func loadDERPMapFromURL(addr url.URL) (*tailcfg.DERPMap, error) {
ctx, cancel := context.WithTimeout(context.Background(), HTTPReadTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", addr.String(), nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, addr.String(), nil)
if err != nil {
return nil, err
}

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"net/url"
"strconv"
"strings"
@@ -98,10 +99,13 @@ func (h *Headscale) DERPHandler(
req *http.Request,
) {
log.Trace().Caller().Msgf("/derp request from %v", req.RemoteAddr)
up := strings.ToLower(req.Header.Get("Upgrade"))
if up != "websocket" && up != "derp" {
if up != "" {
log.Warn().Caller().Msgf("Weird websockets connection upgrade: %q", up)
upgrade := strings.ToLower(req.Header.Get("Upgrade"))
if upgrade != "websocket" && upgrade != "derp" {
if upgrade != "" {
log.Warn().
Caller().
Msg("No Upgrade header in DERP server request. If headscale is behind a reverse proxy, make sure it is configured to pass WebSockets through.")
}
writer.Header().Set("Content-Type", "text/plain")
writer.WriteHeader(http.StatusUpgradeRequired)
@@ -153,7 +157,7 @@ func (h *Headscale) DERPHandler(
if !fastStart {
pubKey := h.privateKey.Public()
pubKeyStr := pubKey.UntypedHexString() // nolint
pubKeyStr := pubKey.UntypedHexString() //nolint
fmt.Fprintf(conn, "HTTP/1.1 101 Switching Protocols\r\n"+
"Upgrade: DERP\r\n"+
"Connection: Upgrade\r\n"+
@@ -163,7 +167,7 @@ func (h *Headscale) DERPHandler(
pubKeyStr)
}
h.DERPServer.tailscaleDERP.Accept(netConn, conn, netConn.RemoteAddr().String())
h.DERPServer.tailscaleDERP.Accept(req.Context(), netConn, conn, netConn.RemoteAddr().String())
}
// DERPProbeHandler is the endpoint that js/wasm clients hit to measure
@@ -173,7 +177,7 @@ func (h *Headscale) DERPProbeHandler(
req *http.Request,
) {
switch req.Method {
case "HEAD", "GET":
case http.MethodHead, http.MethodGet:
writer.Header().Set("Access-Control-Allow-Origin", "*")
writer.WriteHeader(http.StatusOK)
default:
@@ -201,7 +205,7 @@ func (h *Headscale) DERPBootstrapDNSHandler(
) {
dnsEntries := make(map[string][]net.IP)
resolvCtx, cancel := context.WithTimeout(context.Background(), time.Minute)
resolvCtx, cancel := context.WithTimeout(req.Context(), time.Minute)
defer cancel()
var resolver net.Resolver
for _, region := range h.DERPMap.Regions {
@@ -276,7 +280,8 @@ func serverSTUNListener(ctx context.Context, packetConn *net.UDPConn) {
continue
}
res := stun.Response(txid, udpAddr.IP, uint16(udpAddr.Port))
addr, _ := netip.AddrFromSlice(udpAddr.IP)
res := stun.Response(txid, netip.AddrPortFrom(addr, uint16(udpAddr.Port)))
_, err = packetConn.WriteTo(res, udpAddr)
if err != nil {
log.Trace().Caller().Err(err).Msgf("Issue writing to UDP")

21
dns.go
View File

@@ -2,10 +2,11 @@ package headscale
import (
"fmt"
"net/netip"
"strings"
mapset "github.com/deckarep/golang-set/v2"
"inet.af/netaddr"
"go4.org/netipx"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
)
@@ -39,11 +40,11 @@ const (
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
func generateMagicDNSRootDomains(ipPrefixes []netip.Prefix) []dnsname.FQDN {
fqdns := make([]dnsname.FQDN, 0, len(ipPrefixes))
for _, ipPrefix := range ipPrefixes {
var generateDNSRoot func(netaddr.IPPrefix) []dnsname.FQDN
switch ipPrefix.IP().BitLen() {
var generateDNSRoot func(netip.Prefix) []dnsname.FQDN
switch ipPrefix.Addr().BitLen() {
case ipv4AddressLength:
generateDNSRoot = generateIPv4DNSRootDomain
@@ -54,7 +55,7 @@ func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
panic(
fmt.Sprintf(
"unsupported IP version with address length %d",
ipPrefix.IP().BitLen(),
ipPrefix.Addr().BitLen(),
),
)
}
@@ -65,9 +66,9 @@ func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
return fqdns
}
func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
func generateIPv4DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
// Conversion to the std lib net.IPnet, a bit easier to operate
netRange := ipPrefix.IPNet()
netRange := netipx.PrefixIPNet(ipPrefix)
maskBits, _ := netRange.Mask.Size()
// lastOctet is the last IP byte covered by the mask
@@ -101,11 +102,11 @@ func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
return fqdns
}
func generateIPv6DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
func generateIPv6DNSRootDomain(ipPrefix netip.Prefix) []dnsname.FQDN {
const nibbleLen = 4
maskBits, _ := ipPrefix.IPNet().Mask.Size()
expanded := ipPrefix.IP().StringExpanded()
maskBits, _ := netipx.PrefixIPNet(ipPrefix).Mask.Size()
expanded := ipPrefix.Addr().StringExpanded()
nibbleStr := strings.Map(func(r rune) rune {
if r == ':' {
return -1

View File

@@ -2,16 +2,16 @@ package headscale
import (
"fmt"
"net/netip"
"gopkg.in/check.v1"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
)
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
prefixes := []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.0.0/10"),
prefixes := []netip.Prefix{
netip.MustParsePrefix("100.64.0.0/10"),
}
domains := generateMagicDNSRootDomains(prefixes)
@@ -47,8 +47,8 @@ func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
}
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
prefixes := []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("172.16.0.0/16"),
prefixes := []netip.Prefix{
netip.MustParsePrefix("172.16.0.0/16"),
}
domains := generateMagicDNSRootDomains(prefixes)
@@ -75,8 +75,8 @@ func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
// Happens when netmask is a multiple of 4 bits (sounds likely).
func (s *Suite) TestMagicDNSRootDomainsIPv6Single(c *check.C) {
prefixes := []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48"),
prefixes := []netip.Prefix{
netip.MustParsePrefix("fd7a:115c:a1e0::/48"),
}
domains := generateMagicDNSRootDomains(prefixes)
@@ -89,8 +89,8 @@ func (s *Suite) TestMagicDNSRootDomainsIPv6Single(c *check.C) {
}
func (s *Suite) TestMagicDNSRootDomainsIPv6SingleMultiple(c *check.C) {
prefixes := []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/50"),
prefixes := []netip.Prefix{
netip.MustParsePrefix("fd7a:115c:a1e0::/50"),
}
domains := generateMagicDNSRootDomains(prefixes)
@@ -165,7 +165,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
AuthKeyID: uint(preAuthKeyInShared1.ID),
}
app.db.Save(machineInShared1)
@@ -182,7 +182,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
AuthKeyID: uint(preAuthKeyInShared2.ID),
}
app.db.Save(machineInShared2)
@@ -199,7 +199,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
AuthKeyID: uint(preAuthKeyInShared3.ID),
}
app.db.Save(machineInShared3)
@@ -216,7 +216,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.4")},
AuthKeyID: uint(PreAuthKey2InShared1.ID),
}
app.db.Save(machine2InShared1)
@@ -308,7 +308,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
AuthKeyID: uint(preAuthKeyInShared1.ID),
}
app.db.Save(machineInShared1)
@@ -325,7 +325,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
AuthKeyID: uint(preAuthKeyInShared2.ID),
}
app.db.Save(machineInShared2)
@@ -342,7 +342,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
AuthKeyID: uint(preAuthKeyInShared3.ID),
}
app.db.Save(machineInShared3)
@@ -359,7 +359,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.4")},
AuthKeyID: uint(preAuthKey2InShared1.ID),
}
app.db.Save(machine2InShared1)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -53,6 +53,9 @@ server_url: http://your-host-name:8080 # Change to your hostname or host IP
metrics_listen_addr: 0.0.0.0:9090
# The default /var/lib/headscale path is not writable in the container
private_key_path: /etc/headscale/private.key
# The default /var/lib/headscale path is not writable in the container
noise:
private_key_path: /var/lib/headscale/noise_private.key
# The default /var/lib/headscale path is not writable in the container
db_path: /etc/headscale/db.sqlite
```
@@ -63,7 +66,6 @@ db_path: /etc/headscale/db.sqlite
docker run \
--name headscale \
--detach \
--rm \
--volume $(pwd)/config:/etc/headscale/ \
--publish 127.0.0.1:8080:8080 \
--publish 127.0.0.1:9090:9090 \

View File

@@ -17,7 +17,7 @@ describing how to make `headscale` run properly in a server environment.
```shell
# Install prerequistes
# 1. go v1.18+: headscale newer than 0.15 needs go 1.18+ to compile
# 1. go v1.19+: headscale newer than 0.17 needs go 1.19+ to compile
# 2. gmake: Makefile in the headscale repo is written in GNU make syntax
pkg_add -D snap go
pkg_add gmake
@@ -46,7 +46,7 @@ cp headscale /usr/local/sbin
```shell
# Install prerequistes
# 1. go v1.18+: headscale newer than 0.15 needs go 1.18+ to compile
# 1. go v1.19+: headscale newer than 0.17 needs go 1.19+ to compile
# 2. gmake: Makefile in the headscale repo is written in GNU make syntax
git clone https://github.com/juanfont/headscale.git

14
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1653893745,
"narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@@ -17,16 +17,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1654847188,
"narHash": "sha256-MC+eP7XOGE1LAswOPqdcGoUqY9mEQ3ZaaxamVTbc0hM=",
"lastModified": 1662019588,
"narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "8b66e3f2ebcc644b78cec9d6f152192f4e7d322f",
"rev": "2da64a81275b68fdad38af669afeda43d401e94b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}

View File

@@ -2,7 +2,7 @@
description = "headscale - Open Source Tailscale Control server";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
@@ -17,14 +17,14 @@
in
rec {
headscale =
pkgs.buildGo118Module rec {
pkgs.buildGo119Module rec {
pname = "headscale";
version = headscaleVersion;
src = pkgs.lib.cleanSource self;
# When updating go.mod or go.sum, a new sha will need to be calculated,
# update this if you have a mismatch after doing a change to thos files.
vendorSha256 = "sha256-RzmnAh81BN4tbzAGzJbb6CMuws8kuPJDw7aPkRRnSS8=";
vendorSha256 = "sha256-kc8EU+TkwRlsKM2+ljm/88aWe5h2QMgd/ZGPSgdd9QQ=";
ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
};
@@ -95,7 +95,7 @@
overlays = [ self.overlay ];
inherit system;
};
buildDeps = with pkgs; [ git go_1_18 gnumake ];
buildDeps = with pkgs; [ git go_1_19 gnumake ];
devDeps = with pkgs;
buildDeps ++ [
golangci-lint

42
go.mod
View File

@@ -1,50 +1,53 @@
module github.com/juanfont/headscale
go 1.18
go 1.19
require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
github.com/coreos/go-oidc/v3 v3.2.0
github.com/coreos/go-oidc/v3 v3.3.0
github.com/deckarep/golang-set/v2 v2.1.0
github.com/efekarakus/termcolor v1.0.1
github.com/glebarez/sqlite v1.4.6
github.com/gofrs/uuid v4.2.0+incompatible
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3
github.com/klauspost/compress v1.15.9
github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282
github.com/ory/dockertest/v3 v3.9.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philip-bui/grpc-zerolog v1.0.1
github.com/prometheus/client_golang v1.13.0
github.com/prometheus/common v0.37.0
github.com/pterm/pterm v0.12.45
github.com/puzpuzpuz/xsync v1.4.2
github.com/rs/zerolog v1.27.0
github.com/puzpuzpuz/xsync v1.4.3
github.com/rs/zerolog v1.28.0
github.com/spf13/cobra v1.5.0
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.8.0
github.com/tailscale/hujson v0.0.0-20220630195928-54599719472f
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
google.golang.org/genproto v0.0.0-20220808204814-fd01256a5276
google.golang.org/grpc v1.48.0
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
google.golang.org/genproto v0.0.0-20220902135211-223410557253
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/postgres v1.3.8
gorm.io/driver/postgres v1.3.9
gorm.io/gorm v1.23.8
inet.af/netaddr v0.0.0-20220617031823-097006376321
tailscale.com v1.28.0
tailscale.com v1.30.0
)
require (
atomicgo.dev/cursor v0.1.1 // indirect
atomicgo.dev/keyboard v0.2.8 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
@@ -61,8 +64,11 @@ require (
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/glebarez/go-sqlite v1.17.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/go-github v17.0.0+incompatible // indirect
@@ -72,6 +78,7 @@ require (
github.com/gookit/color v1.5.0 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
@@ -119,24 +126,23 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.zx2c4.com/wireguard/windows v0.4.10 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
modernc.org/libc v1.16.8 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.1.1 // indirect
modernc.org/sqlite v1.17.3 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)

282
go.sum
View File

@@ -32,16 +32,28 @@ cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSU
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -54,16 +66,25 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/mkcert v1.4.3 h1:axpnmtrZMM8u5Hf4N3UXxboGemMOV+Tn+e+pkHM6E3o=
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/Antonboom/errname v0.1.5/go.mod h1:DugbBstvPFQbv/5uLcRRzfrNqKE9tVdVCqWCLp6Cifo=
github.com/Antonboom/nilnil v0.1.0/go.mod h1:PhHLvRPSghY5Y7mX4TW+BHZQYo1A8flE5H20D3IPZBo=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
@@ -133,6 +154,7 @@ github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
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=
@@ -146,12 +168,15 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
github.com/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -166,6 +191,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc=
github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-oidc/v3 v3.3.0 h1:Y1LV3mP+QT3MEycATZpAiwfyN+uxZLqVbAHJUuOJEe4=
github.com/coreos/go-oidc/v3 v3.3.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -197,15 +224,20 @@ github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCF
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/cli v20.10.16+incompatible h1:aLQ8XowgKpR3/IysPj8qZQJBVQ+Qws61icFuZl6iKYs=
github.com/docker/cli v20.10.16+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v20.10.17+incompatible h1:eO2KS7ZFeov5UJeaDmIs1NFEDRf32PaqRpvoEkKBy5M=
github.com/docker/cli v20.10.17+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ=
github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=
github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
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/efekarakus/termcolor v1.0.1 h1:YAKFO3bnLrqZGTWyNLcYoSIAQFKVOmbqmDnwsU/znzg=
github.com/efekarakus/termcolor v1.0.1/go.mod h1:AitrZNrE4nPO538fRsqf+p0WgLdAsGN5pUNrHEPsEMM=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -215,6 +247,7 @@ 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/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
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=
@@ -234,10 +267,18 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3nqZCxaQ2Ze/sM=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
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=
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/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk=
github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY=
github.com/glebarez/go-sqlite v1.18.1 h1:w0xtxKWktqYsUsXg//SQK+l1IcpKb3rGOQHmMptvL2U=
github.com/glebarez/go-sqlite v1.18.1/go.mod h1:ydXIGq2M4OzF4YyNhH129SPp7jWoVvgkEgb6pldmS0s=
github.com/glebarez/sqlite v1.4.6 h1:D5uxD2f6UJ82cHnVtO2TZ9pqsLyto3fpDKHIk2OsR8A=
github.com/glebarez/sqlite v1.4.6/go.mod h1:WYEtEFjhADPaPJqL/PGlbQQGINBA3eUAfDNbKFJf/zA=
github.com/go-critic/go-critic v0.6.1/go.mod h1:SdNCfU0yF3UBjtaZGw6586/WocupMOJuiqgom5DsQxM=
@@ -254,6 +295,13 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
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=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -273,6 +321,12 @@ github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2
github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
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.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
@@ -285,12 +339,16 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -384,13 +442,21 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
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/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg=
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=
@@ -398,6 +464,7 @@ github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b0
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
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.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
@@ -419,9 +486,12 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2 h1:BqHID5W5qnMkug0Z8UmL8tN0gAy4jQ+B4WFt8cCgluU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.2/go.mod h1:ZbS3MZTZq/apAfAEHGoB5HbsQQstoqP92SjAqtQ9zeg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -443,6 +513,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -456,6 +528,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/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU=
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -467,8 +541,13 @@ github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
@@ -481,6 +560,8 @@ github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -489,6 +570,7 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
@@ -498,6 +580,8 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
@@ -506,16 +590,21 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSnpuG7toUTG4=
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
@@ -523,6 +612,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -536,10 +627,14 @@ github.com/josharian/txtarfs v0.0.0-20210218200122-0702f000015a/go.mod h1:izVPOv
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY=
github.com/jsimonetti/rtnetlink v1.2.2 h1:Ok9vYMcpxfHyF/iRqNTYJPDLxVaVItvPamAhtzttDBY=
github.com/jsimonetti/rtnetlink v1.2.2/go.mod h1:T3BJ2qI9ZJFkUYWLrzECdcXhCvaGRfnMFmoYF0X8w2A=
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=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@@ -556,6 +651,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
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.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.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
@@ -585,6 +681,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
github.com/kyoh86/exportloopref v0.1.8/go.mod h1:1tUcJeiioIs7VWe5gcOObrux3lb66+sBqGZrRkMwPgg=
github.com/ldez/gomoddirectives v0.2.2/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -617,6 +715,8 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -627,6 +727,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@@ -635,6 +737,7 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -648,6 +751,8 @@ github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfp
github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
@@ -674,10 +779,14 @@ github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
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/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
@@ -696,6 +805,8 @@ github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62
github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282 h1:TQMyrpijtkFyXpNI3rY5hsZQZw+paiH+BfAlsb81HBY=
github.com/oauth2-proxy/mockoidc v0.0.0-20220308204021-b9169deeb282/go.mod h1:rW25Kyd08Wdn3UVn0YBsDTSvReu0jqpmJKzxITPSjks=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
@@ -713,6 +824,8 @@ github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 h1:+cz
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198/go.mod h1:j4h1pJW6ZcJTgMZWP3+7RlG3zTaP02aDZ/Qw0sppK7Q=
github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw=
github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc=
github.com/opencontainers/runc v1.1.4 h1:nRCz/8sKg6K6jgYAFLDlXzPeITBZJyX28DBVhWD+5dg=
github.com/opencontainers/runc v1.1.4/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -734,6 +847,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA=
@@ -789,6 +904,8 @@ github.com/pterm/pterm v0.12.45 h1:5HATKLTDjl9D74b0x7yiHzFI7OADlSXK3yHrJNhRwZE=
github.com/pterm/pterm v0.12.45/go.mod h1:hJgLlBafm45w/Hr0dKXxY//POD7CgowhePaG1sdPNBg=
github.com/puzpuzpuz/xsync v1.4.2 h1:3SmMNFuNaSHy1xsGGR8edy3Rzv0Ix3dOirPWtAnXvKk=
github.com/puzpuzpuz/xsync v1.4.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
github.com/puzpuzpuz/xsync v1.4.3 h1:nS/Iqc4EnpJ8jm/MzJ+e3MUaP2Ys2mqXeEfoxoU0HaM=
github.com/puzpuzpuz/xsync v1.4.3/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30=
github.com/quasilyte/go-ruleguard v0.3.13/go.mod h1:Ul8wwdqR6kBVOCt2dipDBkE+T6vAV/iixkrKuRTN1oQ=
@@ -801,6 +918,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6O
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -808,13 +927,18 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.6.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w=
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -827,6 +951,7 @@ github.com/sanposhiho/wastedassign/v2 v2.0.6/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dms
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
@@ -846,6 +971,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@@ -857,6 +984,8 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
@@ -899,6 +1028,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tailscale/hujson v0.0.0-20220630195928-54599719472f h1:n4r/sJ92cBSBHK8n9lR1XLFr0OiTVeGfN5TR+9LaN7E=
@@ -918,7 +1049,11 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1
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/v2 v2.4.0/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
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/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
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/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
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=
@@ -931,8 +1066,12 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
@@ -983,12 +1122,15 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf h1:IdwJUzqoIo5lkr2EOyKoe5qipUaEjbOKKY5+fzPBZ3A=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf/go.mod h1:+QXzaoURFd0rGDIjDNpyIkv+F9R7EmeKorvlKRnhqgA=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d h1:ggxwEf5eu0l8v+87VhX1czFh8zJul3hK16Gmruxn7hw=
go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1011,8 +1153,11 @@ 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-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1024,6 +1169,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
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/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10=
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=
@@ -1050,6 +1197,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
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.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
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=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1104,10 +1252,17 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1124,9 +1279,15 @@ 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-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw=
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
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=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1139,8 +1300,11 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1226,6 +1390,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/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-20210908233432-aa78b53d3365/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=
@@ -1233,15 +1398,30 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/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-20211116061358-0a5406a5449c/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-20211210111614-af8b64212486/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-20220114195835-da31bd327af9/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-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s=
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/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-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1250,6 +1430,8 @@ golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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=
@@ -1260,6 +1442,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1267,6 +1451,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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=
@@ -1374,15 +1560,21 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
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=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1411,7 +1603,19 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1468,6 +1672,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
@@ -1481,8 +1686,35 @@ google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKr
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220808204814-fd01256a5276 h1:7PEE9xCtufpGJzrqweakEEnTh7YFELmnKm/ee+5jmfQ=
google.golang.org/genproto v0.0.0-20220808204814-fd01256a5276/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220902135211-223410557253 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4=
google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
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=
@@ -1512,8 +1744,16 @@ 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.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
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=
@@ -1528,6 +1768,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -1547,9 +1788,12 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -1565,11 +1809,15 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.3.8 h1:8bEphSAB69t3odsCR4NDzt581iZEWQuRM27Cg6KgfPY=
gorm.io/driver/postgres v1.3.8/go.mod h1:qB98Aj6AhRO/oyu/jmZsi/YM9g6UzVCjMxO/6frFvcA=
gorm.io/driver/postgres v1.3.9 h1:lWGiVt5CijhQAg0PWB7Od1RNcBw/jS4d2cAScBcSDXg=
gorm.io/driver/postgres v1.3.9/go.mod h1:qw/FeqjxmYqW5dBcYNBsnhQULIApQdk7YuuDPktVi1U=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
@@ -1582,14 +1830,15 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU=
inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83 h1:lZ9GIYaU+o5+X6ST702I/Ntyq9Y2oIMZ42rBQpem64A=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
@@ -1598,14 +1847,24 @@ modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.17.3 h1:MbsE18Y/xJMTxGGjq2Il6aZZf1YFUu6mnRZgQm/VUWQ=
modernc.org/libc v1.17.3/go.mod h1:9y68dPDczc/zwZupQ2yLX/HTAC4YY/u3grUCeD5fC9I=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.2.1 h1:dkRh86wgmq/bJu2cAS2oqBCz/KsMZU7TUM4CibQ7eBs=
modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
@@ -1614,9 +1873,12 @@ mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
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/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
tailscale.com v1.28.0 h1:eW5bJMqw6eu7YUjBcgJY94uIcm5Zv+xpyTxxa7ztZOM=
tailscale.com v1.28.0/go.mod h1:T9uKhlkxVPdSu1Qvp882evcS/hQ1+TAyZ7sJ/VACGRI=
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
tailscale.com v1.30.0 h1:J8k19aVG5z2W7FhpjkJyZ53HKb0tiNR1icvWly36Pvg=
tailscale.com v1.30.0/go.mod h1:MO+tWkQp2YIF3KBnnej/mQvgYccRS5Xk/IrEpZ4Z3BU=

View File

@@ -1739,6 +1739,8 @@ func (s *IntegrationCLITestSuite) TestLoadConfigFromCommand() {
assert.Nil(s.T(), err)
altConfig, err := os.ReadFile("integration_test/etc/alt-config.dump.gold.yaml")
assert.Nil(s.T(), err)
altEnvConfig, err := os.ReadFile("integration_test/etc/alt-env-config.dump.gold.yaml")
assert.Nil(s.T(), err)
_, err = ExecuteCommand(
&s.headscale,
@@ -1771,4 +1773,40 @@ func (s *IntegrationCLITestSuite) TestLoadConfigFromCommand() {
assert.Nil(s.T(), err)
assert.YAMLEq(s.T(), string(altConfig), string(altDumpConfig))
_, err = ExecuteCommand(
&s.headscale,
[]string{
"headscale",
"dumpConfig",
},
[]string{
"HEADSCALE_CONFIG=/etc/headscale/alt-env-config.yaml",
},
)
assert.Nil(s.T(), err)
altEnvDumpConfig, err := os.ReadFile("integration_test/etc/config.dump.yaml")
assert.Nil(s.T(), err)
assert.YAMLEq(s.T(), string(altEnvConfig), string(altEnvDumpConfig))
_, err = ExecuteCommand(
&s.headscale,
[]string{
"headscale",
"-c",
"/etc/headscale/alt-config.yaml",
"dumpConfig",
},
[]string{
"HEADSCALE_CONFIG=/etc/headscale/alt-env-config.yaml",
},
)
assert.Nil(s.T(), err)
altDumpConfig, err = os.ReadFile("integration_test/etc/config.dump.yaml")
assert.Nil(s.T(), err)
assert.YAMLEq(s.T(), string(altConfig), string(altDumpConfig))
}

View File

@@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/netip"
"os"
"strconv"
"strings"
@@ -15,7 +16,6 @@ import (
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
"inet.af/netaddr"
)
const (
@@ -26,12 +26,13 @@ const (
var (
errEnvVarEmpty = errors.New("getenv: environment variable empty")
IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10")
IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48")
IpPrefix4 = netip.MustParsePrefix("100.64.0.0/10")
IpPrefix6 = netip.MustParsePrefix("fd7a:115c:a1e0::/48")
tailscaleVersions = []string{
// "head",
// "unstable",
"1.30.0",
"1.28.0",
"1.26.2",
"1.24.2",
@@ -194,8 +195,8 @@ func getDockerBuildOptions(version string) *dockertest.BuildOptions {
func getIPs(
tailscales map[string]dockertest.Resource,
) (map[string][]netaddr.IP, error) {
ips := make(map[string][]netaddr.IP)
) (map[string][]netip.Addr, error) {
ips := make(map[string][]netip.Addr)
for hostname, tailscale := range tailscales {
command := []string{"tailscale", "ip"}
@@ -213,7 +214,7 @@ func getIPs(
if len(address) < 1 {
continue
}
ip, err := netaddr.ParseIP(address)
ip, err := netip.ParseAddr(address)
if err != nil {
return nil, err
}

View File

@@ -10,6 +10,7 @@ import (
"fmt"
"log"
"net/http"
"net/netip"
"os"
"path"
"strings"
@@ -22,7 +23,6 @@ import (
"github.com/ory/dockertest/v3/docker"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/ipn/ipnstate"
)
@@ -477,8 +477,8 @@ func (s *IntegrationTestSuite) TestGetIpAddresses() {
// }
// }
func getIPsfromIPNstate(status ipnstate.Status) []netaddr.IP {
ips := make([]netaddr.IP, 0)
func getIPsfromIPNstate(status ipnstate.Status) []netip.Addr {
ips := make([]netip.Addr, 0)
for _, peer := range status.Peer {
ips = append(ips, peer.TailscaleIPs...)
@@ -562,13 +562,25 @@ func (s *IntegrationTestSuite) TestTailDrop() {
if peername == hostname {
continue
}
var ip4 netip.Addr
for _, ip := range ips[peername] {
if ip.Is4() {
ip4 = ip
break
}
}
if ip4.IsUnspecified() {
panic("no ipv4 address found")
}
s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
command := []string{
"tailscale", "file", "cp",
fmt.Sprintf("/tmp/file_from_%s", hostname),
fmt.Sprintf("%s:", ips[peername][1]),
fmt.Sprintf("%s:", ip4),
}
retry(10, 1*time.Second, func() error {
err := retry(10, 1*time.Second, func() error {
log.Printf(
"Sending file from %s to %s\n",
hostname,
@@ -582,6 +594,7 @@ func (s *IntegrationTestSuite) TestTailDrop() {
)
return err
})
assert.Nil(t, err)
})
}
@@ -683,6 +696,18 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
ips, err := getIPs(scales.tailscales)
assert.Nil(s.T(), err)
retry := func(times int, sleepInverval time.Duration, doWork func() (string, error)) (result string, err error) {
for attempts := 0; attempts < times; attempts++ {
result, err = doWork()
if err == nil {
return
}
time.Sleep(sleepInverval)
}
return
}
for hostname, tailscale := range scales.tailscales {
for _, peername := range hostnames {
if strings.Contains(peername, hostname) {
@@ -693,17 +718,20 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
command := []string{
"tailscale", "ip", peername,
}
result, err := retry(10, 1*time.Second, func() (string, error) {
log.Printf(
"Resolving name %s from %s\n",
peername,
hostname,
)
result, err := ExecuteCommand(
&tailscale,
command,
[]string{},
)
return result, err
})
log.Printf(
"Resolving name %s from %s\n",
peername,
hostname,
)
result, err := ExecuteCommand(
&tailscale,
command,
[]string{},
)
assert.Nil(t, err)
log.Printf("Result for %s: %s\n", hostname, result)
@@ -720,8 +748,8 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
func getAPIURLs(
tailscales map[string]dockertest.Resource,
) (map[netaddr.IP]string, error) {
fts := make(map[netaddr.IP]string)
) (map[netip.Addr]string, error) {
fts := make(map[netip.Addr]string)
for _, tailscale := range tailscales {
command := []string{
"curl",
@@ -745,11 +773,11 @@ func getAPIURLs(
for _, ft := range pft {
n := ft.Node
for _, a := range n.Addresses { // just add all the addresses
if _, ok := fts[a.IP()]; !ok {
if _, ok := fts[a.Addr()]; !ok {
if ft.PeerAPIURL == "" {
return nil, errors.New("api url is empty")
}
fts[a.IP()] = ft.PeerAPIURL
fts[a.Addr()] = ft.PeerAPIURL
}
}
}

View File

@@ -18,6 +18,7 @@ dns_config:
domains: []
magic_dns: true
nameservers:
- 127.0.0.11
- 1.1.1.1
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s
@@ -27,7 +28,9 @@ ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
listen_addr: 0.0.0.0:18080
log_level: disabled
log:
level: disabled
format: text
logtail:
enabled: false
metrics_listen_addr: 127.0.0.1:19090
@@ -38,6 +41,8 @@ oidc:
- email
strip_email_domain: true
private_key_path: private.key
noise:
private_key_path: noise_private.key
server_url: http://headscale:18080
tls_client_auth_mode: relaxed
tls_letsencrypt_cache_dir: /var/www/.cache

View File

@@ -1,4 +1,5 @@
log_level: trace
log:
level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
@@ -11,9 +12,12 @@ dns_config:
magic_dns: true
domains: []
nameservers:
- 127.0.0.11
- 1.1.1.1
db_path: /tmp/integration_test_db.sqlite3
private_key_path: private.key
noise:
private_key_path: noise_private.key
listen_addr: 0.0.0.0:18080
metrics_listen_addr: 127.0.0.1:19090
server_url: http://headscale:18080

View File

@@ -0,0 +1,51 @@
acl_policy_path: ""
cli:
insecure: false
timeout: 5s
db_path: /tmp/integration_test_db.sqlite3
db_type: sqlite3
derp:
auto_update_enabled: false
server:
enabled: false
stun:
enabled: true
update_frequency: 1m
urls:
- https://controlplane.tailscale.com/derpmap/default
dns_config:
base_domain: headscale.net
domains: []
magic_dns: true
nameservers:
- 1.1.1.1
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 30s
grpc_allow_insecure: false
grpc_listen_addr: :50443
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
listen_addr: 0.0.0.0:18080
log:
level: disabled
format: text
logtail:
enabled: false
metrics_listen_addr: 127.0.0.1:19090
oidc:
scope:
- openid
- profile
- email
strip_email_domain: true
private_key_path: private.key
noise:
private_key_path: noise_private.key
server_url: http://headscale:18080
tls_client_auth_mode: relaxed
tls_letsencrypt_cache_dir: /var/www/.cache
tls_letsencrypt_challenge_type: HTTP-01
unix_socket: /var/run/headscale.sock
unix_socket_permission: "0o770"
randomize_client_port: false

View File

@@ -0,0 +1,28 @@
log:
level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 30s
ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
dns_config:
base_domain: headscale.net
magic_dns: true
domains: []
nameservers:
- 1.1.1.1
db_path: /tmp/integration_test_db.sqlite3
private_key_path: private.key
noise:
private_key_path: noise_private.key
listen_addr: 0.0.0.0:18080
metrics_listen_addr: 127.0.0.1:19090
server_url: http://headscale:18080
derp:
urls:
- https://controlplane.tailscale.com/derpmap/default
auto_update_enabled: false
update_frequency: 1m

View File

@@ -18,6 +18,7 @@ dns_config:
domains: []
magic_dns: true
nameservers:
- 127.0.0.11
- 1.1.1.1
ephemeral_node_inactivity_timeout: 30m
node_update_check_interval: 10s
@@ -27,7 +28,9 @@ ip_prefixes:
- fd7a:115c:a1e0::/48
- 100.64.0.0/10
listen_addr: 0.0.0.0:8080
log_level: disabled
log:
format: text
level: disabled
logtail:
enabled: false
metrics_listen_addr: 127.0.0.1:9090
@@ -38,6 +41,8 @@ oidc:
- email
strip_email_domain: true
private_key_path: private.key
noise:
private_key_path: noise_private.key
server_url: http://headscale:8080
tls_client_auth_mode: relaxed
tls_letsencrypt_cache_dir: /var/www/.cache

View File

@@ -1,4 +1,5 @@
log_level: trace
log:
level: trace
acl_policy_path: ""
db_type: sqlite3
ephemeral_node_inactivity_timeout: 30m
@@ -11,9 +12,12 @@ dns_config:
magic_dns: true
domains: []
nameservers:
- 127.0.0.11
- 1.1.1.1
db_path: /tmp/integration_test_db.sqlite3
private_key_path: private.key
noise:
private_key_path: noise_private.key
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
server_url: http://headscale:8080

View File

@@ -14,8 +14,10 @@ dns_config:
- 1.1.1.1
db_path: /tmp/integration_test_db.sqlite3
private_key_path: private.key
listen_addr: 0.0.0.0:8443
server_url: https://headscale:8443
noise:
private_key_path: noise_private.key
listen_addr: 0.0.0.0:443
server_url: https://headscale:443
tls_cert_path: "/etc/headscale/tls/server.crt"
tls_key_path: "/etc/headscale/tls/server.key"
tls_client_auth_mode: disabled

View File

@@ -4,6 +4,7 @@ import (
"database/sql/driver"
"errors"
"fmt"
"net/netip"
"sort"
"strconv"
"strings"
@@ -12,7 +13,6 @@ import (
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log"
"google.golang.org/protobuf/types/known/timestamppb"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
@@ -26,14 +26,22 @@ const (
)
ErrCouldNotConvertMachineInterface = Error("failed to convert machine interface")
ErrHostnameTooLong = Error("Hostname too long")
MachineGivenNameHashLength = 8
MachineGivenNameTrimSize = 2
ErrDifferentRegisteredNamespace = Error(
"machine was previously registered with a different namespace",
)
MachineGivenNameHashLength = 8
MachineGivenNameTrimSize = 2
)
const (
maxHostnameLength = 255
)
var (
ExitRouteV4 = netip.MustParsePrefix("0.0.0.0/0")
ExitRouteV6 = netip.MustParsePrefix("::/0")
)
// Machine is a Headscale client.
type Machine struct {
ID uint64 `gorm:"primary_key"`
@@ -82,7 +90,7 @@ type (
MachinesP []*Machine
)
type MachineAddresses []netaddr.IP
type MachineAddresses []netip.Addr
func (ma MachineAddresses) ToStringSlice() []string {
strSlice := make([]string, 0, len(ma))
@@ -102,7 +110,7 @@ func (ma *MachineAddresses) Scan(destination interface{}) error {
if len(addr) < 1 {
continue
}
parsed, err := netaddr.ParseIP(addr)
parsed, err := netip.ParseAddr(addr)
if err != nil {
return err
}
@@ -244,8 +252,8 @@ func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
Msg("Finding direct peers")
machines := Machines{}
if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("machine_key <> ?",
machine.MachineKey).Find(&machines).Error; err != nil {
if err := h.db.Preload("AuthKey").Preload("AuthKey.Namespace").Preload("Namespace").Where("node_key <> ?",
machine.NodeKey).Find(&machines).Error; err != nil {
log.Error().Err(err).Msg("Error accessing db")
return Machines{}, err
@@ -375,6 +383,19 @@ func (h *Headscale) GetMachineByNodeKey(
return &machine, nil
}
// GetMachineByAnyNodeKey finds a Machine by its current NodeKey or the old one, and returns the Machine struct.
func (h *Headscale) GetMachineByAnyNodeKey(
nodeKey key.NodePublic, oldNodeKey key.NodePublic,
) (*Machine, error) {
machine := Machine{}
if result := h.db.Preload("Namespace").First(&machine, "node_key = ? OR node_key = ?",
NodePublicKeyStripPrefix(nodeKey), NodePublicKeyStripPrefix(oldNodeKey)); result.Error != nil {
return nil, result.Error
}
return &machine, nil
}
// UpdateMachineFromDatabase takes a Machine struct pointer (typically already loaded from database
// and updates it with the latest data from the database.
func (h *Headscale) UpdateMachineFromDatabase(machine *Machine) error {
@@ -397,7 +418,7 @@ func (h *Headscale) SetTags(machine *Machine, tags []string) error {
if err := h.UpdateACLRules(); err != nil && !errors.Is(err, errEmptyPolicy) {
return err
}
h.setLastStateChangeToNow(machine.Namespace.Name)
h.setLastStateChangeToNow()
if err := h.db.Save(machine).Error; err != nil {
return fmt.Errorf("failed to update tags for machine in the database: %w", err)
@@ -411,7 +432,7 @@ func (h *Headscale) ExpireMachine(machine *Machine) error {
now := time.Now()
machine.Expiry = &now
h.setLastStateChangeToNow(machine.Namespace.Name)
h.setLastStateChangeToNow()
if err := h.db.Save(machine).Error; err != nil {
return fmt.Errorf("failed to expire machine in the database: %w", err)
@@ -438,7 +459,7 @@ func (h *Headscale) RenameMachine(machine *Machine, newName string) error {
}
machine.GivenName = newName
h.setLastStateChangeToNow(machine.Namespace.Name)
h.setLastStateChangeToNow()
if err := h.db.Save(machine).Error; err != nil {
return fmt.Errorf("failed to rename machine in the database: %w", err)
@@ -454,7 +475,7 @@ func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) error {
machine.LastSuccessfulUpdate = &now
machine.Expiry = &expiry
h.setLastStateChangeToNow(machine.Namespace.Name)
h.setLastStateChangeToNow()
if err := h.db.Save(machine).Error; err != nil {
return fmt.Errorf(
@@ -587,11 +608,14 @@ func (machine Machine) toNode(
}
var machineKey key.MachinePublic
err = machineKey.UnmarshalText(
[]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)),
)
if err != nil {
return nil, fmt.Errorf("failed to parse machine public key: %w", err)
// MachineKey is only used in the legacy protocol
if machine.MachineKey != "" {
err = machineKey.UnmarshalText(
[]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)),
)
if err != nil {
return nil, fmt.Errorf("failed to parse machine public key: %w", err)
}
}
var discoKey key.DiscoPublic
@@ -606,20 +630,32 @@ func (machine Machine) toNode(
discoKey = key.DiscoPublic{}
}
addrs := []netaddr.IPPrefix{}
addrs := []netip.Prefix{}
for _, machineAddress := range machine.IPAddresses {
ip := netaddr.IPPrefixFrom(machineAddress, machineAddress.BitLen())
ip := netip.PrefixFrom(machineAddress, machineAddress.BitLen())
addrs = append(addrs, ip)
}
allowedIPs := append(
[]netaddr.IPPrefix{},
[]netip.Prefix{},
addrs...) // we append the node own IP, as it is required by the clients
// TODO(kradalby): Needs investigation, We probably dont need this condition
// now that we dont have shared nodes
if includeRoutes {
allowedIPs = append(allowedIPs, machine.EnabledRoutes...)
allowedIPs = append(allowedIPs, machine.EnabledRoutes...)
// TODO(kradalby): This is kind of a hack where we say that
// all the announced routes (except exit), is presented as primary
// routes. This might be problematic if two nodes expose the same route.
// This was added to address an issue where subnet routers stopped working
// when we only populated AllowedIPs.
primaryRoutes := []netip.Prefix{}
if len(machine.EnabledRoutes) > 0 {
for _, route := range machine.EnabledRoutes {
if route == ExitRouteV4 || route == ExitRouteV6 {
continue
}
primaryRoutes = append(primaryRoutes, route)
}
}
var derp string
@@ -666,16 +702,17 @@ func (machine Machine) toNode(
StableID: tailcfg.StableNodeID(
strconv.FormatUint(machine.ID, Base10),
), // in headscale, unlike tailcontrol server, IDs are permanent
Name: hostname,
User: tailcfg.UserID(machine.NamespaceID),
Key: nodeKey,
KeyExpiry: keyExpiry,
Machine: machineKey,
DiscoKey: discoKey,
Addresses: addrs,
AllowedIPs: allowedIPs,
Endpoints: machine.Endpoints,
DERP: derp,
Name: hostname,
User: tailcfg.UserID(machine.NamespaceID),
Key: nodeKey,
KeyExpiry: keyExpiry,
Machine: machineKey,
DiscoKey: discoKey,
Addresses: addrs,
AllowedIPs: allowedIPs,
PrimaryRoutes: primaryRoutes,
Endpoints: machine.Endpoints,
DERP: derp,
Online: &online,
Hostinfo: hostInfo.View(),
@@ -789,6 +826,12 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
)
}
// Registration of expired machine with different namespace
if registrationMachine.ID != 0 &&
registrationMachine.NamespaceID != namespace.ID {
return nil, ErrDifferentRegisteredNamespace
}
registrationMachine.NamespaceID = namespace.ID
registrationMachine.RegisterMethod = registrationMethod
@@ -796,6 +839,10 @@ func (h *Headscale) RegisterMachineFromAuthCallback(
registrationMachine,
)
if err == nil {
h.registrationCache.Delete(nodeKeyStr)
}
return machine, err
} else {
return nil, ErrCouldNotConvertMachineInterface
@@ -847,16 +894,16 @@ func (h *Headscale) RegisterMachine(machine Machine,
return &machine, nil
}
func (machine *Machine) GetAdvertisedRoutes() []netaddr.IPPrefix {
func (machine *Machine) GetAdvertisedRoutes() []netip.Prefix {
return machine.HostInfo.RoutableIPs
}
func (machine *Machine) GetEnabledRoutes() []netaddr.IPPrefix {
func (machine *Machine) GetEnabledRoutes() []netip.Prefix {
return machine.EnabledRoutes
}
func (machine *Machine) IsRoutesEnabled(routeStr string) bool {
route, err := netaddr.ParseIPPrefix(routeStr)
route, err := netip.ParsePrefix(routeStr)
if err != nil {
return false
}
@@ -875,9 +922,9 @@ func (machine *Machine) IsRoutesEnabled(routeStr string) bool {
// EnableNodeRoute enables new routes based on a list of new routes. It will _replace_ the
// previous list of routes.
func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
newRoutes := make([]netaddr.IPPrefix, len(routeStrs))
newRoutes := make([]netip.Prefix, len(routeStrs))
for index, routeStr := range routeStrs {
route, err := netaddr.ParseIPPrefix(routeStr)
route, err := netip.ParsePrefix(routeStr)
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package headscale
import (
"fmt"
"net/netip"
"reflect"
"strconv"
"strings"
@@ -9,8 +10,8 @@ import (
"time"
"gopkg.in/check.v1"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
func (s *Suite) TestGetMachine(c *check.C) {
@@ -65,6 +66,63 @@ func (s *Suite) TestGetMachineByID(c *check.C) {
c.Assert(err, check.IsNil)
}
func (s *Suite) TestGetMachineByNodeKey(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachineByID(0)
c.Assert(err, check.NotNil)
nodeKey := key.NewNode()
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
Hostname: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
app.db.Save(&machine)
_, err = app.GetMachineByNodeKey(nodeKey.Public())
c.Assert(err, check.IsNil)
}
func (s *Suite) TestGetMachineByAnyNodeKey(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
pak, err := app.CreatePreAuthKey(namespace.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = app.GetMachineByID(0)
c.Assert(err, check.NotNil)
nodeKey := key.NewNode()
oldNodeKey := key.NewNode()
machine := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: NodePublicKeyStripPrefix(nodeKey.Public()),
DiscoKey: "faa",
Hostname: "testmachine",
NamespaceID: namespace.ID,
RegisterMethod: RegisterMethodAuthKey,
AuthKeyID: uint(pak.ID),
}
app.db.Save(&machine)
_, err = app.GetMachineByAnyNodeKey(nodeKey.Public(), oldNodeKey.Public())
c.Assert(err, check.IsNil)
}
func (s *Suite) TestDeleteMachine(c *check.C) {
namespace, err := app.CreateNamespace("test")
c.Assert(err, check.IsNil)
@@ -171,7 +229,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
NodeKey: "bar" + strconv.Itoa(index),
DiscoKey: "faa" + strconv.Itoa(index),
IPAddresses: MachineAddresses{
netaddr.MustParseIP(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
netip.MustParseAddr(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
},
Hostname: "testmachine" + strconv.Itoa(index),
NamespaceID: stor[index%2].namespace.ID,
@@ -185,7 +243,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
Groups: map[string][]string{
"group:test": {"admin"},
},
Hosts: map[string]netaddr.IPPrefix{},
Hosts: map[string]netip.Prefix{},
TagOwners: map[string][]string{},
ACLs: []ACL{
{
@@ -268,9 +326,9 @@ func (s *Suite) TestExpireMachine(c *check.C) {
}
func (s *Suite) TestSerdeAddressStrignSlice(c *check.C) {
input := MachineAddresses([]netaddr.IP{
netaddr.MustParseIP("192.0.2.1"),
netaddr.MustParseIP("2001:db8::1"),
input := MachineAddresses([]netip.Addr{
netip.MustParseAddr("192.0.2.1"),
netip.MustParseAddr("2001:db8::1"),
})
serialized, err := input.Value()
c.Assert(err, check.IsNil)
@@ -482,7 +540,6 @@ func Test_getTags(t *testing.T) {
}
}
// nolint
func Test_getFilteredByACLPeers(t *testing.T) {
type args struct {
machines []Machine
@@ -501,21 +558,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -530,19 +587,19 @@ func Test_getFilteredByACLPeers(t *testing.T) {
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
@@ -554,21 +611,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -583,14 +640,14 @@ func Test_getFilteredByACLPeers(t *testing.T) {
},
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.1")},
Namespace: Namespace{Name: "joe"},
},
},
want: Machines{
{
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
@@ -602,21 +659,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -631,14 +688,14 @@ func Test_getFilteredByACLPeers(t *testing.T) {
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
want: Machines{
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
@@ -650,21 +707,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -680,7 +737,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
machine: &Machine{ // current machine
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
@@ -689,7 +746,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
@@ -702,21 +759,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -732,7 +789,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
@@ -741,14 +798,14 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -761,21 +818,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -790,7 +847,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},
@@ -798,13 +855,13 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 3,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.3")},
Namespace: Namespace{Name: "mickael"},
},
},
@@ -816,21 +873,21 @@ func Test_getFilteredByACLPeers(t *testing.T) {
{
ID: 1,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.1"),
netip.MustParseAddr("100.64.0.1"),
},
Namespace: Namespace{Name: "joe"},
},
{
ID: 2,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.2"),
netip.MustParseAddr("100.64.0.2"),
},
Namespace: Namespace{Name: "marc"},
},
{
ID: 3,
IPAddresses: MachineAddresses{
netaddr.MustParseIP("100.64.0.3"),
netip.MustParseAddr("100.64.0.3"),
},
Namespace: Namespace{Name: "mickael"},
},
@@ -839,7 +896,7 @@ func Test_getFilteredByACLPeers(t *testing.T) {
},
machine: &Machine{ // current machine
ID: 2,
IPAddresses: MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: MachineAddresses{netip.MustParseAddr("100.64.0.2")},
Namespace: Namespace{Name: "marc"},
},
},

View File

@@ -1,11 +1,11 @@
package headscale
import (
"net/netip"
"testing"
"gopkg.in/check.v1"
"gorm.io/gorm"
"inet.af/netaddr"
)
func (s *Suite) TestCreateAndDestroyNamespace(c *check.C) {
@@ -146,7 +146,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.1")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.1")},
AuthKeyID: uint(preAuthKeyShared1.ID),
}
app.db.Save(machineInShared1)
@@ -163,7 +163,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
NamespaceID: namespaceShared2.ID,
Namespace: *namespaceShared2,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.2")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.2")},
AuthKeyID: uint(preAuthKeyShared2.ID),
}
app.db.Save(machineInShared2)
@@ -180,7 +180,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
NamespaceID: namespaceShared3.ID,
Namespace: *namespaceShared3,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.3")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
AuthKeyID: uint(preAuthKeyShared3.ID),
}
app.db.Save(machineInShared3)
@@ -197,7 +197,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
NamespaceID: namespaceShared1.ID,
Namespace: *namespaceShared1,
RegisterMethod: RegisterMethodAuthKey,
IPAddresses: []netaddr.IP{netaddr.MustParseIP("100.64.0.4")},
IPAddresses: []netip.Addr{netip.MustParseAddr("100.64.0.4")},
AuthKeyID: uint(preAuthKey2Shared1.ID),
}
app.db.Save(machine2InShared1)

55
noise.go Normal file
View File

@@ -0,0 +1,55 @@
package headscale
import (
"net/http"
"github.com/rs/zerolog/log"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"tailscale.com/control/controlhttp"
"tailscale.com/net/netutil"
)
const (
// ts2021UpgradePath is the path that the server listens on for the WebSockets upgrade.
ts2021UpgradePath = "/ts2021"
)
// NoiseUpgradeHandler is to upgrade the connection and hijack the net.Conn
// in order to use the Noise-based TS2021 protocol. Listens in /ts2021.
func (h *Headscale) NoiseUpgradeHandler(
writer http.ResponseWriter,
req *http.Request,
) {
log.Trace().Caller().Msgf("Noise upgrade handler for client %s", req.RemoteAddr)
upgrade := req.Header.Get("Upgrade")
if upgrade == "" {
// This probably means that the user is running Headscale behind an
// improperly configured reverse proxy. TS2021 requires WebSockets to
// be passed to Headscale. Let's give them a hint.
log.Warn().
Caller().
Msg("No Upgrade header in TS2021 request. If headscale is behind a reverse proxy, make sure it is configured to pass WebSockets through.")
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
noiseConn, err := controlhttp.AcceptHTTP(req.Context(), writer, req, *h.noisePrivateKey)
if err != nil {
log.Error().Err(err).Msg("noise upgrade failed")
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
server := http.Server{
ReadTimeout: HTTPReadTimeout,
}
server.Handler = h2c.NewHandler(h.noiseMux, &http2.Server{})
err = server.Serve(netutil.NewOneConnListener(noiseConn, nil))
if err != nil {
log.Info().Err(err).Msg("The HTTP2 server was closed")
}
}

12
oidc.go
View File

@@ -148,12 +148,12 @@ func (h *Headscale) OIDCCallback(
return
}
rawIDToken, err := h.getIDTokenForOIDCCallback(writer, code, state)
rawIDToken, err := h.getIDTokenForOIDCCallback(req.Context(), writer, code, state)
if err != nil {
return
}
idToken, err := h.verifyIDTokenForOIDCCallback(writer, rawIDToken)
idToken, err := h.verifyIDTokenForOIDCCallback(req.Context(), writer, rawIDToken)
if err != nil {
return
}
@@ -240,10 +240,11 @@ func validateOIDCCallbackParams(
}
func (h *Headscale) getIDTokenForOIDCCallback(
ctx context.Context,
writer http.ResponseWriter,
code, state string,
) (string, error) {
oauth2Token, err := h.oauth2Config.Exchange(context.Background(), code)
oauth2Token, err := h.oauth2Config.Exchange(ctx, code)
if err != nil {
log.Error().
Err(err).
@@ -287,11 +288,12 @@ func (h *Headscale) getIDTokenForOIDCCallback(
}
func (h *Headscale) verifyIDTokenForOIDCCallback(
ctx context.Context,
writer http.ResponseWriter,
rawIDToken string,
) (*oidc.IDToken, error) {
verifier := h.oidcProvider.Verifier(&oidc.Config{ClientID: h.cfg.OIDC.ClientID})
idToken, err := verifier.Verify(context.Background(), rawIDToken)
idToken, err := verifier.Verify(ctx, rawIDToken)
if err != nil {
log.Error().
Err(err).
@@ -318,7 +320,7 @@ func extractIDTokenClaims(
idToken *oidc.IDToken,
) (*IDTokenClaims, error) {
var claims IDTokenClaims
if err := idToken.Claims(claims); err != nil {
if err := idToken.Claims(&claims); err != nil {
log.Error().
Err(err).
Caller().

View File

@@ -4,21 +4,12 @@ deps:
- remote: buf.build
owner: googleapis
repository: googleapis
branch: main
commit: cd101b0abb7b4404a0b1ecc1afd4ce10
digest: b1-H4GHwHVHcJBbVPg-Cdmnx812reFCDQws_QoQ0W2hYQA=
create_time: 2021-10-23T15:04:06.087748Z
commit: 62f35d8aed1149c291d606d958a7ce32
- remote: buf.build
owner: grpc-ecosystem
repository: grpc-gateway
branch: main
commit: ff83506eb9cc4cf8972f49ce87e6ed3e
digest: b1-iLPHgLaoeWWinMiXXqPnxqE4BThtY3eSbswVGh9GOGI=
create_time: 2021-10-23T16:26:52.283938Z
commit: bc28b723cd774c32b6fbc77621518765
- remote: buf.build
owner: ufoundit-dev
repository: protoc-gen-gorm
branch: main
commit: e2ecbaa0d37843298104bd29fd866df8
digest: b1-SV9yKH_8P-IKTOlHZxP-bb0ALANYeEqH_mtPA0EWfLc=
create_time: 2021-10-08T06:03:05.64876Z

742
protocol_common.go Normal file
View File

@@ -0,0 +1,742 @@
package headscale
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
const (
// The CapabilityVersion is used by Tailscale clients to indicate
// their codebase version. Tailscale clients can communicate over TS2021
// from CapabilityVersion 28, but we only have good support for it
// since https://github.com/tailscale/tailscale/pull/4323 (Noise in any HTTPS port).
//
// Related to this change, there is https://github.com/tailscale/tailscale/pull/5379,
// where CapabilityVersion 39 is introduced to indicate #4323 was merged.
//
// See also https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go
NoiseCapabilityVersion = 39
)
// KeyHandler provides the Headscale pub key
// Listens in /key.
func (h *Headscale) KeyHandler(
writer http.ResponseWriter,
req *http.Request,
) {
// New Tailscale clients send a 'v' parameter to indicate the CurrentCapabilityVersion
clientCapabilityStr := req.URL.Query().Get("v")
if clientCapabilityStr != "" {
log.Debug().
Str("handler", "/key").
Str("v", clientCapabilityStr).
Msg("New noise client")
clientCapabilityVersion, err := strconv.Atoi(clientCapabilityStr)
if err != nil {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusBadRequest)
_, err := writer.Write([]byte("Wrong params"))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
return
}
// TS2021 (Tailscale v2 protocol) requires to have a different key
if clientCapabilityVersion >= NoiseCapabilityVersion {
resp := tailcfg.OverTLSPublicKeyResponse{
LegacyPublicKey: h.privateKey.Public(),
PublicKey: h.noisePrivateKey.Public(),
}
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
err = json.NewEncoder(writer).Encode(resp)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
return
}
}
log.Debug().
Str("handler", "/key").
Msg("New legacy client")
// Old clients don't send a 'v' parameter, so we send the legacy public key
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err := writer.Write([]byte(MachinePublicKeyStripPrefix(h.privateKey.Public())))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to write response")
}
}
// handleRegisterCommon is the common logic for registering a client in the legacy and Noise protocols
//
// When using Noise, the machineKey is Zero.
func (h *Headscale) handleRegisterCommon(
writer http.ResponseWriter,
req *http.Request,
registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic,
) {
now := time.Now().UTC()
machine, err := h.GetMachineByAnyNodeKey(registerRequest.NodeKey, registerRequest.OldNodeKey)
if errors.Is(err, gorm.ErrRecordNotFound) {
// If the machine has AuthKey set, handle registration via PreAuthKeys
if registerRequest.Auth.AuthKey != "" {
h.handleAuthKeyCommon(writer, registerRequest, machineKey)
return
}
// Check if the node is waiting for interactive login.
//
// TODO(juan): We could use this field to improve our protocol implementation,
// and hold the request until the client closes it, or the interactive
// login is completed (i.e., the user registers the machine).
// This is not implemented yet, as it is no strictly required. The only side-effect
// is that the client will hammer headscale with requests until it gets a
// successful RegisterResponse.
if registerRequest.Followup != "" {
if _, ok := h.registrationCache.Get(NodePublicKeyStripPrefix(registerRequest.NodeKey)); ok {
log.Debug().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Bool("noise", machineKey.IsZero()).
Msg("Machine is waiting for interactive login")
ticker := time.NewTicker(registrationHoldoff)
select {
case <-req.Context().Done():
return
case <-ticker.C:
h.handleNewMachineCommon(writer, registerRequest, machineKey)
return
}
}
}
log.Info().
Caller().
Str("machine", registerRequest.Hostinfo.Hostname).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("node_key_old", registerRequest.OldNodeKey.ShortString()).
Str("follow_up", registerRequest.Followup).
Bool("noise", machineKey.IsZero()).
Msg("New machine not yet in the database")
givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err)
return
}
// The machine did not have a key to authenticate, which means
// that we rely on a method that calls back some how (OpenID or CLI)
// We create the machine and then keep it around until a callback
// happens
newMachine := Machine{
MachineKey: MachinePublicKeyStripPrefix(machineKey),
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
NodeKey: NodePublicKeyStripPrefix(registerRequest.NodeKey),
LastSeen: &now,
Expiry: &time.Time{},
}
if !registerRequest.Expiry.IsZero() {
log.Trace().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Time("expiry", registerRequest.Expiry).
Msg("Non-zero expiry time requested")
newMachine.Expiry = &registerRequest.Expiry
}
h.registrationCache.Set(
newMachine.NodeKey,
newMachine,
registerCacheExpiration,
)
h.handleNewMachineCommon(writer, registerRequest, machineKey)
return
}
// The machine is already registered, so we need to pass through reauth or key update.
if machine != nil {
// If the NodeKey stored in headscale is the same as the key presented in a registration
// request, then we have a node that is either:
// - Trying to log out (sending a expiry in the past)
// - A valid, registered machine, looking for the node map
// - Expired machine wanting to reauthenticate
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.NodeKey) {
// The client sends an Expiry in the past if the client is requesting to expire the key (aka logout)
// https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L648
if !registerRequest.Expiry.IsZero() &&
registerRequest.Expiry.UTC().Before(now) {
h.handleMachineLogOutCommon(writer, *machine, machineKey)
return
}
// If machine is not expired, and is register, we have a already accepted this machine,
// let it proceed with a valid registration
if !machine.isExpired() {
h.handleMachineValidRegistrationCommon(writer, *machine, machineKey)
return
}
}
// The NodeKey we have matches OldNodeKey, which means this is a refresh after a key expiration
if machine.NodeKey == NodePublicKeyStripPrefix(registerRequest.OldNodeKey) &&
!machine.isExpired() {
h.handleMachineRefreshKeyCommon(
writer,
registerRequest,
*machine,
machineKey,
)
return
}
// The machine has expired
h.handleMachineExpiredCommon(writer, registerRequest, *machine, machineKey)
machine.Expiry = &time.Time{}
h.registrationCache.Set(
NodePublicKeyStripPrefix(registerRequest.NodeKey),
*machine,
registerCacheExpiration,
)
return
}
}
// handleAuthKeyCommon contains the logic to manage auth key client registration
// It is used both by the legacy and the new Noise protocol.
// When using Noise, the machineKey is Zero.
//
// TODO: check if any locks are needed around IP allocation.
func (h *Headscale) handleAuthKeyCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic,
) {
log.Debug().
Str("func", "handleAuthKeyCommon").
Str("machine", registerRequest.Hostinfo.Hostname).
Bool("noise", machineKey.IsZero()).
Msgf("Processing auth key for %s", registerRequest.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{}
pak, err := h.checkKeyValidity(registerRequest.Auth.AuthKey)
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusUnauthorized)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to write response")
}
log.Error().
Caller().
Str("func", "handleAuthKeyCommon").
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Failed authentication via AuthKey")
if pak != nil {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
} else {
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc()
}
return
}
log.Debug().
Str("func", "handleAuthKeyCommon").
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Authentication key was valid, proceeding to acquire IP addresses")
nodeKey := NodePublicKeyStripPrefix(registerRequest.NodeKey)
// retrieve machine information if it exist
// The error is not important, because if it does not
// exist, then this is a new machine and we will move
// on to registration.
machine, _ := h.GetMachineByAnyNodeKey(registerRequest.NodeKey, registerRequest.OldNodeKey)
if machine != nil {
log.Trace().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("machine was already registered before, refreshing with new auth key")
machine.NodeKey = nodeKey
machine.AuthKeyID = uint(pak.ID)
err := h.RefreshMachine(machine, registerRequest.Expiry)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Err(err).
Msg("Failed to refresh machine")
return
}
} else {
now := time.Now().UTC()
givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Str("func", "RegistrationHandler").
Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
Err(err)
return
}
machineToRegister := Machine{
Hostname: registerRequest.Hostinfo.Hostname,
GivenName: givenName,
NamespaceID: pak.Namespace.ID,
MachineKey: MachinePublicKeyStripPrefix(machineKey),
RegisterMethod: RegisterMethodAuthKey,
Expiry: &registerRequest.Expiry,
NodeKey: nodeKey,
LastSeen: &now,
AuthKeyID: uint(pak.ID),
}
machine, err = h.RegisterMachine(
machineToRegister,
)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("could not register machine")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
}
err = h.UsePreAuthKey(pak)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to use pre-auth key")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser()
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Str("func", "handleAuthKeyCommon").
Str("machine", registerRequest.Hostinfo.Hostname).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "success", pak.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to write response")
}
log.Info().
Str("func", "handleAuthKeyCommon").
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Str("ips", strings.Join(machine.IPAddresses.ToStringSlice(), ", ")).
Msg("Successfully authenticated via AuthKey")
}
// handleNewMachineCommon exposes for both legacy and Noise the functionality to get a URL
// for authorizing the machine. This url is then showed to the user by the local Tailscale client.
func (h *Headscale) handleNewMachineCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machineKey key.MachinePublic,
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is new, redirect the client to the registration URL
log.Debug().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("The node seems to be new, sending auth url")
if h.cfg.OIDC.Issuer != "" {
resp.AuthURL = fmt.Sprintf(
"%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey),
)
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Bool("noise", machineKey.IsZero()).
Caller().
Err(err).
Msg("Failed to write response")
}
log.Info().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", registerRequest.Hostinfo.Hostname).
Msg("Successfully sent auth url")
}
func (h *Headscale) handleMachineLogOutCommon(
writer http.ResponseWriter,
machine Machine,
machineKey key.MachinePublic,
) {
resp := tailcfg.RegisterResponse{}
log.Info().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Client requested logout")
err := h.ExpireMachine(&machine)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Str("func", "handleMachineLogOutCommon").
Err(err).
Msg("Failed to expire machine")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.AuthURL = ""
resp.MachineAuthorized = false
resp.User = *machine.Namespace.toUser()
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Bool("noise", machineKey.IsZero()).
Caller().
Err(err).
Msg("Failed to write response")
}
log.Info().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Successfully logged out")
}
func (h *Headscale) handleMachineValidRegistrationCommon(
writer http.ResponseWriter,
machine Machine,
machineKey key.MachinePublic,
) {
resp := tailcfg.RegisterResponse{}
// The machine registration is valid, respond with redirect to /map
log.Debug().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Client is registered and we have the current NodeKey. All clear to /map")
resp.AuthURL = ""
resp.MachineAuthorized = true
resp.User = *machine.Namespace.toUser()
resp.Login = *machine.Namespace.toLogin()
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("update", "web", "error", machine.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("update", "web", "success", machine.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to write response")
}
log.Info().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Machine successfully authorized")
}
func (h *Headscale) handleMachineRefreshKeyCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machine Machine,
machineKey key.MachinePublic,
) {
resp := tailcfg.RegisterResponse{}
log.Debug().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("We have the OldNodeKey in the database. This is a key refresh")
machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
if err := h.db.Save(&machine).Error; err != nil {
log.Error().
Caller().
Err(err).
Msg("Failed to update machine key in the database")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
resp.AuthURL = ""
resp.User = *machine.Namespace.toUser()
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Cannot encode message")
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to write response")
}
log.Info().
Caller().
Bool("noise", machineKey.IsZero()).
Str("node_key", registerRequest.NodeKey.ShortString()).
Str("old_node_key", registerRequest.OldNodeKey.ShortString()).
Str("machine", machine.Hostname).
Msg("Machine successfully refreshed")
}
func (h *Headscale) handleMachineExpiredCommon(
writer http.ResponseWriter,
registerRequest tailcfg.RegisterRequest,
machine Machine,
machineKey key.MachinePublic,
) {
resp := tailcfg.RegisterResponse{}
// The client has registered before, but has expired
log.Debug().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Machine registration has expired. Sending a authurl to register")
if registerRequest.Auth.AuthKey != "" {
h.handleAuthKeyCommon(writer, registerRequest, machineKey)
return
}
if h.cfg.OIDC.Issuer != "" {
resp.AuthURL = fmt.Sprintf("%s/oidc/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
} else {
resp.AuthURL = fmt.Sprintf("%s/register/%s",
strings.TrimSuffix(h.cfg.ServerURL, "/"),
NodePublicKeyStripPrefix(registerRequest.NodeKey))
}
respBody, err := h.marshalResponse(resp, machineKey)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("reauth", "web", "error", machine.Namespace.Name).
Inc()
http.Error(writer, "Internal server error", http.StatusInternalServerError)
return
}
machineRegistrations.WithLabelValues("reauth", "web", "success", machine.Namespace.Name).
Inc()
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err = writer.Write(respBody)
if err != nil {
log.Error().
Caller().
Bool("noise", machineKey.IsZero()).
Err(err).
Msg("Failed to write response")
}
log.Info().
Caller().
Bool("noise", machineKey.IsZero()).
Str("machine", machine.Hostname).
Msg("Auth URL for reauthenticate successfully sent")
}

View File

@@ -2,17 +2,12 @@ package headscale
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
const (
@@ -23,83 +18,15 @@ type contextKey string
const machineNameContextKey = contextKey("machineName")
// PollNetMapHandler takes care of /machine/:id/map
//
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
// the clients when something in the network changes.
//
// The clients POST stuff like HostInfo and their Endpoints here, but
// only after their first request (marked with the ReadOnly field).
//
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
func (h *Headscale) PollNetMapHandler(
// handlePollCommon is the common code for the legacy and Noise protocols to
// managed the poll loop.
func (h *Headscale) handlePollCommon(
writer http.ResponseWriter,
req *http.Request,
ctx context.Context,
machine *Machine,
mapRequest tailcfg.MapRequest,
isNoise bool,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "PollNetMap").
Msg("No machine key in request")
http.Error(writer, "No machine key in request", http.StatusBadRequest)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Msg("PollNetMapHandler called")
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr)))
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot parse client key")
http.Error(writer, "Cannot parse client key", http.StatusBadRequest)
return
}
mapRequest := tailcfg.MapRequest{}
err = decode(body, &mapRequest, &machineKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot decode message")
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
machine, err := h.GetMachineByMachineKey(machineKey)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "PollNetMap").
Msgf("Ignoring request, cannot find machine with key %s", machineKey.String())
http.Error(writer, "", http.StatusUnauthorized)
return
}
log.Error().
Str("handler", "PollNetMap").
Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String())
http.Error(writer, "", http.StatusInternalServerError)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Str("machine", machine.Hostname).
Msg("Found machine in database")
machine.Hostname = mapRequest.Hostinfo.Hostname
machine.HostInfo = HostInfo(*mapRequest.Hostinfo)
machine.DiscoKey = DiscoPublicKeyStripPrefix(mapRequest.DiscoKey)
@@ -107,11 +34,11 @@ func (h *Headscale) PollNetMapHandler(
// update ACLRules with peer informations (to update server tags if necessary)
if h.aclPolicy != nil {
err = h.UpdateACLRules()
err := h.UpdateACLRules()
if err != nil {
log.Error().
Caller().
Str("func", "handleAuthKey").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Err(err)
}
@@ -133,7 +60,8 @@ func (h *Headscale) PollNetMapHandler(
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Bool("noise", isNoise).
Str("node_key", machine.NodeKey).
Str("machine", machine.Hostname).
Err(err).
Msg("Failed to persist/update machine in the database")
@@ -143,11 +71,12 @@ func (h *Headscale) PollNetMapHandler(
}
}
data, err := h.getMapResponse(machineKey, mapRequest, machine)
mapResp, err := h.getMapResponseData(mapRequest, machine, isNoise)
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Bool("noise", isNoise).
Str("node_key", machine.NodeKey).
Str("machine", machine.Hostname).
Err(err).
Msg("Failed to get Map response")
@@ -163,7 +92,7 @@ func (h *Headscale) PollNetMapHandler(
// Details on the protocol can be found in https://github.com/tailscale/tailscale/blob/main/tailcfg/tailcfg.go#L696
log.Debug().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Bool("readOnly", mapRequest.ReadOnly).
Bool("omitPeers", mapRequest.OmitPeers).
@@ -173,12 +102,13 @@ func (h *Headscale) PollNetMapHandler(
if mapRequest.ReadOnly {
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Client is starting up. Probably interested in a DERP map")
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err := writer.Write(data)
_, err := writer.Write(mapResp)
if err != nil {
log.Error().
Caller().
@@ -186,20 +116,24 @@ func (h *Headscale) PollNetMapHandler(
Msg("Failed to write response")
}
if f, ok := writer.(http.Flusher); ok {
f.Flush()
}
return
}
// There has been an update to _any_ of the nodes that the other nodes would
// need to know about
h.setLastStateChangeToNow(machine.Namespace.Name)
h.setLastStateChangeToNow()
// The request is not ReadOnly, so we need to set up channels for updating
// peers via longpoll
// Only create update channel if it has not been created
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Caller().
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Loading or creating update channel")
@@ -214,11 +148,12 @@ func (h *Headscale) PollNetMapHandler(
if mapRequest.OmitPeers && !mapRequest.Stream {
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Client sent endpoint update and is ok with a response without peer list")
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
_, err := writer.Write(data)
_, err := writer.Write(mapResp)
if err != nil {
log.Error().
Caller().
@@ -235,6 +170,7 @@ func (h *Headscale) PollNetMapHandler(
} else if mapRequest.OmitPeers && mapRequest.Stream {
log.Warn().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Ignoring request, don't know how to handle it")
http.Error(writer, "", http.StatusBadRequest)
@@ -244,56 +180,59 @@ func (h *Headscale) PollNetMapHandler(
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Client is ready to access the tailnet")
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Sending initial map")
pollDataChan <- data
pollDataChan <- mapResp
log.Info().
Str("handler", "PollNetMap").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Notifying peers")
updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "full-update").
Inc()
updateChan <- struct{}{}
h.PollNetMapStream(
h.pollNetMapStream(
writer,
req,
ctx,
machine,
mapRequest,
machineKey,
pollDataChan,
keepAliveChan,
updateChan,
isNoise,
)
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Finished stream, closing PollNetMap session")
}
// PollNetMapStream takes care of /machine/:id/map
// stream logic, ensuring we communicate updates and data
// to the connected clients.
func (h *Headscale) PollNetMapStream(
// pollNetMapStream stream logic for /machine/map,
// ensuring we communicate updates and data to the connected clients.
func (h *Headscale) pollNetMapStream(
writer http.ResponseWriter,
req *http.Request,
ctxReq context.Context,
machine *Machine,
mapRequest tailcfg.MapRequest,
machineKey key.MachinePublic,
pollDataChan chan []byte,
keepAliveChan chan []byte,
updateChan chan struct{},
isNoise bool,
) {
h.pollNetMapStreamWG.Add(1)
defer h.pollNetMapStreamWG.Done()
ctx := context.WithValue(req.Context(), machineNameContextKey, machine.Hostname)
ctx := context.WithValue(ctxReq, machineNameContextKey, machine.Hostname)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@@ -302,18 +241,20 @@ func (h *Headscale) PollNetMapStream(
ctx,
updateChan,
keepAliveChan,
machineKey,
mapRequest,
machine,
isNoise,
)
log.Trace().
Str("handler", "PollNetMapStream").
Str("handler", "pollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("Waiting for data to stream...")
log.Trace().
Str("handler", "PollNetMapStream").
Str("handler", "pollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msgf("pollData is %#v, keepAliveChan is %#v, updateChan is %#v", pollDataChan, keepAliveChan, updateChan)
@@ -322,6 +263,7 @@ func (h *Headscale) PollNetMapStream(
case data := <-pollDataChan:
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
@@ -330,6 +272,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Err(err).
@@ -343,6 +286,7 @@ func (h *Headscale) PollNetMapStream(
log.Error().
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Msg("Cannot cast writer to http.Flusher")
@@ -352,6 +296,7 @@ func (h *Headscale) PollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
@@ -363,6 +308,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Err(err).
@@ -383,6 +329,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Err(err).
@@ -393,6 +340,7 @@ func (h *Headscale) PollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "pollData").
Int("bytes", len(data)).
@@ -409,6 +357,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Err(err).
@@ -421,6 +370,7 @@ func (h *Headscale) PollNetMapStream(
log.Error().
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Msg("Cannot cast writer to http.Flusher")
@@ -430,6 +380,7 @@ func (h *Headscale) PollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Int("bytes", len(data)).
@@ -441,6 +392,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Err(err).
@@ -456,6 +408,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Err(err).
@@ -466,6 +419,7 @@ func (h *Headscale) PollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "keepAlive").
Int("bytes", len(data)).
@@ -474,6 +428,7 @@ func (h *Headscale) PollNetMapStream(
case <-updateChan:
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Msg("Received a request for update")
@@ -487,14 +442,16 @@ func (h *Headscale) PollNetMapStream(
}
log.Debug().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
Msgf("There has been updates since the last successful update to %s", machine.Hostname)
data, err := h.getMapResponse(machineKey, mapRequest, machine)
data, err := h.getMapResponseData(mapRequest, machine, false)
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Err(err).
@@ -506,6 +463,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Err(err).
@@ -521,6 +479,7 @@ func (h *Headscale) PollNetMapStream(
log.Error().
Caller().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Msg("Cannot cast writer to http.Flusher")
@@ -530,6 +489,7 @@ func (h *Headscale) PollNetMapStream(
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Msg("Updated Map has been sent")
@@ -547,6 +507,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Err(err).
@@ -566,6 +527,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "update").
Err(err).
@@ -580,6 +542,7 @@ func (h *Headscale) PollNetMapStream(
}
log.Trace().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Time("last_successful_update", lastUpdate).
Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
@@ -598,6 +561,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "Done").
Err(err).
@@ -613,6 +577,7 @@ func (h *Headscale) PollNetMapStream(
if err != nil {
log.Error().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Str("channel", "Done").
Err(err).
@@ -625,6 +590,7 @@ func (h *Headscale) PollNetMapStream(
case <-h.shutdownChan:
log.Info().
Str("handler", "PollNetMapStream").
Bool("noise", isNoise).
Str("machine", machine.Hostname).
Msg("The long-poll handler is shutting down")
@@ -637,9 +603,9 @@ func (h *Headscale) scheduledPollWorker(
ctx context.Context,
updateChan chan struct{},
keepAliveChan chan []byte,
machineKey key.MachinePublic,
mapRequest tailcfg.MapRequest,
machine *Machine,
isNoise bool,
) {
keepAliveTicker := time.NewTicker(keepAliveInterval)
updateCheckerTicker := time.NewTicker(h.cfg.NodeUpdateCheckInterval)
@@ -661,10 +627,11 @@ func (h *Headscale) scheduledPollWorker(
return
case <-keepAliveTicker.C:
data, err := h.getMapKeepAliveResponse(machineKey, mapRequest)
data, err := h.getMapKeepAliveResponseData(mapRequest, machine, isNoise)
if err != nil {
log.Error().
Str("func", "keepAlive").
Bool("noise", isNoise).
Err(err).
Msg("Error generating the keep alive msg")
@@ -674,6 +641,7 @@ func (h *Headscale) scheduledPollWorker(
log.Debug().
Str("func", "keepAlive").
Str("machine", machine.Hostname).
Bool("noise", isNoise).
Msg("Sending keepalive")
keepAliveChan <- data
@@ -681,6 +649,7 @@ func (h *Headscale) scheduledPollWorker(
log.Debug().
Str("func", "scheduledPollWorker").
Str("machine", machine.Hostname).
Bool("noise", isNoise).
Msg("Sending update request")
updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "scheduled-update").
Inc()

122
protocol_common_utils.go Normal file
View File

@@ -0,0 +1,122 @@
package headscale
import (
"encoding/binary"
"encoding/json"
"github.com/klauspost/compress/zstd"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
func (h *Headscale) getMapResponseData(
mapRequest tailcfg.MapRequest,
machine *Machine,
isNoise bool,
) ([]byte, error) {
mapResponse, err := h.generateMapResponse(mapRequest, machine)
if err != nil {
return nil, err
}
if isNoise {
return h.marshalMapResponse(mapResponse, key.MachinePublic{}, mapRequest.Compress)
}
var machineKey key.MachinePublic
err = machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse client key")
return nil, err
}
return h.marshalMapResponse(mapResponse, machineKey, mapRequest.Compress)
}
func (h *Headscale) getMapKeepAliveResponseData(
mapRequest tailcfg.MapRequest,
machine *Machine,
isNoise bool,
) ([]byte, error) {
keepAliveResponse := tailcfg.MapResponse{
KeepAlive: true,
}
if isNoise {
return h.marshalMapResponse(keepAliveResponse, key.MachinePublic{}, mapRequest.Compress)
}
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machine.MachineKey)))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse client key")
return nil, err
}
return h.marshalMapResponse(keepAliveResponse, machineKey, mapRequest.Compress)
}
func (h *Headscale) marshalResponse(
resp interface{},
machineKey key.MachinePublic,
) ([]byte, error) {
jsonBody, err := json.Marshal(resp)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot marshal response")
return nil, err
}
if machineKey.IsZero() { // if Noise
return jsonBody, nil
}
return h.privateKey.SealTo(machineKey, jsonBody), nil
}
func (h *Headscale) marshalMapResponse(
resp interface{},
machineKey key.MachinePublic,
compression string,
) ([]byte, error) {
jsonBody, err := json.Marshal(resp)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot marshal map response")
}
var respBody []byte
if compression == ZstdCompression {
encoder, _ := zstd.NewWriter(nil)
respBody = encoder.EncodeAll(jsonBody, nil)
if !machineKey.IsZero() { // if legacy protocol
respBody = h.privateKey.SealTo(machineKey, respBody)
}
} else {
if !machineKey.IsZero() { // if legacy protocol
respBody = h.privateKey.SealTo(machineKey, jsonBody)
} else {
respBody = jsonBody
}
}
data := make([]byte, reservedResponseHeaderSize)
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
data = append(data, respBody...)
return data, nil
}

58
protocol_legacy.go Normal file
View File

@@ -0,0 +1,58 @@
package headscale
import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// RegistrationHandler handles the actual registration process of a machine
// Endpoint /machine/:mkey.
func (h *Headscale) RegistrationHandler(
writer http.ResponseWriter,
req *http.Request,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "RegistrationHandler").
Msg("No machine ID in request")
http.Error(writer, "No machine ID in request", http.StatusBadRequest)
return
}
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr)))
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse machine key")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot parse machine key", http.StatusBadRequest)
return
}
registerRequest := tailcfg.RegisterRequest{}
err = decode(body, &registerRequest, &machineKey, h.privateKey)
if err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot decode message")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
h.handleRegisterCommon(writer, req, registerRequest, machineKey)
}

94
protocol_legacy_poll.go Normal file
View File

@@ -0,0 +1,94 @@
package headscale
import (
"errors"
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// PollNetMapHandler takes care of /machine/:id/map
//
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
// the clients when something in the network changes.
//
// The clients POST stuff like HostInfo and their Endpoints here, but
// only after their first request (marked with the ReadOnly field).
//
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
func (h *Headscale) PollNetMapHandler(
writer http.ResponseWriter,
req *http.Request,
) {
vars := mux.Vars(req)
machineKeyStr, ok := vars["mkey"]
if !ok || machineKeyStr == "" {
log.Error().
Str("handler", "PollNetMap").
Msg("No machine key in request")
http.Error(writer, "No machine key in request", http.StatusBadRequest)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Msg("PollNetMapHandler called")
body, _ := io.ReadAll(req.Body)
var machineKey key.MachinePublic
err := machineKey.UnmarshalText([]byte(MachinePublicKeyEnsurePrefix(machineKeyStr)))
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot parse client key")
http.Error(writer, "Cannot parse client key", http.StatusBadRequest)
return
}
mapRequest := tailcfg.MapRequest{}
err = decode(body, &mapRequest, &machineKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "PollNetMap").
Err(err).
Msg("Cannot decode message")
http.Error(writer, "Cannot decode message", http.StatusBadRequest)
return
}
machine, err := h.GetMachineByMachineKey(machineKey)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "PollNetMap").
Msgf("Ignoring request, cannot find machine with key %s", machineKey.String())
http.Error(writer, "", http.StatusUnauthorized)
return
}
log.Error().
Str("handler", "PollNetMap").
Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String())
http.Error(writer, "", http.StatusInternalServerError)
return
}
log.Trace().
Str("handler", "PollNetMap").
Str("id", machineKeyStr).
Str("machine", machine.Hostname).
Msg("A machine is entering polling via the legacy protocol")
h.handlePollCommon(writer, req.Context(), machine, mapRequest, false)
}

38
protocol_noise.go Normal file
View File

@@ -0,0 +1,38 @@
package headscale
import (
"encoding/json"
"io"
"net/http"
"github.com/rs/zerolog/log"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// // NoiseRegistrationHandler handles the actual registration process of a machine.
func (h *Headscale) NoiseRegistrationHandler(
writer http.ResponseWriter,
req *http.Request,
) {
log.Trace().Caller().Msgf("Noise registration handler for client %s", req.RemoteAddr)
if req.Method != http.MethodPost {
http.Error(writer, "Wrong method", http.StatusMethodNotAllowed)
return
}
body, _ := io.ReadAll(req.Body)
registerRequest := tailcfg.RegisterRequest{}
if err := json.Unmarshal(body, &registerRequest); err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse RegisterRequest")
machineRegistrations.WithLabelValues("unknown", "web", "error", "unknown").Inc()
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
h.handleRegisterCommon(writer, req, registerRequest, key.MachinePublic{})
}

67
protocol_noise_poll.go Normal file
View File

@@ -0,0 +1,67 @@
package headscale
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/rs/zerolog/log"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
// NoisePollNetMapHandler takes care of /machine/:id/map using the Noise protocol
//
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
// the clients when something in the network changes.
//
// The clients POST stuff like HostInfo and their Endpoints here, but
// only after their first request (marked with the ReadOnly field).
//
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
func (h *Headscale) NoisePollNetMapHandler(
writer http.ResponseWriter,
req *http.Request,
) {
log.Trace().
Str("handler", "NoisePollNetMap").
Msg("PollNetMapHandler called")
body, _ := io.ReadAll(req.Body)
mapRequest := tailcfg.MapRequest{}
if err := json.Unmarshal(body, &mapRequest); err != nil {
log.Error().
Caller().
Err(err).
Msg("Cannot parse MapRequest")
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
machine, err := h.GetMachineByAnyNodeKey(mapRequest.NodeKey, key.NodePublic{})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Warn().
Str("handler", "NoisePollNetMap").
Msgf("Ignoring request, cannot find machine with key %s", mapRequest.NodeKey.String())
http.Error(writer, "Internal error", http.StatusNotFound)
return
}
log.Error().
Str("handler", "NoisePollNetMap").
Msgf("Failed to fetch machine from the database with node key: %s", mapRequest.NodeKey.String())
http.Error(writer, "Internal error", http.StatusInternalServerError)
return
}
log.Debug().
Str("handler", "NoisePollNetMap").
Str("machine", machine.Hostname).
Msg("A machine is entering polling via the Noise protocol")
h.handlePollCommon(writer, req.Context(), machine, mapRequest, true)
}

View File

@@ -2,8 +2,7 @@ package headscale
import (
"fmt"
"inet.af/netaddr"
"net/netip"
)
const (
@@ -16,7 +15,7 @@ const (
func (h *Headscale) GetAdvertisedNodeRoutes(
namespace string,
nodeName string,
) (*[]netaddr.IPPrefix, error) {
) (*[]netip.Prefix, error) {
machine, err := h.GetMachine(namespace, nodeName)
if err != nil {
return nil, err
@@ -31,7 +30,7 @@ func (h *Headscale) GetAdvertisedNodeRoutes(
func (h *Headscale) GetEnabledNodeRoutes(
namespace string,
nodeName string,
) ([]netaddr.IPPrefix, error) {
) ([]netip.Prefix, error) {
machine, err := h.GetMachine(namespace, nodeName)
if err != nil {
return nil, err
@@ -47,7 +46,7 @@ func (h *Headscale) IsNodeRouteEnabled(
nodeName string,
routeStr string,
) bool {
route, err := netaddr.ParseIPPrefix(routeStr)
route, err := netip.ParsePrefix(routeStr)
if err != nil {
return false
}
@@ -79,7 +78,7 @@ func (h *Headscale) EnableNodeRoute(
return err
}
route, err := netaddr.ParseIPPrefix(routeStr)
route, err := netip.ParsePrefix(routeStr)
if err != nil {
return err
}

View File

@@ -1,8 +1,9 @@
package headscale
import (
"net/netip"
"gopkg.in/check.v1"
"inet.af/netaddr"
"tailscale.com/tailcfg"
)
@@ -16,11 +17,11 @@ func (s *Suite) TestGetRoutes(c *check.C) {
_, err = app.GetMachine("test", "test_get_route_machine")
c.Assert(err, check.NotNil)
route, err := netaddr.ParseIPPrefix("10.0.0.0/24")
route, err := netip.ParsePrefix("10.0.0.0/24")
c.Assert(err, check.IsNil)
hostInfo := tailcfg.Hostinfo{
RoutableIPs: []netaddr.IPPrefix{route},
RoutableIPs: []netip.Prefix{route},
}
machine := Machine{
@@ -60,18 +61,18 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
_, err = app.GetMachine("test", "test_enable_route_machine")
c.Assert(err, check.NotNil)
route, err := netaddr.ParseIPPrefix(
route, err := netip.ParsePrefix(
"10.0.0.0/24",
)
c.Assert(err, check.IsNil)
route2, err := netaddr.ParseIPPrefix(
route2, err := netip.ParsePrefix(
"150.0.10.0/25",
)
c.Assert(err, check.IsNil)
hostInfo := tailcfg.Hostinfo{
RoutableIPs: []netaddr.IPPrefix{route, route2},
RoutableIPs: []netip.Prefix{route, route2},
}
machine := Machine{

View File

@@ -83,7 +83,7 @@ func SwaggerAPIv1(
writer http.ResponseWriter,
req *http.Request,
) {
writer.Header().Set("Content-Type", "application/json; charset=utf-88")
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(http.StatusOK)
if _, err := writer.Write(apiV1JSON); err != nil {
log.Error().

View File

@@ -13,6 +13,7 @@ import (
"fmt"
"io/fs"
"net"
"net/netip"
"os"
"path/filepath"
"reflect"
@@ -21,7 +22,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"inet.af/netaddr"
"go4.org/netipx"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
)
@@ -59,6 +60,8 @@ const (
privateHexPrefix = "privkey:"
PermissionFallback = 0o700
ZstdCompression = "zstd"
)
func MachinePublicKeyStripPrefix(machineKey key.MachinePublic) string {
@@ -116,7 +119,10 @@ func decode(
pubKey *key.MachinePublic,
privKey *key.MachinePrivate,
) error {
log.Trace().Int("length", len(msg)).Msg("Trying to decrypt")
log.Trace().
Str("pubkey", pubKey.ShortString()).
Int("length", len(msg)).
Msg("Trying to decrypt")
decrypted, ok := privKey.OpenFrom(*pubKey, msg)
if !ok {
@@ -130,25 +136,12 @@ func decode(
return nil
}
func encode(
v interface{},
pubKey *key.MachinePublic,
privKey *key.MachinePrivate,
) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
return privKey.SealTo(*pubKey, b), nil
}
func (h *Headscale) getAvailableIPs() (MachineAddresses, error) {
var ips MachineAddresses
var err error
ipPrefixes := h.cfg.IPPrefixes
for _, ipPrefix := range ipPrefixes {
var ip *netaddr.IP
var ip *netip.Addr
ip, err = h.getAvailableIP(ipPrefix)
if err != nil {
return ips, err
@@ -159,16 +152,16 @@ func (h *Headscale) getAvailableIPs() (MachineAddresses, error) {
return ips, err
}
func GetIPPrefixEndpoints(na netaddr.IPPrefix) (netaddr.IP, netaddr.IP) {
var network, broadcast netaddr.IP
ipRange := na.Range()
func GetIPPrefixEndpoints(na netip.Prefix) (netip.Addr, netip.Addr) {
var network, broadcast netip.Addr
ipRange := netipx.RangeOfPrefix(na)
network = ipRange.From()
broadcast = ipRange.To()
return network, broadcast
}
func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, error) {
func (h *Headscale) getAvailableIP(ipPrefix netip.Prefix) (*netip.Addr, error) {
usedIps, err := h.getUsedIPs()
if err != nil {
return nil, err
@@ -189,7 +182,7 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro
fallthrough
case usedIps.Contains(ip):
fallthrough
case ip.IsZero() || ip.IsLoopback():
case ip == netip.Addr{} || ip.IsLoopback():
ip = ip.Next()
continue
@@ -200,19 +193,19 @@ func (h *Headscale) getAvailableIP(ipPrefix netaddr.IPPrefix) (*netaddr.IP, erro
}
}
func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
func (h *Headscale) getUsedIPs() (*netipx.IPSet, error) {
// FIXME: This really deserves a better data model,
// but this was quick to get running and it should be enough
// to begin experimenting with a dual stack tailnet.
var addressesSlices []string
h.db.Model(&Machine{}).Pluck("ip_addresses", &addressesSlices)
var ips netaddr.IPSetBuilder
var ips netipx.IPSetBuilder
for _, slice := range addressesSlices {
var machineAddresses MachineAddresses
err := machineAddresses.Scan(slice)
if err != nil {
return &netaddr.IPSet{}, fmt.Errorf(
return &netipx.IPSet{}, fmt.Errorf(
"failed to read ip from database: %w",
err,
)
@@ -225,7 +218,7 @@ func (h *Headscale) getUsedIPs() (*netaddr.IPSet, error) {
ipSet, err := ips.IPSet()
if err != nil {
return &netaddr.IPSet{}, fmt.Errorf(
return &netipx.IPSet{}, fmt.Errorf(
"failed to build IP Set: %w",
err,
)
@@ -258,7 +251,7 @@ func GrpcSocketDialer(ctx context.Context, addr string) (net.Conn, error) {
return d.DialContext(ctx, "unix", addr)
}
func ipPrefixToString(prefixes []netaddr.IPPrefix) []string {
func ipPrefixToString(prefixes []netip.Prefix) []string {
result := make([]string, len(prefixes))
for index, prefix := range prefixes {
@@ -268,13 +261,13 @@ func ipPrefixToString(prefixes []netaddr.IPPrefix) []string {
return result
}
func stringToIPPrefix(prefixes []string) ([]netaddr.IPPrefix, error) {
result := make([]netaddr.IPPrefix, len(prefixes))
func stringToIPPrefix(prefixes []string) ([]netip.Prefix, error) {
result := make([]netip.Prefix, len(prefixes))
for index, prefixStr := range prefixes {
prefix, err := netaddr.ParseIPPrefix(prefixStr)
prefix, err := netip.ParsePrefix(prefixStr)
if err != nil {
return []netaddr.IPPrefix{}, err
return []netip.Prefix{}, err
}
result[index] = prefix
@@ -283,7 +276,7 @@ func stringToIPPrefix(prefixes []string) ([]netaddr.IPPrefix, error) {
return result, nil
}
func contains[T string | netaddr.IPPrefix](ts []T, t T) bool {
func contains[T string | netip.Prefix](ts []T, t T) bool {
for _, v := range ts {
if reflect.DeepEqual(v, t) {
return true

View File

@@ -1,8 +1,10 @@
package headscale
import (
"net/netip"
"go4.org/netipx"
"gopkg.in/check.v1"
"inet.af/netaddr"
)
func (s *Suite) TestGetAvailableIp(c *check.C) {
@@ -10,7 +12,7 @@ func (s *Suite) TestGetAvailableIp(c *check.C) {
c.Assert(err, check.IsNil)
expected := netaddr.MustParseIP("10.27.0.1")
expected := netip.MustParseAddr("10.27.0.1")
c.Assert(len(ips), check.Equals, 1)
c.Assert(ips[0].String(), check.Equals, expected.String())
@@ -46,8 +48,8 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
c.Assert(err, check.IsNil)
expected := netaddr.MustParseIP("10.27.0.1")
expectedIPSetBuilder := netaddr.IPSetBuilder{}
expected := netip.MustParseAddr("10.27.0.1")
expectedIPSetBuilder := netipx.IPSetBuilder{}
expectedIPSetBuilder.Add(expected)
expectedIPSet, _ := expectedIPSetBuilder.IPSet()
@@ -96,11 +98,11 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
usedIps, err := app.getUsedIPs()
c.Assert(err, check.IsNil)
expected0 := netaddr.MustParseIP("10.27.0.1")
expected9 := netaddr.MustParseIP("10.27.0.10")
expected300 := netaddr.MustParseIP("10.27.0.45")
expected0 := netip.MustParseAddr("10.27.0.1")
expected9 := netip.MustParseAddr("10.27.0.10")
expected300 := netip.MustParseAddr("10.27.0.45")
notExpectedIPSetBuilder := netaddr.IPSetBuilder{}
notExpectedIPSetBuilder := netipx.IPSetBuilder{}
notExpectedIPSetBuilder.Add(expected0)
notExpectedIPSetBuilder.Add(expected9)
notExpectedIPSetBuilder.Add(expected300)
@@ -121,7 +123,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
c.Assert(
machine1.IPAddresses[0],
check.Equals,
netaddr.MustParseIP("10.27.0.1"),
netip.MustParseAddr("10.27.0.1"),
)
machine50, err := app.GetMachineByID(50)
@@ -130,10 +132,10 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
c.Assert(
machine50.IPAddresses[0],
check.Equals,
netaddr.MustParseIP("10.27.0.50"),
netip.MustParseAddr("10.27.0.50"),
)
expectedNextIP := netaddr.MustParseIP("10.27.1.95")
expectedNextIP := netip.MustParseAddr("10.27.1.95")
nextIP, err := app.getAvailableIPs()
c.Assert(err, check.IsNil)
@@ -153,7 +155,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
ips, err := app.getAvailableIPs()
c.Assert(err, check.IsNil)
expected := netaddr.MustParseIP("10.27.0.1")
expected := netip.MustParseAddr("10.27.0.1")
c.Assert(len(ips), check.Equals, 1)
c.Assert(ips[0].String(), check.Equals, expected.String())