Compare commits

...

73 Commits

Author SHA1 Message Date
Juan Font
b01f1f1867 Clean apt 2023-05-12 10:09:36 +02:00
Juan Font
c027ef0f6c Added changelog for 0.22.3 2023-05-12 10:09:36 +02:00
Six
db97a7ab10 Add ca-certificates to Dockerfile 2023-05-12 09:24:55 +02:00
Kristoffer Dalby
252342a0a5 update nix hash
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
cdf3c47d63 changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
61a2915f17 port reminder of integrationv1 test to v2
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
a16f0c9f60 clean up unused legacy stuff
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
52ad138c32 update dependency path for integration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
d2413d0a2f move swagger to root for now
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
51dc0d5784 update dependency path for cmd
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
2d365c8c9c inline old acl hujson tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
f2c1d1b8f9 regenerate gen
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
2d6356fa13 move templates
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
3bfc598ccc move generated files
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
3683d3e82f rename package name to hscontrol
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Kristoffer Dalby
4a7921ead5 move all go files from root to hscontrol
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-10 20:47:51 +02:00
Juan Font
22e397e0b6 Use common path in unix_socket default setting 2023-05-10 18:18:04 +02:00
Juan Font
c7db99d6ca Update changelog + prepare for 0.22.2 2023-05-10 18:18:04 +02:00
Juan Font
f73354b4f4 Create default sock path in Docker 2023-05-10 18:18:04 +02:00
Juan Font
4c8f8c6a1c Ditch distroless for Docker image
distroless has proven a mantenance burden for us, and it has caused headaches for user when trying to debug issues in the container.

And in 2023, 20MB of extra disk space are neglectible.
2023-05-10 18:18:04 +02:00
Juan Font
997e93455d Added web ui section
Added discord
2023-05-10 16:16:12 +02:00
Juan Font
9f381256c4 Update config.go 2023-05-10 14:25:13 +02:00
Juan Font
f60c5a1398 Fix socket location in config.go 2023-05-10 14:25:13 +02:00
Juan Font
5706f84cb0 Revert "Revert unix_socket to default value"
This reverts commit ca54fb9f56.
2023-05-10 14:25:13 +02:00
Juan Font
9478c288f6 Added missing file 2023-05-10 10:26:21 +02:00
Juan Font
6043ec87cf Update mkdocs.yml 2023-05-10 09:49:13 +02:00
Juan Font
dcf2439c61 Improved website
More docs
2023-05-10 09:49:13 +02:00
Kristoffer Dalby
ba45d7dbd3 update readme and templates to clarify scope (#1437)
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2023-05-10 08:03:13 +01:00
Juan Font
bab4e14828 Further clarification on unsupported ranges in config example 2023-05-08 12:47:08 +02:00
Juan Font
526e568e1e Update changelog 2023-05-07 15:27:30 +02:00
Juan Font
02ab0df2de Disable and Delete route must affect both exit routes (IPv4 and IPv6)
Fixed linting
2023-05-07 15:27:30 +02:00
Juan Font
7338775de7 Give a warning when users have set an unsupported prefix
Fix minor log issue

Removed debug meessage
2023-05-07 13:14:32 +02:00
Sebastian Muszytowski
00c514608e Add IP forwarding requirement to documentation
I propose to add the information, that IP forwarding needs to be enabled in order to use a node as an exit-node.
2023-05-06 21:48:59 +02:00
Maja Bojarska
6c5723a463 Update CHANGELOG.md
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2023-05-04 22:54:32 +02:00
Maja Bojarska
57fd5cf310 Update CHANGELOG.md 2023-05-04 22:54:32 +02:00
Maja Bojarska
f113cc7846 Add missing GH releases page link 2023-05-04 22:54:32 +02:00
ohdearaugustin
ca54fb9f56 Revert unix_socket to default value 2023-05-03 20:16:04 +02:00
Kristoffer Dalby
735b185e7f use IPSet in acls instead of string slice
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
1a7ae11697 Add basic testcases for Machine.canAccess
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
644be822d5 move matcher to separate file
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
56b63c6e10 use netipx.IPSet for matcher
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
ccedf276ab add a filter case with really large destination set #1372
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
10320a5f1f lint and nolint tailscale borrowed func
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
ecd62fb785 remove terrible filter code
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
0d24e878d0 update flake hash
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
889d5a1b29 testing without that horrible filtercode
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
1700a747f6 outline tests for full filter generate
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
200e3b88cc make generateFilterRule a pol struct func
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
5bbbe437df clear up the acl function naming
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
6de53e2f8d simplify expandAlias function, move seperate logic out
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-03 18:43:57 +02:00
Kristoffer Dalby
b23a9153df trim dockerfiles, script to rebuild test images (#1403) 2023-05-02 10:51:30 +01:00
Juan Font
80772033ee Improvements on Noise implementation (#1379) 2023-05-02 08:15:33 +02:00
Juan Font
a2b760834f Fix extra space 2023-04-30 23:28:16 +02:00
loprima-l
493bcfcf18 Update mkdocs.yml
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2023-04-30 23:28:16 +02:00
loprima-l
df72508089 Fix : Change master branch to main
This fix should change the edit branch to main in the documentation
2023-04-30 23:28:16 +02:00
loprima-l
0f8d8fc2d8 Fix : Updating the doc path
Updating the doc path to be the doc website url as it's a better documentation tool
2023-04-30 22:56:38 +02:00
Jonathan Wright
744e5a11b6 Update CHANGELOG.md
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2023-04-30 18:25:43 +02:00
Jonathan Wright
3ea1750ea0 Update CHANGELOG.md 2023-04-30 18:25:43 +02:00
Jonathan Wright
a45777d22e Put systemd service file in proper location 2023-04-30 18:25:43 +02:00
Kristoffer Dalby
56dd734300 Add go profiling flag, and enable on integration tests (#1382) 2023-04-27 16:57:11 +02:00
Philipp Krivanec
d0113732fe optimize generateACLPeerCacheMap (#1377) 2023-04-26 06:02:54 +02:00
Kristoffer Dalby
6215eb6471 update flake hash (#1376) 2023-04-24 15:52:15 +02:00
Juan Font
1d2b4bca8a Remove legacy DERP tests 2023-04-24 12:35:29 +02:00
Juan Font
96f9680afd Reuse Ping function for DERP ping 2023-04-24 12:17:24 +02:00
Juan Font
b465592c07 Do not use host networking in embedded DERP tests
fixed linting
2023-04-24 12:17:24 +02:00
Juan Font
991ff25362 Added workflow for embedded derp 2023-04-24 12:17:24 +02:00
Juan Font
eacd687dbf Added DERP integration tests
Linting fixes

Set listen addr to :8443
2023-04-24 12:17:24 +02:00
Juan Font
549f5a164d Expand surface of hsic for better TLS support 2023-04-24 12:17:24 +02:00
Juan Font
bb07aec82c Expand tsic to offer PingViaDerp 2023-04-24 12:17:24 +02:00
Kristoffer Dalby
a5afe4bd06 Add more capabilities for systemd
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 15:53:19 +02:00
Kristoffer Dalby
a71cc81fe7 fix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 12:05:57 +02:00
Kristoffer Dalby
679305c3e4 Add version to binary release
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 12:05:57 +02:00
Kristoffer Dalby
c0680f34f1 fix issue where binaries are not released
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 11:10:27 +02:00
163 changed files with 4056 additions and 4411 deletions

View File

@@ -6,19 +6,24 @@ labels: ["bug"]
assignees: "" assignees: ""
--- ---
<!-- Headscale is a multinational community across the globe. Our common language is English. Please consider raising the bug report in this language. --> <!--
Before posting a bug report, discuss the behaviour you are expecting with the Discord community
to make sure that it is truly a bug.
The issue tracker is not the place to ask for support or how to set up Headscale.
**Bug description** Bug reports without the sufficient information will be closed.
Headscale is a multinational community across the globe. Our language is English.
All bug reports needs to be in English.
-->
## Bug description
<!-- A clear and concise description of what the bug is. Describe the expected bahavior <!-- A clear and concise description of what the bug is. Describe the expected bahavior
and how it is currently different. If you are unsure if it is a bug, consider discussing and how it is currently different. If you are unsure if it is a bug, consider discussing
it on our Discord server first. --> it on our Discord server first. -->
**To Reproduce** ## Environment
<!-- Steps to reproduce the behavior. -->
**Context info**
<!-- Please add relevant information about your system. For example: <!-- Please add relevant information about your system. For example:
- Version of headscale used - Version of headscale used
@@ -28,3 +33,20 @@ assignees: ""
- The relevant config parameters you used - The relevant config parameters you used
- Log output - Log output
--> -->
- OS:
- Headscale version:
- Tailscale version:
<!--
We do not support running Headscale in a container nor behind a (reverse) proxy.
If either of these are true for your environment, ask the community in Discord
instead of filing a bug report.
-->
- [ ] Headscale is behind a (reverse) proxy
- [ ] Headscale runs in a container
## To Reproduce
<!-- Steps to reproduce the behavior. -->

View File

@@ -6,12 +6,21 @@ labels: ["enhancement"]
assignees: "" assignees: ""
--- ---
<!-- Headscale is a multinational community across the globe. Our common language is English. Please consider raising the feature request in this language. --> <!--
We typically have a clear roadmap for what we want to improve and reserve the right
to close feature requests that does not fit in the roadmap, or fit with the scope
of the project, or we actually want to implement ourselves.
**Feature request** Headscale is a multinational community across the globe. Our language is English.
All bug reports needs to be in English.
-->
<!-- A clear and precise description of what new or changed feature you want. --> ## Why
<!-- Please include the reason, why you would need the feature. E.g. what problem <!-- Include the reason, why you would need the feature. E.g. what problem
does it solve? Or which workflow is currently frustrating and will be improved by does it solve? Or which workflow is currently frustrating and will be improved by
this? --> this? -->
## Description
<!-- A clear and precise description of what new or changed feature you want. -->

View File

@@ -1,30 +0,0 @@
---
name: "Other issue"
about: "Report a different issue"
title: ""
labels: ["bug"]
assignees: ""
---
<!-- Headscale is a multinational community across the globe. Our common language is English. Please consider raising the issue in this language. -->
<!-- If you have a question, please consider using our Discord for asking questions -->
**Issue description**
<!-- Please add your issue description. -->
**To Reproduce**
<!-- Steps to reproduce the behavior. -->
**Context info**
<!-- Please add relevant information about your system. For example:
- Version of headscale used
- Version of tailscale client
- OS (e.g. Linux, Mac, Cygwin, WSL, etc.) and version
- Kernel version
- The relevant config parameters you used
- Log output
-->

View File

@@ -1,3 +1,15 @@
<!--
Headscale is "Open Source, acknowledged contribution", this means that any
contribution will have to be discussed with the Maintainers before being submitted.
This model has been chosen to reduce the risk of burnout by limiting the
maintenance overhead of reviewing and validating third-party code.
Headscale is open to code contributions for bug fixes without discussion.
If you find mistakes in the documentation, please submit a fix to the documentation.
-->
<!-- Please tick if the following things apply. You… --> <!-- Please tick if the following things apply. You… -->
- [ ] read the [CONTRIBUTING guidelines](README.md#contributing) - [ ] read the [CONTRIBUTING guidelines](README.md#contributing)

26
.github/renovate.json vendored
View File

@@ -6,31 +6,27 @@
"onboarding": false, "onboarding": false,
"extends": ["config:base", ":rebaseStalePrs"], "extends": ["config:base", ":rebaseStalePrs"],
"ignorePresets": [":prHourlyLimit2"], "ignorePresets": [":prHourlyLimit2"],
"enabledManagers": ["dockerfile", "gomod", "github-actions","regex" ], "enabledManagers": ["dockerfile", "gomod", "github-actions", "regex"],
"includeForks": true, "includeForks": true,
"repositories": ["juanfont/headscale"], "repositories": ["juanfont/headscale"],
"platform": "github", "platform": "github",
"packageRules": [ "packageRules": [
{ {
"matchDatasources": ["go"], "matchDatasources": ["go"],
"groupName": "Go modules", "groupName": "Go modules",
"groupSlug": "gomod", "groupSlug": "gomod",
"separateMajorMinor": false "separateMajorMinor": false
}, },
{ {
"matchDatasources": ["docker"], "matchDatasources": ["docker"],
"groupName": "Dockerfiles", "groupName": "Dockerfiles",
"groupSlug": "dockerfiles" "groupSlug": "dockerfiles"
} }
], ],
"regexManagers": [ "regexManagers": [
{ {
"fileMatch": [ "fileMatch": [".github/workflows/.*.yml$"],
".github/workflows/.*.yml$" "matchStrings": ["\\s*go-version:\\s*\"?(?<currentValue>.*?)\"?\\n"],
],
"matchStrings": [
"\\s*go-version:\\s*\"?(?<currentValue>.*?)\"?\\n"
],
"datasourceTemplate": "golang-version", "datasourceTemplate": "golang-version",
"depNameTemplate": "actions/go-version" "depNameTemplate": "actions/go-version"
} }

View File

@@ -1,35 +0,0 @@
name: Integration Test CLI
on: [pull_request]
jobs:
integration-test-cli:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
with:
files: |
*.nix
go.*
**/*.go
integration_test/
config-example.yaml
- uses: cachix/install-nix-action@v16
if: steps.changed-files.outputs.any_changed == 'true'
- name: Run CLI integration tests
if: steps.changed-files.outputs.any_changed == 'true'
run: nix develop --command -- make test_integration_cli

View File

@@ -1,35 +0,0 @@
name: Integration Test DERP
on: [pull_request]
jobs:
integration-test-derp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
with:
files: |
*.nix
go.*
**/*.go
integration_test/
config-example.yaml
- uses: cachix/install-nix-action@v16
if: steps.changed-files.outputs.any_changed == 'true'
- name: Run Embedded DERP server integration tests
if: steps.changed-files.outputs.any_changed == 'true'
run: nix develop --command -- make test_integration_derp

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -0,0 +1,63 @@
# DO NOT EDIT, generated with cmd/gh-action-integration-generator/main.go
# To regenerate, run "go generate" in cmd/gh-action-integration-generator/
name: Integration Test v2 - TestDERPServerScenario
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v34
with:
files: |
*.nix
go.*
**/*.go
integration_test/
config-example.yaml
- uses: cachix/install-nix-action@v18
if: ${{ env.ACT }} || steps.changed-files.outputs.any_changed == 'true'
- name: Run general integration tests
if: steps.changed-files.outputs.any_changed == 'true'
run: |
nix develop --command -- docker run \
--tty --rm \
--volume ~/.cache/hs-integration-go:/go \
--name headscale-test-suite \
--volume $PWD:$PWD -w $PWD/integration \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \
golang:1 \
go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \
-failfast \
-timeout 120m \
-parallel 1 \
-run "^TestDERPServerScenario$"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: logs
path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -43,7 +43,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -55,3 +55,9 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"

View File

@@ -32,17 +32,7 @@ builds:
archives: archives:
- id: golang-cross - id: golang-cross
builds: name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
- darwin-amd64
- darwin-arm64
- freebsd-amd64
- linux-386
- linux-amd64
- linux-arm64
- linux-arm-5
- linux-arm-6
- linux-arm-7
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format: binary format: binary
source: source:
@@ -81,7 +71,7 @@ nfpms:
file_info: file_info:
mode: 0644 mode: 0644
- src: ./docs/packaging/headscale.systemd.service - src: ./docs/packaging/headscale.systemd.service
dst: /etc/systemd/system/headscale.service dst: /usr/lib/systemd/system/headscale.service
- dst: /var/lib/headscale - dst: /var/lib/headscale
type: dir type: dir
- dst: /var/run/headscale - dst: /var/run/headscale

View File

@@ -2,8 +2,36 @@
## 0.23.0 (2023-XX-XX) ## 0.23.0 (2023-XX-XX)
### BREAKING
- Code reorganisation, a lot of code has moved, please review the following PRs accordingly [#1444](https://github.com/juanfont/headscale/pull/1444)
### Changes ### Changes
## 0.22.3 (2023-05-12)
### Changes
- Added missing ca-certificates in Docker image [#1463](https://github.com/juanfont/headscale/pull/1463)
## 0.22.2 (2023-05-10)
### Changes
- Add environment flags to enable pprof (profiling) [#1382](https://github.com/juanfont/headscale/pull/1382)
- Profiles are continously generated in our integration tests.
- Fix systemd service file location in `.deb` packages [#1391](https://github.com/juanfont/headscale/pull/1391)
- Improvements on Noise implementation [#1379](https://github.com/juanfont/headscale/pull/1379)
- Replace node filter logic, ensuring nodes with access can see eachother [#1381](https://github.com/juanfont/headscale/pull/1381)
- Disable (or delete) both exit routes at the same time [#1428](https://github.com/juanfont/headscale/pull/1428)
- Ditch distroless for Docker image, create default socket dir in `/var/run/headscale` [#1450](https://github.com/juanfont/headscale/pull/1450)
## 0.22.1 (2023-04-20)
### Changes
- Fix issue where systemd could not bind to port 80 [#1365](https://github.com/juanfont/headscale/pull/1365)
## 0.22.0 (2023-04-20) ## 0.22.0 (2023-04-20)
### Changes ### Changes

View File

@@ -14,10 +14,17 @@ RUN strip /go/bin/headscale
RUN test -e /go/bin/headscale RUN test -e /go/bin/headscale
# Production image # Production image
FROM gcr.io/distroless/base-debian11 FROM docker.io/debian:bullseye-slim
RUN apt-get update \
&& apt-get install -y ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
COPY --from=build /go/bin/headscale /bin/headscale COPY --from=build /go/bin/headscale /bin/headscale
ENV TZ UTC ENV TZ UTC
RUN mkdir -p /var/run/headscale
EXPOSE 8080/tcp EXPOSE 8080/tcp
CMD ["headscale"] CMD ["headscale"]

View File

@@ -18,6 +18,8 @@ FROM docker.io/golang:1.20.0-bullseye
COPY --from=build /go/bin/headscale /bin/headscale COPY --from=build /go/bin/headscale /bin/headscale
ENV TZ UTC ENV TZ UTC
RUN mkdir -p /var/run/headscale
# Need to reset the entrypoint or everything will run as a busybox script # Need to reset the entrypoint or everything will run as a busybox script
ENTRYPOINT [] ENTRYPOINT []
EXPOSE 8080/tcp EXPOSE 8080/tcp

View File

@@ -1,19 +1,16 @@
FROM ubuntu:latest FROM ubuntu:22.04
ARG TAILSCALE_VERSION=* ARG TAILSCALE_VERSION=*
ARG TAILSCALE_CHANNEL=stable ARG TAILSCALE_CHANNEL=stable
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y gnupg curl ssh \ && apt-get install -y gnupg curl ssh dnsutils ca-certificates \
&& curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.gpg | apt-key add - \ && adduser --shell=/bin/bash ssh-it-user
# Tailscale is deliberately split into a second stage so we can cash utils as a seperate layer.
RUN curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.gpg | apt-key add - \
&& curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \ && curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
&& apt-get update \ && apt-get update \
&& apt-get install -y ca-certificates tailscale=${TAILSCALE_VERSION} dnsutils \ && apt-get install -y tailscale=${TAILSCALE_VERSION} \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN adduser --shell=/bin/bash ssh-it-user
ADD integration_test/etc_embedded_derp/tls/server.crt /usr/local/share/ca-certificates/
RUN chmod 644 /usr/local/share/ca-certificates/server.crt
RUN update-ca-certificates

View File

@@ -1,7 +1,7 @@
FROM golang:latest FROM golang:latest
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y ca-certificates dnsutils git iptables ssh \ && apt-get install -y dnsutils git iptables ssh ca-certificates \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN useradd --shell=/bin/bash --create-home ssh-it-user RUN useradd --shell=/bin/bash --create-home ssh-it-user
@@ -10,15 +10,8 @@ RUN git clone https://github.com/tailscale/tailscale.git
WORKDIR /go/tailscale WORKDIR /go/tailscale
RUN git checkout main RUN git checkout main \
&& sh build_dist.sh tailscale.com/cmd/tailscale \
RUN sh build_dist.sh tailscale.com/cmd/tailscale && sh build_dist.sh tailscale.com/cmd/tailscaled \
RUN sh build_dist.sh tailscale.com/cmd/tailscaled && cp tailscale /usr/local/bin/ \
&& cp tailscaled /usr/local/bin/
RUN cp tailscale /usr/local/bin/
RUN cp tailscaled /usr/local/bin/
ADD integration_test/etc_embedded_derp/tls/server.crt /usr/local/share/ca-certificates/
RUN chmod 644 /usr/local/share/ca-certificates/server.crt
RUN update-ca-certificates

View File

@@ -24,31 +24,9 @@ build:
dev: lint test build dev: lint test build
test: test:
@go test $(TAGS) -short -coverprofile=coverage.out ./... gotestsum -- $(TAGS) -short -coverprofile=coverage.out ./...
test_integration: test_integration_cli test_integration_derp test_integration_v2_general test_integration:
test_integration_cli:
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v ~/.cache/hs-integration-go:/go \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go run gotest.tools/gotestsum@latest -- $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationCLI ./...
test_integration_derp:
docker network rm $$(docker network ls --filter name=headscale --quiet) || true
docker network create headscale-test || true
docker run -t --rm \
--network headscale-test \
-v ~/.cache/hs-integration-go:/go \
-v $$PWD:$$PWD -w $$PWD \
-v /var/run/docker.sock:/var/run/docker.sock golang:1 \
go run gotest.tools/gotestsum@latest -- $(TAGS) -failfast -timeout 30m -count=1 -run IntegrationDERP ./...
test_integration_v2_general:
docker run \ docker run \
-t --rm \ -t --rm \
-v ~/.cache/hs-integration-go:/go \ -v ~/.cache/hs-integration-go:/go \

View File

@@ -32,22 +32,18 @@ organisation.
## Design goal ## Design goal
`headscale` aims to implement a self-hosted, open source alternative to the Tailscale Headscale aims to implement a self-hosted, open source alternative to the Tailscale
control server. `headscale` has a narrower scope and an instance of `headscale` control server.
implements a _single_ Tailnet, which is typically what a single organisation, or Headscale's goal is to provide self-hosters and hobbyists with an open-source
home/personal setup would use. server they can use for their projects and labs.
It implements a narrow scope, a single Tailnet, suitable for a personal use, or a small
open-source organisation.
`headscale` uses terms that maps to Tailscale's control server, consult the ## Supporting Headscale
[glossary](./docs/glossary.md) for explainations.
## Support
If you like `headscale` and find it useful, there is a sponsorship and donation If you like `headscale` and find it useful, there is a sponsorship and donation
buttons available in the repo. buttons available in the repo.
If you would like to sponsor features, bugs or prioritisation, reach out to
one of the maintainers.
## Features ## Features
- Full "base" support of Tailscale's features - Full "base" support of Tailscale's features
@@ -79,16 +75,10 @@ one of the maintainers.
## Running headscale ## Running headscale
Please have a look at the documentation under [`docs/`](docs/). **Please note that we do not support nor encourage the use of reverse proxies
and container to run Headscale.**
## Graphical Control Panels Please have a look at the [`documentation`](https://headscale.net/).
Headscale provides an API for complete management of your Tailnet.
These are community projects not directly affiliated with the Headscale project.
| Name | Repository Link | Description | Status |
| --------------- | ---------------------------------------------------- | ------------------------------------------------------ | ------ |
| headscale-webui | [Github](https://github.com/ifargle/headscale-webui) | A simple Headscale web UI for small-scale deployments. | Alpha |
## Talks ## Talks
@@ -97,11 +87,23 @@ These are community projects not directly affiliated with the Headscale project.
## Disclaimer ## Disclaimer
1. We have nothing to do with Tailscale, or Tailscale Inc. 1. This project is not associated with Tailscale Inc.
2. The purpose of Headscale is maintaining a working, self-hosted Tailscale control panel. 2. The purpose of Headscale is maintaining a working, self-hosted Tailscale control panel.
## Contributing ## Contributing
Headscale is "Open Source, acknowledged contribution", this means that any
contribution will have to be discussed with the Maintainers before being submitted.
This model has been chosen to reduce the risk of burnout by limiting the
maintenance overhead of reviewing and validating third-party code.
Headscale is open to code contributions for bug fixes without discussion.
If you find mistakes in the documentation, please submit a fix to the documentation.
### Requirements
To contribute to headscale you would need the lastest version of [Go](https://golang.org) To contribute to headscale you would need the lastest version of [Go](https://golang.org)
and [Buf](https://buf.build)(Protobuf generator). and [Buf](https://buf.build)(Protobuf generator).
@@ -109,8 +111,6 @@ We recommend using [Nix](https://nixos.org/) to setup a development environment.
be done with `nix develop`, which will install the tools and give you a shell. be done with `nix develop`, which will install the tools and give you a shell.
This guarantees that you will have the same dev env as `headscale` maintainers. This guarantees that you will have the same dev env as `headscale` maintainers.
PRs and suggestions are welcome.
### Code style ### Code style
To ensure we have some consistency with a growing number of contributions, To ensure we have some consistency with a growing number of contributions,

View File

@@ -0,0 +1,47 @@
package main
import (
"log"
"github.com/juanfont/headscale/integration"
"github.com/juanfont/headscale/integration/tsic"
"github.com/ory/dockertest/v3"
)
func main() {
log.Printf("creating docker pool")
pool, err := dockertest.NewPool("")
if err != nil {
log.Fatalf("could not connect to docker: %s", err)
}
log.Printf("creating docker network")
network, err := pool.CreateNetwork("docker-integration-net")
if err != nil {
log.Fatalf("failed to create or get network: %s", err)
}
for _, version := range integration.TailscaleVersions {
log.Printf("creating container image for Tailscale (%s)", version)
tsClient, err := tsic.New(
pool,
version,
network,
)
if err != nil {
log.Fatalf("failed to create tailscale node: %s", err)
}
err = tsClient.Shutdown()
if err != nil {
log.Fatalf("failed to shut down container: %s", err)
}
}
network.Close()
err = pool.RemoveNetwork(network)
if err != nil {
log.Fatalf("failed to remove network: %s", err)
}
}

View File

@@ -64,7 +64,7 @@ jobs:
--volume /var/run/docker.sock:/var/run/docker.sock \ --volume /var/run/docker.sock:/var/run/docker.sock \
--volume $PWD/control_logs:/tmp/control \ --volume $PWD/control_logs:/tmp/control \
golang:1 \ golang:1 \
go test ./... \ go run gotest.tools/gotestsum@latest -- ./... \
-tags ts2019 \ -tags ts2019 \
-failfast \ -failfast \
-timeout 120m \ -timeout 120m \
@@ -76,6 +76,12 @@ jobs:
with: with:
name: logs name: logs
path: "control_logs/*.log" path: "control_logs/*.log"
- uses: actions/upload-artifact@v3
if: always() && steps.changed-files.outputs.any_changed == 'true'
with:
name: pprof
path: "control_logs/*.pprof.tar"
`), `),
) )
) )

View File

@@ -5,8 +5,8 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@@ -83,7 +83,7 @@ var listAPIKeys = &cobra.Command{
} }
tableData = append(tableData, []string{ tableData = append(tableData, []string{
strconv.FormatUint(key.GetId(), headscale.Base10), strconv.FormatUint(key.GetId(), hscontrol.Base10),
key.GetPrefix(), key.GetPrefix(),
expiration, expiration,
key.GetCreatedAt().AsTime().Format(HeadscaleDateTimeFormat), key.GetCreatedAt().AsTime().Format(HeadscaleDateTimeFormat),

View File

@@ -3,8 +3,8 @@ package cli
import ( import (
"fmt" "fmt"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -93,7 +93,7 @@ var createNodeCmd = &cobra.Command{
return return
} }
if !headscale.NodePublicKeyRegex.Match([]byte(machineKey)) { if !hscontrol.NodePublicKeyRegex.Match([]byte(machineKey)) {
err = errPreAuthKeyMalformed err = errPreAuthKeyMalformed
ErrorOutput( ErrorOutput(
err, err,

View File

@@ -9,8 +9,8 @@ import (
"time" "time"
survey "github.com/AlecAivazis/survey/v2" survey "github.com/AlecAivazis/survey/v2"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -529,7 +529,7 @@ func nodesToPtables(
var machineKey key.MachinePublic var machineKey key.MachinePublic
err := machineKey.UnmarshalText( err := machineKey.UnmarshalText(
[]byte(headscale.MachinePublicKeyEnsurePrefix(machine.MachineKey)), []byte(hscontrol.MachinePublicKeyEnsurePrefix(machine.MachineKey)),
) )
if err != nil { if err != nil {
machineKey = key.MachinePublic{} machineKey = key.MachinePublic{}
@@ -537,7 +537,7 @@ func nodesToPtables(
var nodeKey key.NodePublic var nodeKey key.NodePublic
err = nodeKey.UnmarshalText( err = nodeKey.UnmarshalText(
[]byte(headscale.NodePublicKeyEnsurePrefix(machine.NodeKey)), []byte(hscontrol.NodePublicKeyEnsurePrefix(machine.NodeKey)),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@@ -596,7 +596,7 @@ func nodesToPtables(
} }
nodeData := []string{ nodeData := []string{
strconv.FormatUint(machine.Id, headscale.Base10), strconv.FormatUint(machine.Id, hscontrol.Base10),
machine.Name, machine.Name,
machine.GetGivenName(), machine.GetGivenName(),
machineKey.ShortString(), machineKey.ShortString(),

View File

@@ -5,7 +5,7 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/juanfont/headscale" "github.com/juanfont/headscale/hscontrol"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -38,18 +38,18 @@ func initConfig() {
cfgFile = os.Getenv("HEADSCALE_CONFIG") cfgFile = os.Getenv("HEADSCALE_CONFIG")
} }
if cfgFile != "" { if cfgFile != "" {
err := headscale.LoadConfig(cfgFile, true) err := hscontrol.LoadConfig(cfgFile, true)
if err != nil { if err != nil {
log.Fatal().Caller().Err(err).Msgf("Error loading config file %s", cfgFile) log.Fatal().Caller().Err(err).Msgf("Error loading config file %s", cfgFile)
} }
} else { } else {
err := headscale.LoadConfig("", false) err := hscontrol.LoadConfig("", false)
if err != nil { if err != nil {
log.Fatal().Caller().Err(err).Msgf("Error loading config") log.Fatal().Caller().Err(err).Msgf("Error loading config")
} }
} }
cfg, err := headscale.GetHeadscaleConfig() cfg, err := hscontrol.GetHeadscaleConfig()
if err != nil { if err != nil {
log.Fatal().Caller().Err(err) log.Fatal().Caller().Err(err)
} }
@@ -64,7 +64,7 @@ func initConfig() {
zerolog.SetGlobalLevel(zerolog.Disabled) zerolog.SetGlobalLevel(zerolog.Disabled)
} }
if cfg.Log.Format == headscale.JSONLogFormat { if cfg.Log.Format == hscontrol.JSONLogFormat {
log.Logger = log.Output(os.Stdout) log.Logger = log.Output(os.Stdout)
} }

View File

@@ -6,8 +6,8 @@ import (
"net/netip" "net/netip"
"strconv" "strconv"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@@ -277,7 +277,7 @@ func routesToPtables(routes []*v1.Route) pterm.TableData {
continue continue
} }
if prefix == headscale.ExitRouteV4 || prefix == headscale.ExitRouteV6 { if prefix == hscontrol.ExitRouteV4 || prefix == hscontrol.ExitRouteV6 {
isPrimaryStr = "-" isPrimaryStr = "-"
} else { } else {
isPrimaryStr = strconv.FormatBool(route.IsPrimary) isPrimaryStr = strconv.FormatBool(route.IsPrimary)

View File

@@ -4,8 +4,8 @@ import (
"fmt" "fmt"
survey "github.com/AlecAivazis/survey/v2" survey "github.com/AlecAivazis/survey/v2"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/pterm/pterm" "github.com/pterm/pterm"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -21,7 +21,7 @@ func init() {
} }
const ( const (
errMissingParameter = headscale.Error("missing parameters") errMissingParameter = hscontrol.Error("missing parameters")
) )
var userCmd = &cobra.Command{ var userCmd = &cobra.Command{

View File

@@ -8,8 +8,8 @@ import (
"os" "os"
"reflect" "reflect"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
@@ -22,8 +22,8 @@ const (
SocketWritePermissions = 0o666 SocketWritePermissions = 0o666
) )
func getHeadscaleApp() (*headscale.Headscale, error) { func getHeadscaleApp() (*hscontrol.Headscale, error) {
cfg, err := headscale.GetHeadscaleConfig() cfg, err := hscontrol.GetHeadscaleConfig()
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"failed to load configuration while creating headscale instance: %w", "failed to load configuration while creating headscale instance: %w",
@@ -31,7 +31,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
) )
} }
app, err := headscale.NewHeadscale(cfg) app, err := hscontrol.NewHeadscale(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -39,8 +39,8 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
// We are doing this here, as in the future could be cool to have it also hot-reload // We are doing this here, as in the future could be cool to have it also hot-reload
if cfg.ACL.PolicyPath != "" { if cfg.ACL.PolicyPath != "" {
aclPath := headscale.AbsolutePathFromConfigPath(cfg.ACL.PolicyPath) aclPath := hscontrol.AbsolutePathFromConfigPath(cfg.ACL.PolicyPath)
err = app.LoadACLPolicy(aclPath) err = app.LoadACLPolicyFromPath(aclPath)
if err != nil { if err != nil {
log.Fatal(). log.Fatal().
Str("path", aclPath). Str("path", aclPath).
@@ -53,7 +53,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
} }
func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc.ClientConn, context.CancelFunc) { func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc.ClientConn, context.CancelFunc) {
cfg, err := headscale.GetHeadscaleConfig() cfg, err := hscontrol.GetHeadscaleConfig()
if err != nil { if err != nil {
log.Fatal(). log.Fatal().
Err(err). Err(err).
@@ -74,7 +74,7 @@ func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc.
address := cfg.CLI.Address address := cfg.CLI.Address
// If the address is not set, we assume that we are on the server hosting headscale. // If the address is not set, we assume that we are on the server hosting hscontrol.
if address == "" { if address == "" {
log.Debug(). log.Debug().
Str("socket", cfg.UnixSocket). Str("socket", cfg.UnixSocket).
@@ -98,7 +98,7 @@ func getHeadscaleCLIClient() (context.Context, v1.HeadscaleServiceClient, *grpc.
grpcOptions = append( grpcOptions = append(
grpcOptions, grpcOptions,
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(headscale.GrpcSocketDialer), grpc.WithContextDialer(hscontrol.GrpcSocketDialer),
) )
} else { } else {
// If we are not connecting to a local server, require an API key for authentication // If we are not connecting to a local server, require an API key for authentication

View File

@@ -6,11 +6,25 @@ import (
"github.com/efekarakus/termcolor" "github.com/efekarakus/termcolor"
"github.com/juanfont/headscale/cmd/headscale/cli" "github.com/juanfont/headscale/cmd/headscale/cli"
"github.com/pkg/profile"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func main() { func main() {
if _, enableProfile := os.LookupEnv("HEADSCALE_PROFILING_ENABLED"); enableProfile {
if profilePath, ok := os.LookupEnv("HEADSCALE_PROFILING_PATH"); ok {
err := os.MkdirAll(profilePath, os.ModePerm)
if err != nil {
log.Fatal().Err(err).Msg("failed to create profiling directory")
}
defer profile.Start(profile.ProfilePath(profilePath)).Stop()
} else {
defer profile.Start().Stop()
}
}
var colors bool var colors bool
switch l := termcolor.SupportLevel(os.Stderr); l { switch l := termcolor.SupportLevel(os.Stderr); l {
case termcolor.Level16M: case termcolor.Level16M:

View File

@@ -7,7 +7,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/juanfont/headscale" "github.com/juanfont/headscale/hscontrol"
"github.com/spf13/viper" "github.com/spf13/viper"
"gopkg.in/check.v1" "gopkg.in/check.v1"
) )
@@ -50,7 +50,7 @@ func (*Suite) TestConfigFileLoading(c *check.C) {
} }
// Load example config, it should load without validation errors // Load example config, it should load without validation errors
err = headscale.LoadConfig(cfgFile, true) err = hscontrol.LoadConfig(cfgFile, true)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// Test that config file was interpreted correctly // Test that config file was interpreted correctly
@@ -64,7 +64,7 @@ func (*Suite) TestConfigFileLoading(c *check.C) {
c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01")
c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1")
c.Assert( c.Assert(
headscale.GetFileMode("unix_socket_permission"), hscontrol.GetFileMode("unix_socket_permission"),
check.Equals, check.Equals,
fs.FileMode(0o770), fs.FileMode(0o770),
) )
@@ -93,7 +93,7 @@ func (*Suite) TestConfigLoading(c *check.C) {
} }
// Load example config, it should load without validation errors // Load example config, it should load without validation errors
err = headscale.LoadConfig(tmpDir, false) err = hscontrol.LoadConfig(tmpDir, false)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
// Test that config file was interpreted correctly // Test that config file was interpreted correctly
@@ -107,7 +107,7 @@ func (*Suite) TestConfigLoading(c *check.C) {
c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01") c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01")
c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1") c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1")
c.Assert( c.Assert(
headscale.GetFileMode("unix_socket_permission"), hscontrol.GetFileMode("unix_socket_permission"),
check.Equals, check.Equals,
fs.FileMode(0o770), fs.FileMode(0o770),
) )
@@ -137,10 +137,10 @@ func (*Suite) TestDNSConfigLoading(c *check.C) {
} }
// Load example config, it should load without validation errors // Load example config, it should load without validation errors
err = headscale.LoadConfig(tmpDir, false) err = hscontrol.LoadConfig(tmpDir, false)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
dnsConfig, baseDomain := headscale.GetDNSConfig() dnsConfig, baseDomain := hscontrol.GetDNSConfig()
c.Assert(dnsConfig.Nameservers[0].String(), check.Equals, "1.1.1.1") c.Assert(dnsConfig.Nameservers[0].String(), check.Equals, "1.1.1.1")
c.Assert(dnsConfig.Resolvers[0].Addr, check.Equals, "1.1.1.1") c.Assert(dnsConfig.Resolvers[0].Addr, check.Equals, "1.1.1.1")
@@ -172,7 +172,7 @@ noise:
writeConfig(c, tmpDir, configYaml) writeConfig(c, tmpDir, configYaml)
// Check configuration validation errors (1) // Check configuration validation errors (1)
err = headscale.LoadConfig(tmpDir, false) err = hscontrol.LoadConfig(tmpDir, false)
c.Assert(err, check.NotNil) c.Assert(err, check.NotNil)
// check.Matches can not handle multiline strings // check.Matches can not handle multiline strings
tmp := strings.ReplaceAll(err.Error(), "\n", "***") tmp := strings.ReplaceAll(err.Error(), "\n", "***")
@@ -201,6 +201,6 @@ tls_letsencrypt_hostname: example.com
tls_letsencrypt_challenge_type: TLS-ALPN-01 tls_letsencrypt_challenge_type: TLS-ALPN-01
`) `)
writeConfig(c, tmpDir, configYaml) writeConfig(c, tmpDir, configYaml)
err = headscale.LoadConfig(tmpDir, false) err = hscontrol.LoadConfig(tmpDir, false)
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
} }

View File

@@ -58,11 +58,12 @@ noise:
# List of IP prefixes to allocate tailaddresses from. # List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address, # Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash. # and the associated prefix length, delimited by a slash.
# While this looks like it can take arbitrary values, it # It must be within IP ranges supported by the Tailscale
# needs to be within IP ranges supported by the Tailscale # client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48.
# client. # See below:
# IPv6: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#LL81C52-L81C71 # IPv6: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#LL81C52-L81C71
# IPv4: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#L33 # IPv4: https://github.com/tailscale/tailscale/blob/22ebb25e833264f58d7c3f534a8b166894a89536/net/tsaddr/tsaddr.go#L33
# Any other range is NOT supported, and it will cause unexpected issues.
ip_prefixes: ip_prefixes:
- fd7a:115c:a1e0::/48 - fd7a:115c:a1e0::/48
- 100.64.0.0/10 - 100.64.0.0/10

View File

@@ -14,6 +14,8 @@ If the node is already registered, it can advertise exit capabilities like this:
$ sudo tailscale set --advertise-exit-node $ sudo tailscale set --advertise-exit-node
``` ```
To use a node as an exit node, IP forwarding must be enabled on the node. Check the official [Tailscale documentation](https://tailscale.com/kb/1019/subnets/?tab=linux#enable-ip-forwarding) for how to enable IP fowarding.
## On the control server ## On the control server
```console ```console

53
docs/faq.md Normal file
View File

@@ -0,0 +1,53 @@
---
hide:
- navigation
---
# Frequently Asked Questions
## What is the design goal of headscale?
`headscale` aims to implement a self-hosted, open source alternative to the [Tailscale](https://tailscale.com/)
control server.
`headscale`'s goal is to provide self-hosters and hobbyists with an open-source
server they can use for their projects and labs.
It implements a narrow scope, a _single_ Tailnet, suitable for a personal use, or a small
open-source organisation.
## How can I contribute?
Headscale is "Open Source, acknowledged contribution", this means that any
contribution will have to be discussed with the Maintainers before being submitted.
Headscale is open to code contributions for bug fixes without discussion.
If you find mistakes in the documentation, please also submit a fix to the documentation.
## Why is 'acknowledged contribution' the chosen model?
Both maintainers have full-time jobs and families, and we want to avoid burnout. We also want to avoid frustration from contributors when their PRs are not accepted.
We are more than happy to exchange emails, or to have dedicated calls before a PR is submitted.
## When/Why is Feature X going to be implemented?
We don't know. We might be working on it. If you want to help, please send us a PR.
Please be aware that there are a number of reasons why we might not accept specific contributions:
- It is not possible to implement the feature in a way that makes sense in a self-hosted environment.
- Given that we are reverse-engineering Tailscale to satify our own curiosity, we might be interested in implementing the feature ourselves.
- You are not sending unit and integration tests with it.
## Do you support Y method of deploying Headscale?
We currently support deploying `headscale` using our binaries and the DEB packages. Both can be found in the
[GitHub releases page](https://github.com/juanfont/headscale/releases).
In addition to that, there are semi-official RPM packages by the Fedora infra team https://copr.fedorainfracloud.org/coprs/jonathanspw/headscale/
For convenience, we also build Docker images with `headscale`. But **please be aware that we don't officially support deploying `headscale` using Docker**. We have a [Discord channel](https://discord.com/channels/896711691637780480/1070619770942148618) where you can ask for Docker-specific help to the community.
## Why is my reverse proxy not working with Headscale?
We don't know. We don't use reverse proxies with `headscale` ourselves, so we don't have any experience with them. We have [community documentation](https://headscale.net/reverse-proxy/) on how to configure various reverse proxies, and a dedicated [Discord channel](https://discord.com/channels/896711691637780480/1070619818346164324) where you can ask for help to the community.

View File

@@ -4,9 +4,40 @@ hide:
- toc - toc
--- ---
# headscale documentation # headscale
This site contains the official and community contributed documentation for `headscale`. `headscale` is an open source, self-hosted implementation of the Tailscale control server.
If you are having trouble with following the documentation or get unexpected results, This page contains the documentation for the latest version of headscale. Please also check our [FAQ](/faq/).
please ask on [Discord](https://discord.gg/c84AZQhmpx) instead of opening an Issue.
Join our [Discord](https://discord.gg/c84AZQhmpx) server for a chat and community support.
## Design goal
Headscale aims to implement a self-hosted, open source alternative to the Tailscale
control server.
Headscale's goal is to provide self-hosters and hobbyists with an open-source
server they can use for their projects and labs.
It implements a narrower scope, a single Tailnet, suitable for a personal use, or a small
open-source organisation.
## Supporting headscale
If you like `headscale` and find it useful, there is a sponsorship and donation
buttons available in the repo.
## Contributing
Headscale is "Open Source, acknowledged contribution", this means that any
contribution will have to be discussed with the Maintainers before being submitted.
This model has been chosen to reduce the risk of burnout by limiting the
maintenance overhead of reviewing and validating third-party code.
Headscale is open to code contributions for bug fixes without discussion.
If you find mistakes in the documentation, please submit a fix to the documentation.
## About
`headscale` is maintained by [Kristoffer Dalby](https://kradalby.no/) and [Juan Font](https://font.eu).

View File

@@ -16,7 +16,7 @@ WorkingDirectory=/var/lib/headscale
ReadWritePaths=/var/lib/headscale /var/run ReadWritePaths=/var/lib/headscale /var/run
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_CHOWN AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_CHOWN
CapabilityBoundingSet=CAP_CHOWN CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_CHOWN
LockPersonality=true LockPersonality=true
NoNewPrivileges=true NoNewPrivileges=true
PrivateDevices=true PrivateDevices=true

View File

@@ -20,7 +20,7 @@ configuration (`/etc/headscale/config.yaml`).
## Installation ## Installation
1. Download the lastest Headscale package for your platform (`.deb` for Ubuntu and Debian) from [Headscale's releases page](): 1. Download the lastest Headscale package for your platform (`.deb` for Ubuntu and Debian) from [Headscale's releases page](https://github.com/juanfont/headscale/releases):
```shell ```shell
wget --output-document=headscale.deb \ wget --output-document=headscale.deb \

14
docs/web-ui.md Normal file
View File

@@ -0,0 +1,14 @@
# Headscale web interface
!!! warning "Community contributions"
This page contains community contributions. The projects listed here are not
maintained by the Headscale authors and are written by community members.
| Name | Repository Link | Description | Status |
| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------- | ------ |
| headscale-webui | [Github](https://github.com/ifargle/headscale-webui) | A simple Headscale web UI for small-scale deployments. | Alpha |
| headscale-ui | [Github](https://github.com/gurucomputing/headscale-ui) | A web frontend for the headscale Tailscale-compatible coordination server | Alpha |
| HeadscaleUi | [GitHub](https://github.com/simcu/headscale-ui) | A static headscale admin ui, no backend enviroment required | Alpha |
You can ask for support on our dedicated [Discord channel](https://discord.com/channels/896711691637780480/1105842846386356294).

View File

@@ -36,7 +36,7 @@
# When updating go.mod or go.sum, a new sha will need to be calculated, # 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. # update this if you have a mismatch after doing a change to thos files.
vendorSha256 = "sha256-+JxS4Q6rTpdBwms2nkVDY/Kluv2qu2T0BaOIjfeX85M="; vendorSha256 = "sha256-IOkbbFtE6+tNKnglE/8ZuNxhPSnloqM2sLgTvagMmnc=";
ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ]; ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
}; };
@@ -99,6 +99,11 @@
goreleaser goreleaser
nfpm nfpm
gotestsum gotestsum
gotests
# 'dot' is needed for pprof graphs
# go tool pprof -http=: <source>
graphviz
# Protobuf dependencies # Protobuf dependencies
protobuf protobuf

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/apikey.proto // source: headscale/v1/apikey.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/device.proto // source: headscale/v1/device.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/headscale.proto // source: headscale/v1/headscale.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.2.0 // - protoc-gen-go-grpc v1.3.0
// - protoc (unknown) // - protoc (unknown)
// source: headscale/v1/headscale.proto // source: headscale/v1/headscale.proto
@@ -18,6 +18,34 @@ import (
// Requires gRPC-Go v1.32.0 or later. // Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7 const _ = grpc.SupportPackageIsVersion7
const (
HeadscaleService_GetUser_FullMethodName = "/headscale.v1.HeadscaleService/GetUser"
HeadscaleService_CreateUser_FullMethodName = "/headscale.v1.HeadscaleService/CreateUser"
HeadscaleService_RenameUser_FullMethodName = "/headscale.v1.HeadscaleService/RenameUser"
HeadscaleService_DeleteUser_FullMethodName = "/headscale.v1.HeadscaleService/DeleteUser"
HeadscaleService_ListUsers_FullMethodName = "/headscale.v1.HeadscaleService/ListUsers"
HeadscaleService_CreatePreAuthKey_FullMethodName = "/headscale.v1.HeadscaleService/CreatePreAuthKey"
HeadscaleService_ExpirePreAuthKey_FullMethodName = "/headscale.v1.HeadscaleService/ExpirePreAuthKey"
HeadscaleService_ListPreAuthKeys_FullMethodName = "/headscale.v1.HeadscaleService/ListPreAuthKeys"
HeadscaleService_DebugCreateMachine_FullMethodName = "/headscale.v1.HeadscaleService/DebugCreateMachine"
HeadscaleService_GetMachine_FullMethodName = "/headscale.v1.HeadscaleService/GetMachine"
HeadscaleService_SetTags_FullMethodName = "/headscale.v1.HeadscaleService/SetTags"
HeadscaleService_RegisterMachine_FullMethodName = "/headscale.v1.HeadscaleService/RegisterMachine"
HeadscaleService_DeleteMachine_FullMethodName = "/headscale.v1.HeadscaleService/DeleteMachine"
HeadscaleService_ExpireMachine_FullMethodName = "/headscale.v1.HeadscaleService/ExpireMachine"
HeadscaleService_RenameMachine_FullMethodName = "/headscale.v1.HeadscaleService/RenameMachine"
HeadscaleService_ListMachines_FullMethodName = "/headscale.v1.HeadscaleService/ListMachines"
HeadscaleService_MoveMachine_FullMethodName = "/headscale.v1.HeadscaleService/MoveMachine"
HeadscaleService_GetRoutes_FullMethodName = "/headscale.v1.HeadscaleService/GetRoutes"
HeadscaleService_EnableRoute_FullMethodName = "/headscale.v1.HeadscaleService/EnableRoute"
HeadscaleService_DisableRoute_FullMethodName = "/headscale.v1.HeadscaleService/DisableRoute"
HeadscaleService_GetMachineRoutes_FullMethodName = "/headscale.v1.HeadscaleService/GetMachineRoutes"
HeadscaleService_DeleteRoute_FullMethodName = "/headscale.v1.HeadscaleService/DeleteRoute"
HeadscaleService_CreateApiKey_FullMethodName = "/headscale.v1.HeadscaleService/CreateApiKey"
HeadscaleService_ExpireApiKey_FullMethodName = "/headscale.v1.HeadscaleService/ExpireApiKey"
HeadscaleService_ListApiKeys_FullMethodName = "/headscale.v1.HeadscaleService/ListApiKeys"
)
// HeadscaleServiceClient is the client API for HeadscaleService service. // HeadscaleServiceClient is the client API for HeadscaleService service.
// //
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
@@ -64,7 +92,7 @@ func NewHeadscaleServiceClient(cc grpc.ClientConnInterface) HeadscaleServiceClie
func (c *headscaleServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { func (c *headscaleServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) {
out := new(GetUserResponse) out := new(GetUserResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetUser", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_GetUser_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -73,7 +101,7 @@ func (c *headscaleServiceClient) GetUser(ctx context.Context, in *GetUserRequest
func (c *headscaleServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) { func (c *headscaleServiceClient) CreateUser(ctx context.Context, in *CreateUserRequest, opts ...grpc.CallOption) (*CreateUserResponse, error) {
out := new(CreateUserResponse) out := new(CreateUserResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreateUser", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_CreateUser_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -82,7 +110,7 @@ func (c *headscaleServiceClient) CreateUser(ctx context.Context, in *CreateUserR
func (c *headscaleServiceClient) RenameUser(ctx context.Context, in *RenameUserRequest, opts ...grpc.CallOption) (*RenameUserResponse, error) { func (c *headscaleServiceClient) RenameUser(ctx context.Context, in *RenameUserRequest, opts ...grpc.CallOption) (*RenameUserResponse, error) {
out := new(RenameUserResponse) out := new(RenameUserResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RenameUser", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_RenameUser_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -91,7 +119,7 @@ func (c *headscaleServiceClient) RenameUser(ctx context.Context, in *RenameUserR
func (c *headscaleServiceClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) { func (c *headscaleServiceClient) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*DeleteUserResponse, error) {
out := new(DeleteUserResponse) out := new(DeleteUserResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteUser", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_DeleteUser_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -100,7 +128,7 @@ func (c *headscaleServiceClient) DeleteUser(ctx context.Context, in *DeleteUserR
func (c *headscaleServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) { func (c *headscaleServiceClient) ListUsers(ctx context.Context, in *ListUsersRequest, opts ...grpc.CallOption) (*ListUsersResponse, error) {
out := new(ListUsersResponse) out := new(ListUsersResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListUsers", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ListUsers_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -109,7 +137,7 @@ func (c *headscaleServiceClient) ListUsers(ctx context.Context, in *ListUsersReq
func (c *headscaleServiceClient) CreatePreAuthKey(ctx context.Context, in *CreatePreAuthKeyRequest, opts ...grpc.CallOption) (*CreatePreAuthKeyResponse, error) { func (c *headscaleServiceClient) CreatePreAuthKey(ctx context.Context, in *CreatePreAuthKeyRequest, opts ...grpc.CallOption) (*CreatePreAuthKeyResponse, error) {
out := new(CreatePreAuthKeyResponse) out := new(CreatePreAuthKeyResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreatePreAuthKey", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_CreatePreAuthKey_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -118,7 +146,7 @@ func (c *headscaleServiceClient) CreatePreAuthKey(ctx context.Context, in *Creat
func (c *headscaleServiceClient) ExpirePreAuthKey(ctx context.Context, in *ExpirePreAuthKeyRequest, opts ...grpc.CallOption) (*ExpirePreAuthKeyResponse, error) { func (c *headscaleServiceClient) ExpirePreAuthKey(ctx context.Context, in *ExpirePreAuthKeyRequest, opts ...grpc.CallOption) (*ExpirePreAuthKeyResponse, error) {
out := new(ExpirePreAuthKeyResponse) out := new(ExpirePreAuthKeyResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpirePreAuthKey", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ExpirePreAuthKey_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -127,7 +155,7 @@ func (c *headscaleServiceClient) ExpirePreAuthKey(ctx context.Context, in *Expir
func (c *headscaleServiceClient) ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) { func (c *headscaleServiceClient) ListPreAuthKeys(ctx context.Context, in *ListPreAuthKeysRequest, opts ...grpc.CallOption) (*ListPreAuthKeysResponse, error) {
out := new(ListPreAuthKeysResponse) out := new(ListPreAuthKeysResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListPreAuthKeys", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ListPreAuthKeys_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -136,7 +164,7 @@ func (c *headscaleServiceClient) ListPreAuthKeys(ctx context.Context, in *ListPr
func (c *headscaleServiceClient) DebugCreateMachine(ctx context.Context, in *DebugCreateMachineRequest, opts ...grpc.CallOption) (*DebugCreateMachineResponse, error) { func (c *headscaleServiceClient) DebugCreateMachine(ctx context.Context, in *DebugCreateMachineRequest, opts ...grpc.CallOption) (*DebugCreateMachineResponse, error) {
out := new(DebugCreateMachineResponse) out := new(DebugCreateMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DebugCreateMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_DebugCreateMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -145,7 +173,7 @@ func (c *headscaleServiceClient) DebugCreateMachine(ctx context.Context, in *Deb
func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) { func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineRequest, opts ...grpc.CallOption) (*GetMachineResponse, error) {
out := new(GetMachineResponse) out := new(GetMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_GetMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -154,7 +182,7 @@ func (c *headscaleServiceClient) GetMachine(ctx context.Context, in *GetMachineR
func (c *headscaleServiceClient) SetTags(ctx context.Context, in *SetTagsRequest, opts ...grpc.CallOption) (*SetTagsResponse, error) { func (c *headscaleServiceClient) SetTags(ctx context.Context, in *SetTagsRequest, opts ...grpc.CallOption) (*SetTagsResponse, error) {
out := new(SetTagsResponse) out := new(SetTagsResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/SetTags", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_SetTags_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -163,7 +191,7 @@ func (c *headscaleServiceClient) SetTags(ctx context.Context, in *SetTagsRequest
func (c *headscaleServiceClient) RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error) { func (c *headscaleServiceClient) RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error) {
out := new(RegisterMachineResponse) out := new(RegisterMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RegisterMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_RegisterMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -172,7 +200,7 @@ func (c *headscaleServiceClient) RegisterMachine(ctx context.Context, in *Regist
func (c *headscaleServiceClient) DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) { func (c *headscaleServiceClient) DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) {
out := new(DeleteMachineResponse) out := new(DeleteMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_DeleteMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -181,7 +209,7 @@ func (c *headscaleServiceClient) DeleteMachine(ctx context.Context, in *DeleteMa
func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) { func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error) {
out := new(ExpireMachineResponse) out := new(ExpireMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpireMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ExpireMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -190,7 +218,7 @@ func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMa
func (c *headscaleServiceClient) RenameMachine(ctx context.Context, in *RenameMachineRequest, opts ...grpc.CallOption) (*RenameMachineResponse, error) { func (c *headscaleServiceClient) RenameMachine(ctx context.Context, in *RenameMachineRequest, opts ...grpc.CallOption) (*RenameMachineResponse, error) {
out := new(RenameMachineResponse) out := new(RenameMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RenameMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_RenameMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -199,7 +227,7 @@ func (c *headscaleServiceClient) RenameMachine(ctx context.Context, in *RenameMa
func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) { func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) {
out := new(ListMachinesResponse) out := new(ListMachinesResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListMachines", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ListMachines_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -208,7 +236,7 @@ func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachi
func (c *headscaleServiceClient) MoveMachine(ctx context.Context, in *MoveMachineRequest, opts ...grpc.CallOption) (*MoveMachineResponse, error) { func (c *headscaleServiceClient) MoveMachine(ctx context.Context, in *MoveMachineRequest, opts ...grpc.CallOption) (*MoveMachineResponse, error) {
out := new(MoveMachineResponse) out := new(MoveMachineResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/MoveMachine", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_MoveMachine_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -217,7 +245,7 @@ func (c *headscaleServiceClient) MoveMachine(ctx context.Context, in *MoveMachin
func (c *headscaleServiceClient) GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error) { func (c *headscaleServiceClient) GetRoutes(ctx context.Context, in *GetRoutesRequest, opts ...grpc.CallOption) (*GetRoutesResponse, error) {
out := new(GetRoutesResponse) out := new(GetRoutesResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetRoutes", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_GetRoutes_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -226,7 +254,7 @@ func (c *headscaleServiceClient) GetRoutes(ctx context.Context, in *GetRoutesReq
func (c *headscaleServiceClient) EnableRoute(ctx context.Context, in *EnableRouteRequest, opts ...grpc.CallOption) (*EnableRouteResponse, error) { func (c *headscaleServiceClient) EnableRoute(ctx context.Context, in *EnableRouteRequest, opts ...grpc.CallOption) (*EnableRouteResponse, error) {
out := new(EnableRouteResponse) out := new(EnableRouteResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/EnableRoute", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_EnableRoute_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -235,7 +263,7 @@ func (c *headscaleServiceClient) EnableRoute(ctx context.Context, in *EnableRout
func (c *headscaleServiceClient) DisableRoute(ctx context.Context, in *DisableRouteRequest, opts ...grpc.CallOption) (*DisableRouteResponse, error) { func (c *headscaleServiceClient) DisableRoute(ctx context.Context, in *DisableRouteRequest, opts ...grpc.CallOption) (*DisableRouteResponse, error) {
out := new(DisableRouteResponse) out := new(DisableRouteResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DisableRoute", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_DisableRoute_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -244,7 +272,7 @@ func (c *headscaleServiceClient) DisableRoute(ctx context.Context, in *DisableRo
func (c *headscaleServiceClient) GetMachineRoutes(ctx context.Context, in *GetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error) { func (c *headscaleServiceClient) GetMachineRoutes(ctx context.Context, in *GetMachineRoutesRequest, opts ...grpc.CallOption) (*GetMachineRoutesResponse, error) {
out := new(GetMachineRoutesResponse) out := new(GetMachineRoutesResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/GetMachineRoutes", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_GetMachineRoutes_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -253,7 +281,7 @@ func (c *headscaleServiceClient) GetMachineRoutes(ctx context.Context, in *GetMa
func (c *headscaleServiceClient) DeleteRoute(ctx context.Context, in *DeleteRouteRequest, opts ...grpc.CallOption) (*DeleteRouteResponse, error) { func (c *headscaleServiceClient) DeleteRoute(ctx context.Context, in *DeleteRouteRequest, opts ...grpc.CallOption) (*DeleteRouteResponse, error) {
out := new(DeleteRouteResponse) out := new(DeleteRouteResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/DeleteRoute", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_DeleteRoute_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -262,7 +290,7 @@ func (c *headscaleServiceClient) DeleteRoute(ctx context.Context, in *DeleteRout
func (c *headscaleServiceClient) CreateApiKey(ctx context.Context, in *CreateApiKeyRequest, opts ...grpc.CallOption) (*CreateApiKeyResponse, error) { func (c *headscaleServiceClient) CreateApiKey(ctx context.Context, in *CreateApiKeyRequest, opts ...grpc.CallOption) (*CreateApiKeyResponse, error) {
out := new(CreateApiKeyResponse) out := new(CreateApiKeyResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/CreateApiKey", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_CreateApiKey_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -271,7 +299,7 @@ func (c *headscaleServiceClient) CreateApiKey(ctx context.Context, in *CreateApi
func (c *headscaleServiceClient) ExpireApiKey(ctx context.Context, in *ExpireApiKeyRequest, opts ...grpc.CallOption) (*ExpireApiKeyResponse, error) { func (c *headscaleServiceClient) ExpireApiKey(ctx context.Context, in *ExpireApiKeyRequest, opts ...grpc.CallOption) (*ExpireApiKeyResponse, error) {
out := new(ExpireApiKeyResponse) out := new(ExpireApiKeyResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ExpireApiKey", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ExpireApiKey_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -280,7 +308,7 @@ func (c *headscaleServiceClient) ExpireApiKey(ctx context.Context, in *ExpireApi
func (c *headscaleServiceClient) ListApiKeys(ctx context.Context, in *ListApiKeysRequest, opts ...grpc.CallOption) (*ListApiKeysResponse, error) { func (c *headscaleServiceClient) ListApiKeys(ctx context.Context, in *ListApiKeysRequest, opts ...grpc.CallOption) (*ListApiKeysResponse, error) {
out := new(ListApiKeysResponse) out := new(ListApiKeysResponse)
err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListApiKeys", in, out, opts...) err := c.cc.Invoke(ctx, HeadscaleService_ListApiKeys_FullMethodName, in, out, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -426,7 +454,7 @@ func _HeadscaleService_GetUser_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/GetUser", FullMethod: HeadscaleService_GetUser_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).GetUser(ctx, req.(*GetUserRequest)) return srv.(HeadscaleServiceServer).GetUser(ctx, req.(*GetUserRequest))
@@ -444,7 +472,7 @@ func _HeadscaleService_CreateUser_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/CreateUser", FullMethod: HeadscaleService_CreateUser_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).CreateUser(ctx, req.(*CreateUserRequest)) return srv.(HeadscaleServiceServer).CreateUser(ctx, req.(*CreateUserRequest))
@@ -462,7 +490,7 @@ func _HeadscaleService_RenameUser_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/RenameUser", FullMethod: HeadscaleService_RenameUser_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).RenameUser(ctx, req.(*RenameUserRequest)) return srv.(HeadscaleServiceServer).RenameUser(ctx, req.(*RenameUserRequest))
@@ -480,7 +508,7 @@ func _HeadscaleService_DeleteUser_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/DeleteUser", FullMethod: HeadscaleService_DeleteUser_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).DeleteUser(ctx, req.(*DeleteUserRequest)) return srv.(HeadscaleServiceServer).DeleteUser(ctx, req.(*DeleteUserRequest))
@@ -498,7 +526,7 @@ func _HeadscaleService_ListUsers_Handler(srv interface{}, ctx context.Context, d
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ListUsers", FullMethod: HeadscaleService_ListUsers_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ListUsers(ctx, req.(*ListUsersRequest)) return srv.(HeadscaleServiceServer).ListUsers(ctx, req.(*ListUsersRequest))
@@ -516,7 +544,7 @@ func _HeadscaleService_CreatePreAuthKey_Handler(srv interface{}, ctx context.Con
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/CreatePreAuthKey", FullMethod: HeadscaleService_CreatePreAuthKey_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).CreatePreAuthKey(ctx, req.(*CreatePreAuthKeyRequest)) return srv.(HeadscaleServiceServer).CreatePreAuthKey(ctx, req.(*CreatePreAuthKeyRequest))
@@ -534,7 +562,7 @@ func _HeadscaleService_ExpirePreAuthKey_Handler(srv interface{}, ctx context.Con
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ExpirePreAuthKey", FullMethod: HeadscaleService_ExpirePreAuthKey_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ExpirePreAuthKey(ctx, req.(*ExpirePreAuthKeyRequest)) return srv.(HeadscaleServiceServer).ExpirePreAuthKey(ctx, req.(*ExpirePreAuthKeyRequest))
@@ -552,7 +580,7 @@ func _HeadscaleService_ListPreAuthKeys_Handler(srv interface{}, ctx context.Cont
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ListPreAuthKeys", FullMethod: HeadscaleService_ListPreAuthKeys_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ListPreAuthKeys(ctx, req.(*ListPreAuthKeysRequest)) return srv.(HeadscaleServiceServer).ListPreAuthKeys(ctx, req.(*ListPreAuthKeysRequest))
@@ -570,7 +598,7 @@ func _HeadscaleService_DebugCreateMachine_Handler(srv interface{}, ctx context.C
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/DebugCreateMachine", FullMethod: HeadscaleService_DebugCreateMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).DebugCreateMachine(ctx, req.(*DebugCreateMachineRequest)) return srv.(HeadscaleServiceServer).DebugCreateMachine(ctx, req.(*DebugCreateMachineRequest))
@@ -588,7 +616,7 @@ func _HeadscaleService_GetMachine_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/GetMachine", FullMethod: HeadscaleService_GetMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest)) return srv.(HeadscaleServiceServer).GetMachine(ctx, req.(*GetMachineRequest))
@@ -606,7 +634,7 @@ func _HeadscaleService_SetTags_Handler(srv interface{}, ctx context.Context, dec
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/SetTags", FullMethod: HeadscaleService_SetTags_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).SetTags(ctx, req.(*SetTagsRequest)) return srv.(HeadscaleServiceServer).SetTags(ctx, req.(*SetTagsRequest))
@@ -624,7 +652,7 @@ func _HeadscaleService_RegisterMachine_Handler(srv interface{}, ctx context.Cont
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/RegisterMachine", FullMethod: HeadscaleService_RegisterMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).RegisterMachine(ctx, req.(*RegisterMachineRequest)) return srv.(HeadscaleServiceServer).RegisterMachine(ctx, req.(*RegisterMachineRequest))
@@ -642,7 +670,7 @@ func _HeadscaleService_DeleteMachine_Handler(srv interface{}, ctx context.Contex
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/DeleteMachine", FullMethod: HeadscaleService_DeleteMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).DeleteMachine(ctx, req.(*DeleteMachineRequest)) return srv.(HeadscaleServiceServer).DeleteMachine(ctx, req.(*DeleteMachineRequest))
@@ -660,7 +688,7 @@ func _HeadscaleService_ExpireMachine_Handler(srv interface{}, ctx context.Contex
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ExpireMachine", FullMethod: HeadscaleService_ExpireMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ExpireMachine(ctx, req.(*ExpireMachineRequest)) return srv.(HeadscaleServiceServer).ExpireMachine(ctx, req.(*ExpireMachineRequest))
@@ -678,7 +706,7 @@ func _HeadscaleService_RenameMachine_Handler(srv interface{}, ctx context.Contex
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/RenameMachine", FullMethod: HeadscaleService_RenameMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).RenameMachine(ctx, req.(*RenameMachineRequest)) return srv.(HeadscaleServiceServer).RenameMachine(ctx, req.(*RenameMachineRequest))
@@ -696,7 +724,7 @@ func _HeadscaleService_ListMachines_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ListMachines", FullMethod: HeadscaleService_ListMachines_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ListMachines(ctx, req.(*ListMachinesRequest)) return srv.(HeadscaleServiceServer).ListMachines(ctx, req.(*ListMachinesRequest))
@@ -714,7 +742,7 @@ func _HeadscaleService_MoveMachine_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/MoveMachine", FullMethod: HeadscaleService_MoveMachine_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).MoveMachine(ctx, req.(*MoveMachineRequest)) return srv.(HeadscaleServiceServer).MoveMachine(ctx, req.(*MoveMachineRequest))
@@ -732,7 +760,7 @@ func _HeadscaleService_GetRoutes_Handler(srv interface{}, ctx context.Context, d
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/GetRoutes", FullMethod: HeadscaleService_GetRoutes_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).GetRoutes(ctx, req.(*GetRoutesRequest)) return srv.(HeadscaleServiceServer).GetRoutes(ctx, req.(*GetRoutesRequest))
@@ -750,7 +778,7 @@ func _HeadscaleService_EnableRoute_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/EnableRoute", FullMethod: HeadscaleService_EnableRoute_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).EnableRoute(ctx, req.(*EnableRouteRequest)) return srv.(HeadscaleServiceServer).EnableRoute(ctx, req.(*EnableRouteRequest))
@@ -768,7 +796,7 @@ func _HeadscaleService_DisableRoute_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/DisableRoute", FullMethod: HeadscaleService_DisableRoute_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).DisableRoute(ctx, req.(*DisableRouteRequest)) return srv.(HeadscaleServiceServer).DisableRoute(ctx, req.(*DisableRouteRequest))
@@ -786,7 +814,7 @@ func _HeadscaleService_GetMachineRoutes_Handler(srv interface{}, ctx context.Con
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/GetMachineRoutes", FullMethod: HeadscaleService_GetMachineRoutes_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).GetMachineRoutes(ctx, req.(*GetMachineRoutesRequest)) return srv.(HeadscaleServiceServer).GetMachineRoutes(ctx, req.(*GetMachineRoutesRequest))
@@ -804,7 +832,7 @@ func _HeadscaleService_DeleteRoute_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/DeleteRoute", FullMethod: HeadscaleService_DeleteRoute_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).DeleteRoute(ctx, req.(*DeleteRouteRequest)) return srv.(HeadscaleServiceServer).DeleteRoute(ctx, req.(*DeleteRouteRequest))
@@ -822,7 +850,7 @@ func _HeadscaleService_CreateApiKey_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/CreateApiKey", FullMethod: HeadscaleService_CreateApiKey_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).CreateApiKey(ctx, req.(*CreateApiKeyRequest)) return srv.(HeadscaleServiceServer).CreateApiKey(ctx, req.(*CreateApiKeyRequest))
@@ -840,7 +868,7 @@ func _HeadscaleService_ExpireApiKey_Handler(srv interface{}, ctx context.Context
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ExpireApiKey", FullMethod: HeadscaleService_ExpireApiKey_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ExpireApiKey(ctx, req.(*ExpireApiKeyRequest)) return srv.(HeadscaleServiceServer).ExpireApiKey(ctx, req.(*ExpireApiKeyRequest))
@@ -858,7 +886,7 @@ func _HeadscaleService_ListApiKeys_Handler(srv interface{}, ctx context.Context,
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/headscale.v1.HeadscaleService/ListApiKeys", FullMethod: HeadscaleService_ListApiKeys_FullMethodName,
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HeadscaleServiceServer).ListApiKeys(ctx, req.(*ListApiKeysRequest)) return srv.(HeadscaleServiceServer).ListApiKeys(ctx, req.(*ListApiKeysRequest))

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/machine.proto // source: headscale/v1/machine.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/preauthkey.proto // source: headscale/v1/preauthkey.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/routes.proto // source: headscale/v1/routes.proto

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.28.1 // protoc-gen-go v1.29.1
// protoc (unknown) // protoc (unknown)
// source: headscale/v1/user.proto // source: headscale/v1/user.proto

7
go.mod
View File

@@ -4,7 +4,6 @@ go 1.20
require ( require (
github.com/AlecAivazis/survey/v2 v2.3.6 github.com/AlecAivazis/survey/v2 v2.3.6
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
github.com/cenkalti/backoff/v4 v4.2.0 github.com/cenkalti/backoff/v4 v4.2.0
github.com/coreos/go-oidc/v3 v3.5.0 github.com/coreos/go-oidc/v3 v3.5.0
github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew v1.1.1
@@ -12,6 +11,7 @@ require (
github.com/efekarakus/termcolor v1.0.1 github.com/efekarakus/termcolor v1.0.1
github.com/glebarez/sqlite v1.7.0 github.com/glebarez/sqlite v1.7.0
github.com/gofrs/uuid/v5 v5.0.0 github.com/gofrs/uuid/v5 v5.0.0
github.com/google/go-cmp v0.5.9
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2 github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.2
@@ -20,6 +20,7 @@ require (
github.com/ory/dockertest/v3 v3.9.1 github.com/ory/dockertest/v3 v3.9.1
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philip-bui/grpc-zerolog v1.0.1 github.com/philip-bui/grpc-zerolog v1.0.1
github.com/pkg/profile v1.7.0
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.14.0
github.com/prometheus/common v0.42.0 github.com/prometheus/common v0.42.0
github.com/pterm/pterm v0.12.58 github.com/pterm/pterm v0.12.58
@@ -64,6 +65,7 @@ require (
github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/glebarez/go-sqlite v1.20.3 // indirect github.com/glebarez/go-sqlite v1.20.3 // indirect
@@ -72,9 +74,9 @@ require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-github v17.0.0+incompatible // indirect
github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.3 // indirect github.com/gookit/color v1.5.3 // indirect
@@ -141,6 +143,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools/v3 v3.4.0 // indirect
modernc.org/libc v1.22.2 // indirect modernc.org/libc v1.22.2 // indirect
modernc.org/mathutil v1.5.0 // indirect modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect

13
go.sum
View File

@@ -74,8 +74,6 @@ github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkU
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdeyM8Aomg4tKDcwATCFuW+cYLkj6pwsw9pc=
github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5n9cGHYdM3S3IK8ROSUUUYjQOu+MSUCZDcJbYWi8=
github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4=
github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -129,6 +127,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
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.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -238,7 +238,9 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@@ -272,6 +274,7 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -384,6 +387,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -669,6 +674,7 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-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-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-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -894,7 +900,8 @@ gorm.io/driver/postgres v1.4.8/go.mod h1:O9MruWGNLUBUWVYfWuBClpf3HeGjOoybY0SNmCs
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s= gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools/v3 v3.2.0 h1:I0DwBVMGAx26dttAj1BtJLAkVGncrkkUXfJLC4Flt/I= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"encoding/json" "encoding/json"
@@ -13,7 +13,6 @@ import (
"time" "time"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/samber/lo"
"github.com/tailscale/hujson" "github.com/tailscale/hujson"
"go4.org/netipx" "go4.org/netipx"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -60,8 +59,8 @@ const (
var featureEnableSSH = envknob.RegisterBool("HEADSCALE_EXPERIMENTAL_FEATURE_SSH") var featureEnableSSH = envknob.RegisterBool("HEADSCALE_EXPERIMENTAL_FEATURE_SSH")
// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules. // LoadACLPolicyFromPath loads the ACL policy from the specify path, and generates the ACL rules.
func (h *Headscale) LoadACLPolicy(path string) error { func (h *Headscale) LoadACLPolicyFromPath(path string) error {
log.Debug(). log.Debug().
Str("func", "LoadACLPolicy"). Str("func", "LoadACLPolicy").
Str("path", path). Str("path", path).
@@ -73,37 +72,42 @@ func (h *Headscale) LoadACLPolicy(path string) error {
} }
defer policyFile.Close() defer policyFile.Close()
var policy ACLPolicy
policyBytes, err := io.ReadAll(policyFile) policyBytes, err := io.ReadAll(policyFile)
if err != nil { if err != nil {
return err return err
} }
log.Debug().
Str("path", path).
Bytes("file", policyBytes).
Msg("Loading ACLs")
switch filepath.Ext(path) { switch filepath.Ext(path) {
case ".yml", ".yaml": case ".yml", ".yaml":
log.Debug(). return h.LoadACLPolicyFromBytes(policyBytes, "yaml")
Str("path", path). }
Bytes("file", policyBytes).
Msg("Loading ACLs from YAML")
err := yaml.Unmarshal(policyBytes, &policy) return h.LoadACLPolicyFromBytes(policyBytes, "hujson")
}
func (h *Headscale) LoadACLPolicyFromBytes(acl []byte, format string) error {
var policy ACLPolicy
switch format {
case "yaml":
err := yaml.Unmarshal(acl, &policy)
if err != nil { if err != nil {
return err return err
} }
log.Trace().
Interface("policy", policy).
Msg("Loaded policy from YAML")
default: default:
ast, err := hujson.Parse(policyBytes) ast, err := hujson.Parse(acl)
if err != nil { if err != nil {
return err return err
} }
ast.Standardize() ast.Standardize()
policyBytes = ast.Pack() acl = ast.Pack()
err = json.Unmarshal(policyBytes, &policy) err = json.Unmarshal(acl, &policy)
if err != nil { if err != nil {
return err return err
} }
@@ -128,21 +132,14 @@ func (h *Headscale) UpdateACLRules() error {
return errEmptyPolicy return errEmptyPolicy
} }
rules, err := generateACLRules(machines, *h.aclPolicy, h.cfg.OIDC.StripEmaildomain) rules, err := h.aclPolicy.generateFilterRules(machines, h.cfg.OIDC.StripEmaildomain)
if err != nil { if err != nil {
return err return err
} }
log.Trace().Interface("ACL", rules).Msg("ACL rules generated") log.Trace().Interface("ACL", rules).Msg("ACL rules generated")
h.aclRules = rules h.aclRules = rules
// Precompute a map of which sources can reach each destination, this is
// to provide quicker lookup when we calculate the peerlist for the map
// response to nodes.
aclPeerCacheMap := generateACLPeerCacheMap(rules)
h.aclPeerCacheMapRW.Lock()
h.aclPeerCacheMap = aclPeerCacheMap
h.aclPeerCacheMapRW.Unlock()
if featureEnableSSH() { if featureEnableSSH() {
sshRules, err := h.generateSSHRules() sshRules, err := h.generateSSHRules()
if err != nil { if err != nil {
@@ -160,91 +157,28 @@ func (h *Headscale) UpdateACLRules() error {
return nil return nil
} }
// generateACLPeerCacheMap takes a list of Tailscale filter rules and generates a map // generateFilterRules takes a set of machines and an ACLPolicy and generates a
// of which Sources ("*" and IPs) can access destinations. This is to speed up the // set of Tailscale compatible FilterRules used to allow traffic on clients.
// process of generating MapResponses when deciding which Peers to inform nodes about. func (pol *ACLPolicy) generateFilterRules(
func generateACLPeerCacheMap(rules []tailcfg.FilterRule) map[string]map[string]struct{} {
aclCachePeerMap := make(map[string]map[string]struct{})
for _, rule := range rules {
for _, srcIP := range rule.SrcIPs {
for _, ip := range expandACLPeerAddr(srcIP) {
if data, ok := aclCachePeerMap[ip]; ok {
for _, dstPort := range rule.DstPorts {
for _, dstIP := range expandACLPeerAddr(dstPort.IP) {
data[dstIP] = struct{}{}
}
}
} else {
dstPortsMap := make(map[string]struct{}, len(rule.DstPorts))
for _, dstPort := range rule.DstPorts {
for _, dstIP := range expandACLPeerAddr(dstPort.IP) {
dstPortsMap[dstIP] = struct{}{}
}
}
aclCachePeerMap[ip] = dstPortsMap
}
}
}
}
log.Trace().Interface("ACL Cache Map", aclCachePeerMap).Msg("ACL Peer Cache Map generated")
return aclCachePeerMap
}
// expandACLPeerAddr takes a "tailcfg.FilterRule" "IP" and expands it into
// something our cache logic can look up, which is "*" or single IP addresses.
// This is probably quite inefficient, but it is a result of
// "make it work, then make it fast", and a lot of the ACL stuff does not
// work, but people have tried to make it fast.
func expandACLPeerAddr(srcIP string) []string {
if ip, err := netip.ParseAddr(srcIP); err == nil {
return []string{ip.String()}
}
if cidr, err := netip.ParsePrefix(srcIP); err == nil {
addrs := []string{}
ipRange := netipx.RangeOfPrefix(cidr)
from := ipRange.From()
too := ipRange.To()
if from == too {
return []string{from.String()}
}
for from != too && from.Less(too) {
addrs = append(addrs, from.String())
from = from.Next()
}
addrs = append(addrs, too.String()) // Add the last IP address in the range
return addrs
}
// probably "*" or other string based "IP"
return []string{srcIP}
}
func generateACLRules(
machines []Machine, machines []Machine,
aclPolicy ACLPolicy, stripEmailDomain bool,
stripEmaildomain bool,
) ([]tailcfg.FilterRule, error) { ) ([]tailcfg.FilterRule, error) {
rules := []tailcfg.FilterRule{} rules := []tailcfg.FilterRule{}
for index, acl := range aclPolicy.ACLs { for index, acl := range pol.ACLs {
if acl.Action != "accept" { if acl.Action != "accept" {
return nil, errInvalidAction return nil, errInvalidAction
} }
srcIPs := []string{} srcIPs := []string{}
for innerIndex, src := range acl.Sources { for srcIndex, src := range acl.Sources {
srcs, err := generateACLPolicySrc(machines, aclPolicy, src, stripEmaildomain) srcs, err := pol.getIPsFromSource(src, machines, stripEmailDomain)
if err != nil { if err != nil {
log.Error(). log.Error().
Msgf("Error parsing ACL %d, Source %d", index, innerIndex) Interface("src", src).
Int("ACL index", index).
Int("Src index", srcIndex).
Msgf("Error parsing ACL")
return nil, err return nil, err
} }
@@ -260,17 +194,19 @@ func generateACLRules(
} }
destPorts := []tailcfg.NetPortRange{} destPorts := []tailcfg.NetPortRange{}
for innerIndex, dest := range acl.Destinations { for destIndex, dest := range acl.Destinations {
dests, err := generateACLPolicyDest( dests, err := pol.getNetPortRangeFromDestination(
machines,
aclPolicy,
dest, dest,
machines,
needsWildcard, needsWildcard,
stripEmaildomain, stripEmailDomain,
) )
if err != nil { if err != nil {
log.Error(). log.Error().
Msgf("Error parsing ACL %d, Destination %d", index, innerIndex) Interface("dest", dest).
Int("ACL index", index).
Int("dest index", destIndex).
Msgf("Error parsing ACL")
return nil, err return nil, err
} }
@@ -341,22 +277,41 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) {
principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources)) principals := make([]*tailcfg.SSHPrincipal, 0, len(sshACL.Sources))
for innerIndex, rawSrc := range sshACL.Sources { for innerIndex, rawSrc := range sshACL.Sources {
expandedSrcs, err := expandAlias( if isWildcard(rawSrc) {
machines,
*h.aclPolicy,
rawSrc,
h.cfg.OIDC.StripEmaildomain,
)
if err != nil {
log.Error().
Msgf("Error parsing SSH %d, Source %d", index, innerIndex)
return nil, err
}
for _, expandedSrc := range expandedSrcs {
principals = append(principals, &tailcfg.SSHPrincipal{ principals = append(principals, &tailcfg.SSHPrincipal{
NodeIP: expandedSrc, Any: true,
}) })
} else if isGroup(rawSrc) {
users, err := h.aclPolicy.getUsersInGroup(rawSrc, h.cfg.OIDC.StripEmaildomain)
if err != nil {
log.Error().
Msgf("Error parsing SSH %d, Source %d", index, innerIndex)
return nil, err
}
for _, user := range users {
principals = append(principals, &tailcfg.SSHPrincipal{
UserLogin: user,
})
}
} else {
expandedSrcs, err := h.aclPolicy.expandAlias(
machines,
rawSrc,
h.cfg.OIDC.StripEmaildomain,
)
if err != nil {
log.Error().
Msgf("Error parsing SSH %d, Source %d", index, innerIndex)
return nil, err
}
for _, expandedSrc := range expandedSrcs.Prefixes() {
principals = append(principals, &tailcfg.SSHPrincipal{
NodeIP: expandedSrc.Addr().String(),
})
}
} }
} }
@@ -365,10 +320,9 @@ func (h *Headscale) generateSSHRules() ([]*tailcfg.SSHRule, error) {
userMap[user] = "=" userMap[user] = "="
} }
rules = append(rules, &tailcfg.SSHRule{ rules = append(rules, &tailcfg.SSHRule{
RuleExpires: nil, Principals: principals,
Principals: principals, SSHUsers: userMap,
SSHUsers: userMap, Action: &action,
Action: &action,
}) })
} }
@@ -392,19 +346,32 @@ func sshCheckAction(duration string) (*tailcfg.SSHAction, error) {
}, nil }, nil
} }
func generateACLPolicySrc( // getIPsFromSource returns a set of Source IPs that would be associated
machines []Machine, // with the given src alias.
aclPolicy ACLPolicy, func (pol *ACLPolicy) getIPsFromSource(
src string, src string,
machines []Machine,
stripEmaildomain bool, stripEmaildomain bool,
) ([]string, error) { ) ([]string, error) {
return expandAlias(machines, aclPolicy, src, stripEmaildomain) ipSet, err := pol.expandAlias(machines, src, stripEmaildomain)
if err != nil {
return []string{}, err
}
prefixes := []string{}
for _, prefix := range ipSet.Prefixes() {
prefixes = append(prefixes, prefix.String())
}
return prefixes, nil
} }
func generateACLPolicyDest( // getNetPortRangeFromDestination returns a set of tailcfg.NetPortRange
machines []Machine, // which are associated with the dest alias.
aclPolicy ACLPolicy, func (pol *ACLPolicy) getNetPortRangeFromDestination(
dest string, dest string,
machines []Machine,
needsWildcard bool, needsWildcard bool,
stripEmaildomain bool, stripEmaildomain bool,
) ([]tailcfg.NetPortRange, error) { ) ([]tailcfg.NetPortRange, error) {
@@ -451,9 +418,8 @@ func generateACLPolicyDest(
alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1]) alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1])
} }
expanded, err := expandAlias( expanded, err := pol.expandAlias(
machines, machines,
aclPolicy,
alias, alias,
stripEmaildomain, stripEmaildomain,
) )
@@ -466,11 +432,11 @@ func generateACLPolicyDest(
} }
dests := []tailcfg.NetPortRange{} dests := []tailcfg.NetPortRange{}
for _, d := range expanded { for _, dest := range expanded.Prefixes() {
for _, p := range *ports { for _, port := range *ports {
pr := tailcfg.NetPortRange{ pr := tailcfg.NetPortRange{
IP: d, IP: dest.String(),
Ports: p, Ports: port,
} }
dests = append(dests, pr) dests = append(dests, pr)
} }
@@ -537,135 +503,64 @@ func parseProtocol(protocol string) ([]int, bool, error) {
// - an ip // - an ip
// - a cidr // - a cidr
// and transform these in IPAddresses. // and transform these in IPAddresses.
func expandAlias( func (pol *ACLPolicy) expandAlias(
machines Machines, machines Machines,
aclPolicy ACLPolicy,
alias string, alias string,
stripEmailDomain bool, stripEmailDomain bool,
) ([]string, error) { ) (*netipx.IPSet, error) {
ips := []string{} if isWildcard(alias) {
if alias == "*" { return parseIPSet("*", nil)
return []string{"*"}, nil
} }
build := netipx.IPSetBuilder{}
log.Debug(). log.Debug().
Str("alias", alias). Str("alias", alias).
Msg("Expanding") Msg("Expanding")
if strings.HasPrefix(alias, "group:") { // if alias is a group
users, err := expandGroup(aclPolicy, alias, stripEmailDomain) if isGroup(alias) {
if err != nil { return pol.getIPsFromGroup(alias, machines, stripEmailDomain)
return ips, err
}
for _, n := range users {
nodes := filterMachinesByUser(machines, n)
for _, node := range nodes {
ips = append(ips, node.IPAddresses.ToStringSlice()...)
}
}
return ips, nil
} }
if strings.HasPrefix(alias, "tag:") { // if alias is a tag
// check for forced tags if isTag(alias) {
for _, machine := range machines { return pol.getIPsFromTag(alias, machines, stripEmailDomain)
if contains(machine.ForcedTags, alias) {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
}
}
// find tag owners
owners, err := expandTagOwners(aclPolicy, alias, stripEmailDomain)
if err != nil {
if errors.Is(err, errInvalidTag) {
if len(ips) == 0 {
return ips, fmt.Errorf(
"%w. %v isn't owned by a TagOwner and no forced tags are defined",
errInvalidTag,
alias,
)
}
return ips, nil
} else {
return ips, err
}
}
// filter out machines per tag owner
for _, user := range owners {
machines := filterMachinesByUser(machines, user)
for _, machine := range machines {
hi := machine.GetHostInfo()
if contains(hi.RequestTags, alias) {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
}
}
}
return ips, nil
} }
// if alias is a user // if alias is a user
nodes := filterMachinesByUser(machines, alias) if ips, err := pol.getIPsForUser(alias, machines, stripEmailDomain); ips != nil {
nodes = excludeCorrectlyTaggedNodes(aclPolicy, nodes, alias, stripEmailDomain) return ips, err
for _, n := range nodes {
ips = append(ips, n.IPAddresses.ToStringSlice()...)
}
if len(ips) > 0 {
return ips, nil
} }
// if alias is an host // if alias is an host
if h, ok := aclPolicy.Hosts[alias]; ok { // Note, this is recursive.
if h, ok := pol.Hosts[alias]; ok {
log.Trace().Str("host", h.String()).Msg("expandAlias got hosts entry") log.Trace().Str("host", h.String()).Msg("expandAlias got hosts entry")
return expandAlias(machines, aclPolicy, h.String(), stripEmailDomain) return pol.expandAlias(machines, h.String(), stripEmailDomain)
} }
// if alias is an IP // if alias is an IP
if ip, err := netip.ParseAddr(alias); err == nil { if ip, err := netip.ParseAddr(alias); err == nil {
log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip") return pol.getIPsFromSingleIP(ip, machines)
ips := []string{ip.String()}
matches := machines.FilterByIP(ip)
for _, machine := range matches {
ips = append(ips, machine.IPAddresses.ToStringSlice()...)
}
return lo.Uniq(ips), nil
} }
if cidr, err := netip.ParsePrefix(alias); err == nil { // if alias is an IP Prefix (CIDR)
log.Trace().Str("cidr", cidr.String()).Msg("expandAlias got cidr") if prefix, err := netip.ParsePrefix(alias); err == nil {
val := []string{cidr.String()} return pol.getIPsFromIPPrefix(prefix, machines)
// This is suboptimal and quite expensive, but if we only add the cidr, we will miss all the relevant IPv6
// addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers.
for _, machine := range machines {
for _, ip := range machine.IPAddresses {
// log.Trace().
// Msgf("checking if machine ip (%s) is part of cidr (%s): %v, is single ip cidr (%v), addr: %s", ip.String(), cidr.String(), cidr.Contains(ip), cidr.IsSingleIP(), cidr.Addr().String())
if cidr.Contains(ip) {
val = append(val, machine.IPAddresses.ToStringSlice()...)
}
}
}
return lo.Uniq(val), nil
} }
log.Warn().Msgf("No IPs found with the alias %v", alias) log.Warn().Msgf("No IPs found with the alias %v", alias)
return ips, nil return build.IPSet()
} }
// excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones // excludeCorrectlyTaggedNodes will remove from the list of input nodes the ones
// that are correctly tagged since they should not be listed as being in the user // that are correctly tagged since they should not be listed as being in the user
// we assume in this function that we only have nodes from 1 user. // we assume in this function that we only have nodes from 1 user.
func excludeCorrectlyTaggedNodes( func excludeCorrectlyTaggedNodes(
aclPolicy ACLPolicy, aclPolicy *ACLPolicy,
nodes []Machine, nodes []Machine,
user string, user string,
stripEmailDomain bool, stripEmailDomain bool,
@@ -673,7 +568,7 @@ func excludeCorrectlyTaggedNodes(
out := []Machine{} out := []Machine{}
tags := []string{} tags := []string{}
for tag := range aclPolicy.TagOwners { for tag := range aclPolicy.TagOwners {
owners, _ := expandTagOwners(aclPolicy, user, stripEmailDomain) owners, _ := getTagOwners(aclPolicy, user, stripEmailDomain)
ns := append(owners, user) ns := append(owners, user)
if contains(ns, user) { if contains(ns, user) {
tags = append(tags, tag) tags = append(tags, tag)
@@ -703,7 +598,7 @@ func excludeCorrectlyTaggedNodes(
} }
func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) { func expandPorts(portsStr string, needsWildcard bool) (*[]tailcfg.PortRange, error) {
if portsStr == "*" { if isWildcard(portsStr) {
return &[]tailcfg.PortRange{ return &[]tailcfg.PortRange{
{First: portRangeBegin, Last: portRangeEnd}, {First: portRangeBegin, Last: portRangeEnd},
}, nil }, nil
@@ -761,15 +656,15 @@ func filterMachinesByUser(machines []Machine, user string) []Machine {
return out return out
} }
// expandTagOwners will return a list of user. An owner can be either a user or a group // getTagOwners will return a list of user. An owner can be either a user or a group
// a group cannot be composed of groups. // a group cannot be composed of groups.
func expandTagOwners( func getTagOwners(
aclPolicy ACLPolicy, pol *ACLPolicy,
tag string, tag string,
stripEmailDomain bool, stripEmailDomain bool,
) ([]string, error) { ) ([]string, error) {
var owners []string var owners []string
ows, ok := aclPolicy.TagOwners[tag] ows, ok := pol.TagOwners[tag]
if !ok { if !ok {
return []string{}, fmt.Errorf( return []string{}, fmt.Errorf(
"%w. %v isn't owned by a TagOwner. Please add one first. https://tailscale.com/kb/1018/acls/#tag-owners", "%w. %v isn't owned by a TagOwner. Please add one first. https://tailscale.com/kb/1018/acls/#tag-owners",
@@ -778,8 +673,8 @@ func expandTagOwners(
) )
} }
for _, owner := range ows { for _, owner := range ows {
if strings.HasPrefix(owner, "group:") { if isGroup(owner) {
gs, err := expandGroup(aclPolicy, owner, stripEmailDomain) gs, err := pol.getUsersInGroup(owner, stripEmailDomain)
if err != nil { if err != nil {
return []string{}, err return []string{}, err
} }
@@ -792,15 +687,15 @@ func expandTagOwners(
return owners, nil return owners, nil
} }
// expandGroup will return the list of user inside the group // getUsersInGroup will return the list of user inside the group
// after some validation. // after some validation.
func expandGroup( func (pol *ACLPolicy) getUsersInGroup(
aclPolicy ACLPolicy,
group string, group string,
stripEmailDomain bool, stripEmailDomain bool,
) ([]string, error) { ) ([]string, error) {
outGroups := []string{} users := []string{}
aclGroups, ok := aclPolicy.Groups[group] log.Trace().Caller().Interface("pol", pol).Msg("test")
aclGroups, ok := pol.Groups[group]
if !ok { if !ok {
return []string{}, fmt.Errorf( return []string{}, fmt.Errorf(
"group %v isn't registered. %w", "group %v isn't registered. %w",
@@ -809,7 +704,7 @@ func expandGroup(
) )
} }
for _, group := range aclGroups { for _, group := range aclGroups {
if strings.HasPrefix(group, "group:") { if isGroup(group) {
return []string{}, fmt.Errorf( return []string{}, fmt.Errorf(
"%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups", "%w. A group cannot be composed of groups. https://tailscale.com/kb/1018/acls/#groups",
errInvalidGroup, errInvalidGroup,
@@ -823,8 +718,151 @@ func expandGroup(
errInvalidGroup, errInvalidGroup,
) )
} }
outGroups = append(outGroups, grp) users = append(users, grp)
} }
return outGroups, nil return users, nil
}
func (pol *ACLPolicy) getIPsFromGroup(
group string,
machines Machines,
stripEmailDomain bool,
) (*netipx.IPSet, error) {
build := netipx.IPSetBuilder{}
users, err := pol.getUsersInGroup(group, stripEmailDomain)
if err != nil {
return &netipx.IPSet{}, err
}
for _, user := range users {
filteredMachines := filterMachinesByUser(machines, user)
for _, machine := range filteredMachines {
machine.IPAddresses.AppendToIPSet(&build)
}
}
return build.IPSet()
}
func (pol *ACLPolicy) getIPsFromTag(
alias string,
machines Machines,
stripEmailDomain bool,
) (*netipx.IPSet, error) {
build := netipx.IPSetBuilder{}
// check for forced tags
for _, machine := range machines {
if contains(machine.ForcedTags, alias) {
machine.IPAddresses.AppendToIPSet(&build)
}
}
// find tag owners
owners, err := getTagOwners(pol, alias, stripEmailDomain)
if err != nil {
if errors.Is(err, errInvalidTag) {
ipSet, _ := build.IPSet()
if len(ipSet.Prefixes()) == 0 {
return ipSet, fmt.Errorf(
"%w. %v isn't owned by a TagOwner and no forced tags are defined",
errInvalidTag,
alias,
)
}
return build.IPSet()
} else {
return nil, err
}
}
// filter out machines per tag owner
for _, user := range owners {
machines := filterMachinesByUser(machines, user)
for _, machine := range machines {
hi := machine.GetHostInfo()
if contains(hi.RequestTags, alias) {
machine.IPAddresses.AppendToIPSet(&build)
}
}
}
return build.IPSet()
}
func (pol *ACLPolicy) getIPsForUser(
user string,
machines Machines,
stripEmailDomain bool,
) (*netipx.IPSet, error) {
build := netipx.IPSetBuilder{}
filteredMachines := filterMachinesByUser(machines, user)
filteredMachines = excludeCorrectlyTaggedNodes(pol, filteredMachines, user, stripEmailDomain)
// shortcurcuit if we have no machines to get ips from.
if len(filteredMachines) == 0 {
return nil, nil //nolint
}
for _, machine := range filteredMachines {
machine.IPAddresses.AppendToIPSet(&build)
}
return build.IPSet()
}
func (pol *ACLPolicy) getIPsFromSingleIP(
ip netip.Addr,
machines Machines,
) (*netipx.IPSet, error) {
log.Trace().Str("ip", ip.String()).Msg("expandAlias got ip")
matches := machines.FilterByIP(ip)
build := netipx.IPSetBuilder{}
build.Add(ip)
for _, machine := range matches {
machine.IPAddresses.AppendToIPSet(&build)
}
return build.IPSet()
}
func (pol *ACLPolicy) getIPsFromIPPrefix(
prefix netip.Prefix,
machines Machines,
) (*netipx.IPSet, error) {
log.Trace().Str("prefix", prefix.String()).Msg("expandAlias got prefix")
build := netipx.IPSetBuilder{}
build.AddPrefix(prefix)
// This is suboptimal and quite expensive, but if we only add the prefix, we will miss all the relevant IPv6
// addresses for the hosts that belong to tailscale. This doesnt really affect stuff like subnet routers.
for _, machine := range machines {
for _, ip := range machine.IPAddresses {
// log.Trace().
// Msgf("checking if machine ip (%s) is part of prefix (%s): %v, is single ip prefix (%v), addr: %s", ip.String(), prefix.String(), prefix.Contains(ip), prefix.IsSingleIP(), prefix.Addr().String())
if prefix.Contains(ip) {
machine.IPAddresses.AppendToIPSet(&build)
}
}
}
return build.IPSet()
}
func isWildcard(str string) bool {
return str == "*"
}
func isGroup(str string) bool {
return strings.HasPrefix(str, "group:")
}
func isTag(str string) bool {
return strings.HasPrefix(str, "tag:")
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"encoding/json" "encoding/json"
@@ -111,8 +111,8 @@ func (hosts *Hosts) UnmarshalYAML(data []byte) error {
} }
// IsZero is perhaps a bit naive here. // IsZero is perhaps a bit naive here.
func (policy ACLPolicy) IsZero() bool { func (pol ACLPolicy) IsZero() bool {
if len(policy.Groups) == 0 && len(policy.Hosts) == 0 && len(policy.ACLs) == 0 { if len(pol.Groups) == 0 && len(pol.Hosts) == 0 && len(pol.ACLs) == 0 {
return true return true
} }

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"time" "time"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"time" "time"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"context" "context"
@@ -21,6 +21,7 @@ import (
"github.com/gorilla/mux" "github.com/gorilla/mux"
grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware" grpcMiddleware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/patrickmn/go-cache" "github.com/patrickmn/go-cache"
zerolog "github.com/philip-bui/grpc-zerolog" zerolog "github.com/philip-bui/grpc-zerolog"
@@ -84,11 +85,9 @@ type Headscale struct {
DERPMap *tailcfg.DERPMap DERPMap *tailcfg.DERPMap
DERPServer *DERPServer DERPServer *DERPServer
aclPolicy *ACLPolicy aclPolicy *ACLPolicy
aclRules []tailcfg.FilterRule aclRules []tailcfg.FilterRule
aclPeerCacheMapRW sync.RWMutex sshPolicy *tailcfg.SSHPolicy
aclPeerCacheMap map[string]map[string]struct{}
sshPolicy *tailcfg.SSHPolicy
lastStateChange *xsync.MapOf[string, time.Time] lastStateChange *xsync.MapOf[string, time.Time]
@@ -509,8 +508,10 @@ func (h *Headscale) createRouter(grpcMux *runtime.ServeMux) *mux.Router {
router.HandleFunc("/windows", h.WindowsConfigMessage).Methods(http.MethodGet) router.HandleFunc("/windows", h.WindowsConfigMessage).Methods(http.MethodGet)
router.HandleFunc("/windows/tailscale.reg", h.WindowsRegConfig). router.HandleFunc("/windows/tailscale.reg", h.WindowsRegConfig).
Methods(http.MethodGet) Methods(http.MethodGet)
router.HandleFunc("/swagger", SwaggerUI).Methods(http.MethodGet)
router.HandleFunc("/swagger/v1/openapiv2.json", SwaggerAPIv1). // TODO(kristoffer): move swagger into a package
router.HandleFunc("/swagger", headscale.SwaggerUI).Methods(http.MethodGet)
router.HandleFunc("/swagger/v1/openapiv2.json", headscale.SwaggerAPIv1).
Methods(http.MethodGet) Methods(http.MethodGet)
if h.cfg.DERP.ServerEnabled { if h.cfg.DERP.ServerEnabled {
@@ -760,7 +761,7 @@ func (h *Headscale) Serve() error {
if h.cfg.ACL.PolicyPath != "" { if h.cfg.ACL.PolicyPath != "" {
aclPath := AbsolutePathFromConfigPath(h.cfg.ACL.PolicyPath) aclPath := AbsolutePathFromConfigPath(h.cfg.ACL.PolicyPath)
err := h.LoadACLPolicy(aclPath) err := h.LoadACLPolicyFromPath(aclPath)
if err != nil { if err != nil {
log.Error().Err(err).Msg("Failed to reload ACL policy") log.Error().Err(err).Msg("Failed to reload ACL policy")
} }
@@ -820,7 +821,6 @@ func (h *Headscale) Serve() error {
// And we're done: // And we're done:
cancel() cancel()
os.Exit(0)
} }
} }
} }

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"net/netip" "net/netip"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"errors" "errors"
@@ -16,6 +16,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/spf13/viper" "github.com/spf13/viper"
"go4.org/netipx" "go4.org/netipx"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
"tailscale.com/types/dnstype" "tailscale.com/types/dnstype"
) )
@@ -174,7 +175,7 @@ func LoadConfig(path string, isFile bool) error {
viper.SetDefault("derp.server.enabled", false) viper.SetDefault("derp.server.enabled", false)
viper.SetDefault("derp.server.stun.enabled", true) viper.SetDefault("derp.server.stun.enabled", true)
viper.SetDefault("unix_socket", "/var/run/headscale.sock") viper.SetDefault("unix_socket", "/var/run/headscale/headscale.sock")
viper.SetDefault("unix_socket_permission", "0o770") viper.SetDefault("unix_socket_permission", "0o770")
viper.SetDefault("grpc_listen_addr", ":50443") viper.SetDefault("grpc_listen_addr", ":50443")
@@ -515,6 +516,29 @@ func GetHeadscaleConfig() (*Config, error) {
if err != nil { if err != nil {
panic(fmt.Errorf("failed to parse ip_prefixes[%d]: %w", i, err)) panic(fmt.Errorf("failed to parse ip_prefixes[%d]: %w", i, err))
} }
if prefix.Addr().Is4() {
builder := netipx.IPSetBuilder{}
builder.AddPrefix(tsaddr.CGNATRange())
ipSet, _ := builder.IPSet()
if !ipSet.ContainsPrefix(prefix) {
log.Warn().
Msgf("Prefix %s is not in the %s range. This is an unsupported configuration.",
prefixInConfig, tsaddr.CGNATRange())
}
}
if prefix.Addr().Is6() {
builder := netipx.IPSetBuilder{}
builder.AddPrefix(tsaddr.TailscaleULARange())
ipSet, _ := builder.IPSet()
if !ipSet.ContainsPrefix(prefix) {
log.Warn().
Msgf("Prefix %s is not in the %s range. This is an unsupported configuration.",
prefixInConfig, tsaddr.TailscaleULARange())
}
}
parsedPrefixes = append(parsedPrefixes, prefix) parsedPrefixes = append(parsedPrefixes, prefix)
} }

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"fmt" "fmt"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"fmt" "fmt"

View File

@@ -1,5 +1,5 @@
// nolint // nolint
package headscale package hscontrol
import ( import (
"context" "context"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import "testing" import "testing"

View File

@@ -1,6 +1,6 @@
//go:build ts2019 //go:build ts2019
package headscale package hscontrol
import ( import (
"net/http" "net/http"

View File

@@ -1,6 +1,6 @@
//go:build !ts2019 //go:build !ts2019
package headscale package hscontrol
import "github.com/gorilla/mux" import "github.com/gorilla/mux"

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"database/sql/driver" "database/sql/driver"
@@ -8,12 +8,12 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1" v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/samber/lo" "github.com/samber/lo"
"go4.org/netipx"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"gorm.io/gorm" "gorm.io/gorm"
"tailscale.com/tailcfg" "tailscale.com/tailcfg"
@@ -98,6 +98,14 @@ func (ma MachineAddresses) ToStringSlice() []string {
return strSlice return strSlice
} }
// AppendToIPSet adds the individual ips in MachineAddresses to a
// given netipx.IPSetBuilder.
func (ma MachineAddresses) AppendToIPSet(build *netipx.IPSetBuilder) {
for _, ip := range ma {
build.Add(ip)
}
}
func (ma *MachineAddresses) Scan(destination interface{}) error { func (ma *MachineAddresses) Scan(destination interface{}) error {
switch value := destination.(type) { switch value := destination.(type) {
case string: case string:
@@ -161,125 +169,48 @@ func (machine *Machine) isEphemeral() bool {
return machine.AuthKey != nil && machine.AuthKey.Ephemeral return machine.AuthKey != nil && machine.AuthKey.Ephemeral
} }
func (machine *Machine) canAccess(filter []tailcfg.FilterRule, machine2 *Machine) bool {
for _, rule := range filter {
// TODO(kradalby): Cache or pregen this
matcher := MatchFromFilterRule(rule)
if !matcher.SrcsContainsIPs([]netip.Addr(machine.IPAddresses)) {
continue
}
if matcher.DestsContainsIP([]netip.Addr(machine2.IPAddresses)) {
return true
}
}
return false
}
// filterMachinesByACL wrapper function to not have devs pass around locks and maps // filterMachinesByACL wrapper function to not have devs pass around locks and maps
// related to the application outside of tests. // related to the application outside of tests.
func (h *Headscale) filterMachinesByACL(currentMachine *Machine, peers Machines) Machines { func (h *Headscale) filterMachinesByACL(currentMachine *Machine, peers Machines) Machines {
return filterMachinesByACL(currentMachine, peers, &h.aclPeerCacheMapRW, h.aclPeerCacheMap) return filterMachinesByACL(currentMachine, peers, h.aclRules)
} }
// filterMachinesByACL returns the list of peers authorized to be accessed from a given machine. // filterMachinesByACL returns the list of peers authorized to be accessed from a given machine.
func filterMachinesByACL( func filterMachinesByACL(
machine *Machine, machine *Machine,
machines Machines, machines Machines,
lock *sync.RWMutex, filter []tailcfg.FilterRule,
aclPeerCacheMap map[string]map[string]struct{},
) Machines { ) Machines {
log.Trace(). result := Machines{}
Caller().
Str("self", machine.Hostname).
Str("input", machines.String()).
Msg("Finding peers filtered by ACLs")
peers := make(map[uint64]Machine) for index, peer := range machines {
// Aclfilter peers here. We are itering through machines in all users and search through the computed aclRules
// for match between rule SrcIPs and DstPorts. If the rule is a match we allow the machine to be viewable.
machineIPs := machine.IPAddresses.ToStringSlice()
// TODO(kradalby): Remove this lock, I suspect its not a good idea, and might not be necessary,
// we only set this at startup atm (reading ACLs) and it might become a bottleneck.
lock.RLock()
for _, peer := range machines {
if peer.ID == machine.ID { if peer.ID == machine.ID {
continue continue
} }
peerIPs := peer.IPAddresses.ToStringSlice()
if dstMap, ok := aclPeerCacheMap["*"]; ok { if machine.canAccess(filter, &machines[index]) || peer.canAccess(filter, machine) {
// match source and all destination result = append(result, peer)
if _, dstOk := dstMap["*"]; dstOk {
peers[peer.ID] = peer
continue
}
// match source and all destination
for _, peerIP := range peerIPs {
if _, dstOk := dstMap[peerIP]; dstOk {
peers[peer.ID] = peer
continue
}
}
// match all sources and source
for _, machineIP := range machineIPs {
if _, dstOk := dstMap[machineIP]; dstOk {
peers[peer.ID] = peer
continue
}
}
}
for _, machineIP := range machineIPs {
if dstMap, ok := aclPeerCacheMap[machineIP]; ok {
// match source and all destination
if _, dstOk := dstMap["*"]; dstOk {
peers[peer.ID] = peer
continue
}
// match source and destination
for _, peerIP := range peerIPs {
if _, dstOk := dstMap[peerIP]; dstOk {
peers[peer.ID] = peer
continue
}
}
}
}
for _, peerIP := range peerIPs {
if dstMap, ok := aclPeerCacheMap[peerIP]; ok {
// match source and all destination
if _, dstOk := dstMap["*"]; dstOk {
peers[peer.ID] = peer
continue
}
// match return path
for _, machineIP := range machineIPs {
if _, dstOk := dstMap[machineIP]; dstOk {
peers[peer.ID] = peer
continue
}
}
}
} }
} }
lock.RUnlock() return result
authorizedPeers := make(Machines, 0, len(peers))
for _, m := range peers {
authorizedPeers = append(authorizedPeers, m)
}
sort.Slice(
authorizedPeers,
func(i, j int) bool { return authorizedPeers[i].ID < authorizedPeers[j].ID },
)
log.Trace().
Caller().
Str("self", machine.Hostname).
Str("peers", authorizedPeers.String()).
Msg("Authorized peers")
return authorizedPeers
} }
func (h *Headscale) ListPeers(machine *Machine) (Machines, error) { func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
@@ -868,7 +799,7 @@ func getTags(
validTagMap := make(map[string]bool) validTagMap := make(map[string]bool)
invalidTagMap := make(map[string]bool) invalidTagMap := make(map[string]bool)
for _, tag := range machine.HostInfo.RequestTags { for _, tag := range machine.HostInfo.RequestTags {
owners, err := expandTagOwners(*aclPolicy, tag, stripEmailDomain) owners, err := getTagOwners(aclPolicy, tag, stripEmailDomain)
if errors.Is(err, errInvalidTag) { if errors.Is(err, errInvalidTag) {
invalidTagMap[tag] = true invalidTagMap[tag] = true
@@ -1182,7 +1113,7 @@ func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error {
if approvedAlias == machine.User.Name { if approvedAlias == machine.User.Name {
approvedRoutes = append(approvedRoutes, advertisedRoute) approvedRoutes = append(approvedRoutes, advertisedRoute)
} else { } else {
approvedIps, err := expandAlias([]Machine{*machine}, *h.aclPolicy, approvedAlias, h.cfg.OIDC.StripEmaildomain) approvedIps, err := h.aclPolicy.expandAlias([]Machine{*machine}, approvedAlias, h.cfg.OIDC.StripEmaildomain)
if err != nil { if err != nil {
log.Err(err). log.Err(err).
Str("alias", approvedAlias). Str("alias", approvedAlias).
@@ -1192,7 +1123,7 @@ func (h *Headscale) EnableAutoApprovedRoutes(machine *Machine) error {
} }
// approvedIPs should contain all of machine's IPs if it matches the rule, so check for first // approvedIPs should contain all of machine's IPs if it matches the rule, so check for first
if contains(approvedIps, machine.IPAddresses[0].String()) { if approvedIps.Contains(machine.IPAddresses[0]) {
approvedRoutes = append(approvedRoutes, advertisedRoute) approvedRoutes = append(approvedRoutes, advertisedRoute)
} }
} }

View File

@@ -1,4 +1,4 @@
package headscale package hscontrol
import ( import (
"fmt" "fmt"
@@ -6,7 +6,6 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"strconv" "strconv"
"sync"
"testing" "testing"
"time" "time"
@@ -1041,16 +1040,12 @@ func Test_getFilteredByACLPeers(t *testing.T) {
}, },
}, },
} }
var lock sync.RWMutex
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
aclRulesMap := generateACLPeerCacheMap(tt.args.rules)
got := filterMachinesByACL( got := filterMachinesByACL(
tt.args.machine, tt.args.machine,
tt.args.machines, tt.args.machines,
&lock, tt.args.rules,
aclRulesMap,
) )
if !reflect.DeepEqual(got, tt.want) { if !reflect.DeepEqual(got, tt.want) {
t.Errorf("filterMachinesByACL() = %v, want %v", got, tt.want) t.Errorf("filterMachinesByACL() = %v, want %v", got, tt.want)
@@ -1217,7 +1212,31 @@ func TestHeadscale_generateGivenName(t *testing.T) {
} }
func (s *Suite) TestAutoApproveRoutes(c *check.C) { func (s *Suite) TestAutoApproveRoutes(c *check.C) {
err := app.LoadACLPolicy("./tests/acls/acl_policy_autoapprovers.hujson") acl := []byte(`
{
"tagOwners": {
"tag:exit": ["test"],
},
"groups": {
"group:test": ["test"]
},
"acls": [
{"action": "accept", "users": ["*"], "ports": ["*:*"]},
],
"autoApprovers": {
"exitNode": ["tag:exit"],
"routes": {
"10.10.0.0/16": ["group:test"],
"10.11.0.0/16": ["test"],
}
}
}
`)
err := app.LoadACLPolicyFromBytes(acl, "hujson")
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
user, err := app.CreateUser("test") user, err := app.CreateUser("test")
@@ -1264,3 +1283,131 @@ func (s *Suite) TestAutoApproveRoutes(c *check.C) {
c.Assert(err, check.IsNil) c.Assert(err, check.IsNil)
c.Assert(enabledRoutes, check.HasLen, 3) c.Assert(enabledRoutes, check.HasLen, 3)
} }
func TestMachine_canAccess(t *testing.T) {
type args struct {
filter []tailcfg.FilterRule
machine2 *Machine
}
tests := []struct {
name string
machine Machine
args args
want bool
}{
{
name: "no-rules",
machine: Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
},
},
args: args{
filter: []tailcfg.FilterRule{},
machine2: &Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.2"),
},
},
},
want: false,
},
{
name: "wildcard",
machine: Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
},
},
args: args{
filter: []tailcfg.FilterRule{
{
SrcIPs: []string{"*"},
DstPorts: []tailcfg.NetPortRange{
{
IP: "*",
Ports: tailcfg.PortRange{
First: 0,
Last: 65535,
},
},
},
},
},
machine2: &Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.2"),
},
},
},
want: true,
},
{
name: "explicit-m1-to-m2",
machine: Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
},
},
args: args{
filter: []tailcfg.FilterRule{
{
SrcIPs: []string{"10.0.0.1"},
DstPorts: []tailcfg.NetPortRange{
{
IP: "10.0.0.2",
Ports: tailcfg.PortRange{
First: 0,
Last: 65535,
},
},
},
},
},
machine2: &Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.2"),
},
},
},
want: true,
},
{
name: "explicit-m2-to-m1",
machine: Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.1"),
},
},
args: args{
filter: []tailcfg.FilterRule{
{
SrcIPs: []string{"10.0.0.2"},
DstPorts: []tailcfg.NetPortRange{
{
IP: "10.0.0.1",
Ports: tailcfg.PortRange{
First: 0,
Last: 65535,
},
},
},
},
},
machine2: &Machine{
IPAddresses: MachineAddresses{
netip.MustParseAddr("10.0.0.2"),
},
},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.machine.canAccess(tt.args.filter, tt.args.machine2); got != tt.want {
t.Errorf("Machine.canAccess() = %v, want %v", got, tt.want)
}
})
}
}

142
hscontrol/matcher.go Normal file
View File

@@ -0,0 +1,142 @@
package hscontrol
import (
"fmt"
"net/netip"
"strings"
"go4.org/netipx"
"tailscale.com/tailcfg"
)
// This is borrowed from, and updated to use IPSet
// https://github.com/tailscale/tailscale/blob/71029cea2ddf82007b80f465b256d027eab0f02d/wgengine/filter/tailcfg.go#L97-L162
// TODO(kradalby): contribute upstream and make public.
var (
zeroIP4 = netip.AddrFrom4([4]byte{})
zeroIP6 = netip.AddrFrom16([16]byte{})
)
// parseIPSet parses arg as one:
//
// - an IP address (IPv4 or IPv6)
// - the string "*" to match everything (both IPv4 & IPv6)
// - a CIDR (e.g. "192.168.0.0/16")
// - a range of two IPs, inclusive, separated by hyphen ("2eff::1-2eff::0800")
//
// bits, if non-nil, is the legacy SrcBits CIDR length to make a IP
// address (without a slash) treated as a CIDR of *bits length.
// nolint
func parseIPSet(arg string, bits *int) (*netipx.IPSet, error) {
var ipSet netipx.IPSetBuilder
if arg == "*" {
ipSet.AddPrefix(netip.PrefixFrom(zeroIP4, 0))
ipSet.AddPrefix(netip.PrefixFrom(zeroIP6, 0))
return ipSet.IPSet()
}
if strings.Contains(arg, "/") {
pfx, err := netip.ParsePrefix(arg)
if err != nil {
return nil, err
}
if pfx != pfx.Masked() {
return nil, fmt.Errorf("%v contains non-network bits set", pfx)
}
ipSet.AddPrefix(pfx)
return ipSet.IPSet()
}
if strings.Count(arg, "-") == 1 {
ip1s, ip2s, _ := strings.Cut(arg, "-")
ip1, err := netip.ParseAddr(ip1s)
if err != nil {
return nil, err
}
ip2, err := netip.ParseAddr(ip2s)
if err != nil {
return nil, err
}
r := netipx.IPRangeFrom(ip1, ip2)
if !r.IsValid() {
return nil, fmt.Errorf("invalid IP range %q", arg)
}
for _, prefix := range r.Prefixes() {
ipSet.AddPrefix(prefix)
}
return ipSet.IPSet()
}
ip, err := netip.ParseAddr(arg)
if err != nil {
return nil, fmt.Errorf("invalid IP address %q", arg)
}
bits8 := uint8(ip.BitLen())
if bits != nil {
if *bits < 0 || *bits > int(bits8) {
return nil, fmt.Errorf("invalid CIDR size %d for IP %q", *bits, arg)
}
bits8 = uint8(*bits)
}
ipSet.AddPrefix(netip.PrefixFrom(ip, int(bits8)))
return ipSet.IPSet()
}
type Match struct {
Srcs *netipx.IPSet
Dests *netipx.IPSet
}
func MatchFromFilterRule(rule tailcfg.FilterRule) Match {
srcs := new(netipx.IPSetBuilder)
dests := new(netipx.IPSetBuilder)
for _, srcIP := range rule.SrcIPs {
set, _ := parseIPSet(srcIP, nil)
srcs.AddSet(set)
}
for _, dest := range rule.DstPorts {
set, _ := parseIPSet(dest.IP, nil)
dests.AddSet(set)
}
srcsSet, _ := srcs.IPSet()
destsSet, _ := dests.IPSet()
match := Match{
Srcs: srcsSet,
Dests: destsSet,
}
return match
}
func (m *Match) SrcsContainsIPs(ips []netip.Addr) bool {
for _, ip := range ips {
if m.Srcs.Contains(ip) {
return true
}
}
return false
}
func (m *Match) DestsContainsIP(ips []netip.Addr) bool {
for _, ip := range ips {
if m.Dests.Contains(ip) {
return true
}
}
return false
}

119
hscontrol/matcher_test.go Normal file
View File

@@ -0,0 +1,119 @@
package hscontrol
import (
"net/netip"
"reflect"
"testing"
"go4.org/netipx"
)
func Test_parseIPSet(t *testing.T) {
set := func(ips []string, prefixes []string) *netipx.IPSet {
var builder netipx.IPSetBuilder
for _, ip := range ips {
builder.Add(netip.MustParseAddr(ip))
}
for _, pre := range prefixes {
builder.AddPrefix(netip.MustParsePrefix(pre))
}
s, _ := builder.IPSet()
return s
}
type args struct {
arg string
bits *int
}
tests := []struct {
name string
args args
want *netipx.IPSet
wantErr bool
}{
{
name: "simple ip4",
args: args{
arg: "10.0.0.1",
bits: nil,
},
want: set([]string{
"10.0.0.1",
}, []string{}),
wantErr: false,
},
{
name: "simple ip6",
args: args{
arg: "2001:db8:abcd:1234::2",
bits: nil,
},
want: set([]string{
"2001:db8:abcd:1234::2",
}, []string{}),
wantErr: false,
},
{
name: "wildcard",
args: args{
arg: "*",
bits: nil,
},
want: set([]string{}, []string{
"0.0.0.0/0",
"::/0",
}),
wantErr: false,
},
{
name: "prefix4",
args: args{
arg: "192.168.0.0/16",
bits: nil,
},
want: set([]string{}, []string{
"192.168.0.0/16",
}),
wantErr: false,
},
{
name: "prefix6",
args: args{
arg: "2001:db8:abcd:1234::/64",
bits: nil,
},
want: set([]string{}, []string{
"2001:db8:abcd:1234::/64",
}),
wantErr: false,
},
{
name: "range4",
args: args{
arg: "192.168.0.0-192.168.255.255",
bits: nil,
},
want: set([]string{}, []string{
"192.168.0.0/16",
}),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parseIPSet(tt.args.arg, tt.args.bits)
if (err != nil) != tt.wantErr {
t.Errorf("parseIPSet() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseIPSet() = %v, want %v", got, tt.want)
}
})
}
}

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