Compare commits

...

3102 Commits

Author SHA1 Message Date
Kristoffer Dalby
72fcb93ef3 cli: ensure tagged-devices is included in profile list (#2991) 2026-01-09 16:31:23 +01:00
Kristoffer Dalby
f5c779626a nix: use testers.nixosTest instead of nixosTest
nixosTest was renamed to testers.nixosTest in nixpkgs.
2026-01-09 12:57:32 +01:00
Kristoffer Dalby
d227b3a135 docs: update integration testing docs for concurrent execution
Update documentation to reflect the new concurrent test execution
capabilities and add guidance on run ID isolation.

AGENTS.md:
- Add examples for running multiple tests concurrently
- Document run ID format and container naming conventions
- Update "Critical Notes" to explain isolation mechanisms

.claude/agents/headscale-integration-tester.md:
- Add "Concurrent Execution and Run ID Isolation" section
- Document forbidden and safe operations for cleanup
- Add "Agent Session Isolation Rules" for multi-agent environments
- Add 6th core responsibility about concurrent execution awareness
- Add ISOLATION PRINCIPLE to critical principles
- Update pre-test cleanup documentation
2026-01-09 12:34:16 +01:00
Kristoffer Dalby
0bcfdc29ad cmd/hi: enable concurrent test execution
Remove the concurrent test prevention logic and update cleanup to use
run ID-based isolation, allowing multiple tests to run simultaneously.

Changes:
- cleanup: Add killTestContainersByRunID() to clean only containers
  belonging to a specific run, add cleanupStaleTestContainers() to
  remove only stopped/exited containers without affecting running tests
- docker: Remove RunningTestInfo, checkForRunningTests(), and related
  error types, update cleanupAfterTest() to use run ID-based cleanup
- run: Remove Force flag and concurrent test prevention check

The test runner now:
- Allows multiple concurrent test runs on the same Docker daemon
- Cleans only stale containers before tests (not running ones)
- Cleans only containers with matching run ID after tests
- Prints run ID and monitoring info for operator visibility
2026-01-09 12:34:16 +01:00
Kristoffer Dalby
87c230d251 integration: add run ID isolation for concurrent test execution
Add run ID-based isolation to container naming and network setup to
enable multiple integration tests to run concurrently on the same
Docker daemon without conflicts.

Changes:
- hsic: Add run ID prefix to headscale container names and use dynamic
  port allocation for metrics endpoint (port 0 lets kernel assign)
- tsic: Add run ID prefix to tailscale container names
- dsic: Add run ID prefix to DERP container names
- scenario: Use run ID-aware test suite container name for network setup

Container naming now follows: {type}-{runIDShort}-{identifier}-{hash}
Example: ts-mdjtzx-1-74-fgdyls, hs-mdjtzx-pingallbyip-abc123

The run ID is obtained from HEADSCALE_INTEGRATION_RUN_ID environment
variable via dockertestutil.GetIntegrationRunID().
2026-01-09 12:34:16 +01:00
github-actions[bot]
84c092a9f9 flake.lock: Update
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/35f5903' (2025-10-15)
  → 'github:NixOS/nixpkgs/3edc4a3' (2025-12-27)
2025-12-28 08:25:28 +01:00
Florian Preinstorfer
9146140217 Add headscale-operator
Ref: #1523
2025-12-23 20:22:57 +01:00
Kristoffer Dalby
5103b35f3c sqliteconfig: add config opt for tx locking
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-22 14:01:40 +01:00
Justin Angel
7be20912f5 oidc: make email verification configurable
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-18 11:42:32 +00:00
Kristoffer Dalby
e8753619de capver: generate
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-18 10:02:23 +01:00
Kristoffer Dalby
251e16d772 tools/capver: regenerate from docker tags
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-18 10:02:23 +01:00
Kristoffer Dalby
3f0bfe28cc changelog: prepare for 0.28.0 beta
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-17 15:15:43 +01:00
Kristoffer Dalby
82d4275c3b mapper: correct some variable names missed from change
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 13:19:26 +01:00
Kristoffer Dalby
f3767dddf8 batcher: ensure removal from batcher
Fixes #2924

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 13:19:26 +01:00
Florian Preinstorfer
5c6cd62df1 Legacy preauthkeys must be used as-is 2025-12-17 13:05:08 +01:00
Shourya Gautam
56bec66a44 app: only wire up debug server if set
Fixes #2871

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 12:32:04 +01:00
Kristoffer Dalby
f0e464dc36 policy: add test to confirm group cant approve tag
Confirms #2891 is implemented correctly.

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 09:32:05 +01:00
Kristoffer Dalby
2c3c943acf .github/workflows: split long TestAutoApproveMultiNetwork into multiple jobs
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 09:32:05 +01:00
Kristoffer Dalby
a50bd13930 integration: prepare AutoApprove test for new tags
Validates #2891

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 09:32:05 +01:00
Kristoffer Dalby
5655ef86d7 AGENTS: golangci-lint from main, no "full matrix"
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-17 09:32:05 +01:00
Kristoffer Dalby
21ba197d06 integration: make entrypoint override more robust
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
9d77207ed8 policy: clarify usernam resolve comment
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
cf1ad47b42 flake: remove hi from shell
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
a288f04a1a Dockerfile: align packages
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
5767ca5085 change: smarter change notifications
This commit replaces the ChangeSet with a simpler bool based
change model that can be directly used in the map builder to
build the appropriate map response based on the change that
has occured. Previously, we fell back to sending full maps
for a lot of changes as that was consider "the safe" thing to
do to ensure no updates were missed.

This was slightly problematic as a node that already has a list
of peers will only do full replacement of the peers if the list
is non-empty, meaning that it was not possible to remove all
nodes (if for example policy changed).

Now we will keep track of last seen nodes, so we can send remove
ids, but also we are much smarter on how we send smaller, partial
maps when needed.

Fixes #2389

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
f67ed36fe2 integration: replicate tag propagation issue
This commit adds tests to validate that there are
issues with how we propagate tag changes in the system.

This replicates #2389

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
506bd8c8eb policy: more accurate node change
This commit changes so that node changes to the policy is
calculated if any of the nodes has changed in a way that might
affect the policy.

Previously we just checked if the number of nodes had changed,
which meant that if a node was added and removed, we would be
in a bad state.

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
daf9f36c78 editorconfig: add basic editor config
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-12-16 10:12:36 +01:00
Kristoffer Dalby
616c0e895d batcher: fix closed panic
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-15 16:28:27 +01:00
Kristoffer Dalby
c4600346f9 .github/workflows: prebuilt integration test artifacts (#2954)
This PR restructures the integration tests and prebuilds all common assets used in all tests:

Headscale and Tailscale HEAD image
hi binary that is used to run tests
go cache is warmed up for compilation of the test
This essentially means we spend 6-10 minutes building assets before any tests starts, when that is done, all tests can just sprint through.

It looks like we are saving 3-9 minutes per test, and since we are limited to running max 20 concurrent tests across the repo, that means we had a lot of double work.

There is currently 113 checks, so we have to do five runs of 20, and the saving should be quite noticeable! I think the "worst case" saving would be 20+min and "best case" probably towards an hour.
2025-12-12 23:01:52 +01:00
Kristoffer Dalby
642073f4b8 types: add option to disable taildrop, improve tests (#2955) 2025-12-12 11:35:16 +01:00
Kristoffer Dalby
87bd67318b golangci-lint: use forbidigo to block time.Sleep (#2946) 2025-12-10 16:45:59 +00:00
Kristoffer Dalby
0e1673041c all: remove deadcode (#2952) 2025-12-10 15:55:15 +01:00
Kristoffer Dalby
f3f2d30004 cli: better formatting of lists (#2951) 2025-12-10 12:33:21 +01:00
Kristoffer Dalby
c8376e44a2 mapper: move tail node conversion to node type (#2950) 2025-12-10 09:16:22 +01:00
Rogan Lynch
5d0a6ab0e9 fix: list-routes command now respects identifier filter with JSON output
Fixes #2927

In v0.27.0, the list-routes command with -i flag and -o json output
was returning all nodes instead of just the specified node.

The issue was that JSON output was happening before the identifier
filtering logic. This change moves the JSON output to after both
the identifier filter and route existence filter are applied,
ensuring the correct filtered results are returned.

This restores the v0.26.1 behavior where:
  headscale nodes list-routes -i 12 -o json
correctly returns only node 12's route information.
2025-12-10 06:19:17 +01:00
Kristoffer Dalby
22ee2bfc9c tags: process tags on registration, simplify policy (#2931)
This PR investigates, adds tests and aims to correctly implement Tailscale's model for how Tags should be accepted, assigned and used to identify nodes in the Tailscale access and ownership model.

When evaluating in Headscale's policy, Tags are now only checked against a nodes "tags" list, which defines the source of truth for all tags for a given node. This simplifies the code for dealing with tags greatly, and should help us have less access bugs related to nodes belonging to tags or users.

A node can either be owned by a user, or a tag.

Next, to ensure the tags list on the node is correctly implemented, we first add tests for every registration scenario and combination of user, pre auth key and pre auth key with tags with the same registration expectation as observed by trying them all with the Tailscale control server. This should ensure that we implement the correct behaviour and that it does not change or break over time.

Lastly, the missing parts of the auth has been added, or changed in the cases where it was wrong. This has in large parts allowed us to delete and simplify a lot of code.
Now, tags can only be changed when a node authenticates or if set via the CLI/API. Tags can only be fully overwritten/replaced and any use of either auth or CLI will replace the current set if different.

A user owned device can be converted to a tagged device, but it cannot be changed back. A tagged device can never remove the last tag either, it has to have a minimum of one.
2025-12-08 18:51:07 +01:00
Dusty Mabe
1f5df017a1 hscontrol: log acme/autocert errors (#2933) 2025-12-08 16:39:30 +00:00
Florian Preinstorfer
bba91a89be Use lists for integration docs
Refactor the tables in "Tools" and "WebUI" integration pages to lists.
Lists are easier to extend and contributions are easier to review.
2025-12-08 12:50:19 +01:00
Florian Preinstorfer
6359511a62 Use debian13 distroless images 2025-12-07 20:58:29 +01:00
adinhodovic
d2fcd5b95b docs(tools): Add tailscale-exporter
A Prometheus exporter for Tailscale and Headscale that provides tailnet-level metrics using the Tailscale/Headscale API.
2025-12-07 14:39:08 +01:00
Kristoffer Dalby
15c84b34e0 policy: allow tags to own tags (#2930) 2025-12-06 10:23:35 +01:00
Kristoffer Dalby
eb788cd007 make tags first class node owner (#2885)
This PR changes tags to be something that exists on nodes in addition to users, to being its own thing. It is part of moving our tags support towards the correct tailscale compatible implementation.

There are probably rough edges in this PR, but the intention is to get it in, and then start fixing bugs from 0.28.0 milestone (long standing tags issue) to discover what works and what doesnt.

Updates #2417
Closes #2619
2025-12-02 12:01:25 +01:00
Kristoffer Dalby
705b239677 changelog: prep for 0.27.2 rc
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-12-02 12:01:02 +01:00
Kristoffer Dalby
cb4d5b1906 hscontrol/oidc: fix ACL policy not applied to new OIDC nodes (#2890)
Fixes #2888
Fixes #2896
2025-12-02 12:01:02 +01:00
Vitalij Dovhanyc
0078eb7790 chore: fix filterHash to work with autogroup:self in the acls (#2882) 2025-12-02 12:01:02 +01:00
Kristoffer Dalby
3cf2d7195a auth: ensure machines are allowed in when pak change (#2917) 2025-12-02 12:01:02 +01:00
Kristoffer Dalby
16d811b306 cli: remove node move command (#2922) 2025-12-01 21:43:31 +01:00
Kristoffer Dalby
eec196d200 modernize: run gopls modernize to bring up to 1.25 (#2920) 2025-12-01 19:40:25 +01:00
Kristoffer Dalby
bfcd9d261d cmd/hi: reject if we are already running (#2919) 2025-12-01 19:40:08 +01:00
Florian Preinstorfer
f00c412cde Move static doc assets into docs/assets 2025-11-28 21:27:54 +01:00
Florian Preinstorfer
2010805712 Provide Headscale's favicon at its expected place
Assets need to reside within the docs/ directory for mkdocs to pick them
up.
2025-11-28 21:27:54 +01:00
Florian Preinstorfer
c5133ee5d3 Fix trailing whitespace 2025-11-28 21:27:54 +01:00
Florian Preinstorfer
9c33cbfdc8 Exclude docs/ only for prettier pre-commit hook
Applying the the built-in hooks to docs/ seems to be fine.
2025-11-28 21:27:54 +01:00
Florian Preinstorfer
9b327f6b56 Update pre-commit-hooks 2025-11-28 21:27:54 +01:00
Kristoffer Dalby
9368fee1c5 generate: add new patches (#2921) 2025-11-28 17:00:52 +01:00
Kristoffer Dalby
ed78bf4b98 cmd/hi: improve test cleanup to reduce CI disk usage (#2881) 2025-11-28 16:59:54 +01:00
Kristoffer Dalby
db293e0698 hscontrol/state: make NodeStore batch configuration tunable (#2886) 2025-11-28 16:38:29 +01:00
pwuersch
9c4c017eac docs: Enable automatic theme switching
Signed-off-by: pwuersch <49908921+pwuersch@users.noreply.github.com>
2025-11-24 06:43:22 +01:00
János Benjamin Antal
14af9b3ab1 Add docs to manage headscale from another local user 2025-11-24 06:37:35 +01:00
Florian Preinstorfer
72d5fd04a7 Remove duplicated documentation and link to getting started instead 2025-11-18 11:07:49 +01:00
Florian Preinstorfer
e86d063056 Mention /health instead of /windows 2025-11-18 11:07:49 +01:00
Acha
e0c9e18e22 Update OIDC documentation for allowed groups filter
Clarify configuration for allowed groups filter with Microsoft Entra ID.
2025-11-15 17:44:00 +01:00
Florian Preinstorfer
21af106f68 Containers should be read-only
This improves security and explicitly fails on startup when a user picks
the wrong directory to store its data.

- Run in read-only mode
- Make /var/run/headscale a read-write tmpfs
- Mount the config volume read-only
- Use the /health endpoint to check if Headscale is up
2025-11-14 14:51:27 +01:00
Kristoffer Dalby
7fb0f9a501 batcher: send endpoint and derp only updates. (#2856) 2025-11-13 20:38:49 +01:00
Kristoffer Dalby
4b25976288 db: add comment to always check errors in migration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-13 09:46:40 -06:00
Kristoffer Dalby
1c146f70e9 db: remove _schema from migration tests
Previously we tested migrations on schemas and dumps
of old databases.

The problems with testing migrations against the schemas
is that the migration table is empty, so we try to run
migrations that are already ran on that schema, which might
blow up.

This commit removes the schema approach and just leaves all
the dumps, which include the migration table.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-13 09:46:40 -06:00
Florian Preinstorfer
249630bed8 Add API documentation
Document the API endpoint and the built-in swagger docs at /swagger. The
remote control docs are just a use case for gRPC - move it in the API
docs and update links to it.
2025-11-13 15:22:55 +01:00
Kristoffer Dalby
75247f82b8 hscontrol/db: add init schema, drop pre-0.25 support (#2883) 2025-11-13 04:44:10 -06:00
Tianon Gravi
665cc44094 Explicitly drop apt-get clean and use dist-clean
The former is a no-op in the base images (45491f2c5c/scripts/debuerreotype-minimizing-config (L87-L109)), and `apt-get dist-clean` is a safer/better version of the `rm -rf /var/lib/apt/lists/*` that keeps the cryptographic bits that help prevent downgrade attacks.
2025-11-13 07:15:22 +01:00
Kristoffer Dalby
8394e7094a capver: update latest (#2774) 2025-11-12 20:26:54 +01:00
Kristoffer Dalby
da9018a0eb types: make pre auth key use bcrypt (#2853) 2025-11-12 16:36:36 +01:00
Kristoffer Dalby
e3ced80278 hscontrol: consolidate assets into single package
Move favicon.png, style.css, and headscale.svg to hscontrol/assets/
and create a single assets.go file with all embed directives.

Update hscontrol/handlers.go and hscontrol/templates/general.go to
use the centralized assets package.
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
09c9762fe0 hscontrol: convert BlankHandler to use elem-go 2025-11-12 08:28:12 -06:00
Kristoffer Dalby
75e24de7bd flake: disable CGO in dev shell
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
2aa5b8b68d changelog: add entry for templates redesign 2025-11-12 08:28:12 -06:00
Kristoffer Dalby
4e77e910c5 hscontrol: use octal literal syntax in test 2025-11-12 08:28:12 -06:00
Kristoffer Dalby
a496864762 hscontrol: add template HTML consistency test
Add test to validate HTML template output consistency across all
templates (OIDC callback, registration, Windows, Apple).

Verifies all templates produce valid HTML5 with:
- Proper DOCTYPE declaration
- HTML5 lang attribute
- UTF-8 charset
- Viewport meta tag
- Semantic HTML structure

Ensures template refactoring maintains standards compliance.
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
3ed1067a95 hscontrol/templates: refactor to use CSS classes and embedded files
Refactor template system to use go:embed for external assets and
CSS classes for styling instead of inline styles:

- general.go: Add go:embed directives for style.css and headscale.svg,
  replace inline styles with CSS classes (H1, H2, H3, P, etc.),
  add mdTypesetBody wrapper with Material for MkDocs styling

- apple.go, oidc_callback.go, register_web.go, windows.go:
  Update to use new CSS-based helper functions (H1, H2, P, etc.)
  and mdTypesetBody for consistent layout

This separates content from presentation, making templates easier
to maintain and update. All styling is now centralized in style.css
with Material for MkDocs design system.
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
285c4e46a9 hscontrol/templates: add Material for MkDocs design assets
Add design system assets for HTML templates:
- headscale.svg: Logo with optimized viewBox for proper alignment
- style.css: Material for MkDocs CSS variables and typography
- design.go: Design system constants for consistent styling

The logo viewBox is adjusted to 32.92 0 1247.08 640 to eliminate
whitespace from the original export and ensure left alignment with
text content.
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
89285c317b templates: migrate OIDC callback to elem-go
Replace html/template with type-safe elem-go templating for OIDC
callback page. Improves consistency with other templates and provides
compile-time safety. All UI elements and styling preserved.
2025-11-12 08:28:12 -06:00
Kristoffer Dalby
d14be8d43b nix: add NixOS module and tests (#2857) 2025-11-12 13:11:38 +00:00
Kristoffer Dalby
000d5c3b0c prettier: use standard config for all files including changelog (#2879) 2025-11-12 13:59:43 +01:00
Teej
218a8db1b9 add favicon to webpages (#2858)
Co-authored-by: TeejMcSteez <tjhall047@gmail.com>
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-11-12 03:46:57 +00:00
Kristoffer Dalby
1dcb04ce9b changelog: add changelog entry
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-11 17:27:00 -06:00
Andrey Bobelev
299cef4e99 fix: free ips from usedIps ipset on DeleteNode 2025-11-11 17:27:00 -06:00
Kristoffer Dalby
6d24afba1c add pre-commit hooks, move claude to agents. (#2877) 2025-11-11 20:35:23 +01:00
Kristoffer Dalby
f658a8eacd mkdocs: 0.27.1
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-11 13:17:02 -06:00
Kristoffer Dalby
785168a7b8 changelog: prepare for 0.27.1
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-11 13:17:02 -06:00
Kristoffer Dalby
3bd4ecd9cd fix: preserve node expiry when tailscaled restarts
When tailscaled restarts, it sends RegisterRequest with Auth=nil and
Expiry=zero. Previously this was treated as a logout because
time.Time{}.Before(time.Now()) returns true.

Add early return in handleRegister() to detect this case and preserve
the existing node state without modification.

Fixes #2862
2025-11-11 12:47:48 -06:00
Kristoffer Dalby
3455d1cb59 hscontrol/db: fix RenameUser to use Updates()
RenameUser only modifies Name field, should use Updates() not Save().
2025-11-11 12:47:48 -06:00
Kristoffer Dalby
ddd31ba774 hscontrol: use Updates() instead of Save() for partial updates
Changed UpdateUser and re-registration flows to use Updates() which only
writes modified fields, preventing unintended overwrites of unchanged fields.

Also updated UsePreAuthKey to use Model().Update() for single field updates
and removed unused NodeSave wrapper.
2025-11-11 12:47:48 -06:00
Kristoffer Dalby
4a8dc2d445 hscontrol/state,db: preserve node expiry on MapRequest updates
Fixes a regression introduced in v0.27.0 where node expiry times were
being reset to zero when tailscaled restarts and sends a MapRequest.

The issue was caused by using GORM's Save() method in persistNodeToDB(),
which overwrites ALL fields including zero values. When a MapRequest
updates a node (without including expiry information), Save() would
overwrite the database expiry field with a zero value.

Changed to use Updates() which only updates non-zero values, preserving
existing database values when struct pointer fields are nil.

In BackfillNodeIPs, we need to explicitly update IPv4/IPv6 fields even
when nil (to remove IPs), so we use Select() to specify those fields.

Added regression test that validates expiry is preserved after MapRequest.

Fixes #2862
2025-11-11 12:47:48 -06:00
Kristoffer Dalby
773a46a968 integration: add test to replicate #2862
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-11 12:47:48 -06:00
Kristoffer Dalby
4728a2ba9e hscontrol/state: allow expired auth keys for node re-registration
Skip auth key validation for existing nodes re-registering with the same
NodeKey. Pre-auth keys are only required for initial authentication.

NodeKey rotation still requires a valid auth key as it is a security-sensitive
operation that changes the node's cryptographic identity.

Fixes #2830
2025-11-11 05:12:59 -06:00
Florian Preinstorfer
abed534628 Document how to restrict access to exit nodes per user/group
Updates: #2855
Ref: #2784
2025-11-11 11:51:35 +01:00
Kristoffer Dalby
21e3f2598d policy: fix issue where non existent user results in empty ssh pol
When we encounter a source we cannot resolve, we skipped the whole rule,
even if some of the srcs could be resolved. In this case, if we had one user
that exists and one that does not.

In the regular policy, we log this, and still let a rule be created from what
does exist, while in the SSH policy we did not.

This commit fixes it so the behaviour is the same.

Fixes #2863

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-10 20:34:12 +01:00
Kristoffer Dalby
a28d9bed6d policy: reproduce 2863 in test
reproduce that if a user does not exist, the ssh policy ends up empty

Updates #2863

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-10 20:34:12 +01:00
Kristoffer Dalby
28faf8cd71 db: add defensive removal of old indicies
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-10 20:07:29 +01:00
Kristoffer Dalby
5a2ee0c391 db: add comment about removing migrations
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-10 17:32:39 +01:00
Andrey Bobelev
5cd15c3656 fix: make state cookies valid when client uses multiple login URLs
On Windows, if the user clicks the Tailscale icon in the system tray,
it opens a login URL in the browser.

When the login URL is opened, `state/nonce` cookies are set for that particular URL.

If the user clicks the icon again, a new login URL is opened in the browser,
and new cookies are set.

If the user proceeds with auth in the first tab,
the redirect results in a "state did not match" error.

This patch ensures that each opened login URL sets an individual cookie
that remains valid on the `/oidc/callback` page.

`TestOIDCMultipleOpenedLoginUrls` illustrates and tests this behavior.
2025-11-10 16:27:46 +01:00
Kristoffer Dalby
2024219bd1 types: Distinguish subnet and exit node access
When we fixed the issue of node visibility of nodes
that only had access to eachother because of a subnet
route, we gave all nodes access to all exit routes by
accident.

This commit splits exit nodes and subnet routes in the
access.

If a matcher indicates that the node should have access to
any part of the subnet routes, we do not remove it from the
node list.

If a matcher destination is equal to the internet, and the
target node is an exit node, we also do not remove the access.

Fixes #2784
Fixes #2788

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Kristoffer Dalby
d9c3eaf8c8 matcher: Add func for comparing Dests and TheInternet
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Kristoffer Dalby
bd9cf42b96 types: NodeView CanAccess uses internal
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Kristoffer Dalby
d7a43a7cf1 state: use AllApprovedRoutes instead of SubnetRoutes
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Kristoffer Dalby
1c0bb0338d types: split SubnetRoutes and ExitRoutes
There are situations where the subnet routes and exit nodes
must be treated differently. This splits it so SubnetRoutes
only returns routes that are not exit routes.

It adds `IsExitRoutes` and `AllApprovedRoutes` for convenience.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Kristoffer Dalby
c649c89e00 policy: Reproduce exit node visibility issues
Reproduces #2784 and #2788

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-11-02 13:19:59 +01:00
Vitalij Dovhanyc
af2de35b6c chore: fix autogroup:self with other acl rules (#2842) 2025-11-02 10:48:27 +00:00
Kristoffer Dalby
02c7c1a0e7 cli: only validate bypass-grpc set policy (#2854) 2025-11-02 09:42:59 +00:00
Copilot
d23fa26395 Fix flaky TestShuffleDERPMapDeterministic by ensuring deterministic map iteration (#2848)
Co-authored-by: kradalby <98431+kradalby@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2025-11-02 10:05:23 +01:00
Andrey
f9bb88ad24 expire nodes with a custom timestamp (#2828) 2025-11-01 08:09:13 +01:00
Kristoffer Dalby
456a5d5cce db: ignore _litestream tables when validating (#2843) 2025-11-01 07:08:22 +00:00
Kristoffer Dalby
ddbd3e14ba db: remove all old, unused tables (#2844) 2025-11-01 08:03:37 +01:00
Florian Preinstorfer
0a43aab8f5 Use Debian 12 as minimum version for the deb package 2025-10-28 05:55:26 +01:00
Florian Preinstorfer
4bd614a559 Use current stable base images for Debian and Alpine 2025-10-28 05:55:26 +01:00
Kristoffer Dalby
19a33394f6 changelog: set 0.27 date (#2823) 2025-10-27 12:14:02 +01:00
Kristoffer Dalby
84fe3de251 integration: reduce TestAutoApproveMultiNetwork matrix to 3 tests (#2815) 2025-10-27 11:08:52 +00:00
Paarth Shah
450a7b15ec #2796: Add creation_time and ko_data_creation_time to goreleaser.yml kos 2025-10-27 11:18:57 +01:00
Kristoffer Dalby
64b7142e22 .goreleaser: add upgrade section (#2820) 2025-10-27 10:41:52 +01:00
Kristoffer Dalby
52d27d58f0 hscontrol: add /version HTTP endpoint (#2821) 2025-10-27 10:41:34 +01:00
Kristoffer Dalby
e68e2288f7 gen: test-integration (#2814) 2025-10-24 17:22:53 +02:00
Kristoffer Dalby
c808587de0 cli: do not show new pre-releases on stable (#2813) 2025-10-24 13:15:53 +02:00
Kristoffer Dalby
2bf1200483 policy: fix autogroup:self propagation and optimize cache invalidation (#2807) 2025-10-23 17:57:41 +02:00
Kristoffer Dalby
66826232ff integration: add tests for api bypass (#2811) 2025-10-22 16:30:25 +02:00
Kristoffer Dalby
1cdea7ed9b stricter hostname validation and replace (#2383) 2025-10-22 13:50:39 +02:00
Elyas Asmad
2c9e98d3f5 fix: guard every error statement with early return (#2810) 2025-10-22 13:48:07 +02:00
Florian Preinstorfer
8becb7e54a Mention explicitly that @ is only required in policy 2025-10-21 14:28:03 +02:00
Florian Preinstorfer
ed38d00aaa Fix autogroup:self alternative example
Also indent and split the comment into two lines to avoid horizontal
scrolling.
2025-10-21 14:28:03 +02:00
Florian Preinstorfer
8010cc574e Remove outdated hint about an empty config file 2025-10-19 17:14:15 +02:00
Juanjo Presa
c97d0ff23d Fix fatal error on missing config file by handling viper.ConfigFileNotFoundError
Correctly identify Viper's ConfigFileNotFoundError in LoadConfig to log a warning and use defaults, unifying behavior with empty config files. Fixes fatal error when no config file is present for CLI commands relying on environment variables.
2025-10-19 15:29:47 +02:00
Florian Preinstorfer
047dbda136 Add FAQ on how to disable log submission
Fixes: #2793
2025-10-19 08:24:23 +02:00
Florian Preinstorfer
2a1392fb5b Add healthcheck to container docs 2025-10-19 08:22:30 +02:00
Florian Preinstorfer
46477b8021 Downgrade completed broadcast message to debug 2025-10-18 07:56:59 +02:00
Kristoffer Dalby
c87471136b integration: eventually fixups (#2799) 2025-10-17 08:28:30 +02:00
Kristoffer Dalby
e7a28a14af changelog: prepare for 0.27.0 (#2797) 2025-10-16 19:04:07 +02:00
Kristoffer Dalby
4912769ab3 update dependencies (#2798) 2025-10-16 19:03:30 +02:00
Stavros Kois
c07cc491bf add health command (#2659)
* add health command
* update health check implementation to allow for more checks to added over time
* add change changelog entry
2025-10-16 12:00:11 +00:00
Vitalij Dovhanyc
c2a58a304d feat: add autogroup:self (#2789) 2025-10-16 12:59:52 +02:00
Kristoffer Dalby
fddc7117e4 stability and race conditions in auth and node store (#2781)
This PR addresses some consistency issues that was introduced or discovered with the nodestore.

nodestore:
Now returns the node that is being put or updated when it is finished. This closes a race condition where when we read it back, we do not necessarily get the node with the given change and it ensures we get all the other updates from that batch write.

auth:
Authentication paths have been unified and simplified. It removes a lot of bad branches and ensures we only do the minimal work.
A comprehensive auth test set has been created so we do not have to run integration tests to validate auth and it has allowed us to generate test cases for all the branches we currently know of.

integration:
added a lot more tooling and checks to validate that nodes reach the expected state when they come up and down. Standardised between the different auth models. A lot of this is to support or detect issues in the changes to nodestore (races) and auth (inconsistencies after login and reaching correct state)

This PR was assisted, particularly tests, by claude code.
2025-10-16 12:17:43 +02:00
Florian Preinstorfer
881a6b9227 The sequential prefix allocation uses a best-effort approach
Fixes: #2682
2025-10-15 17:07:13 +02:00
yckwan
3fbde7a1b6 Update official.md
in the step 5 file default value is [line11] ExecStart=/usr/bin/headscale serve
2025-10-13 17:06:56 +02:00
Andrey Bobelev
c4a8c038cd fix: return valid AuthUrl in followup request on expired reg id
- tailscale client gets a new AuthUrl and sets entry in the regcache
- regcache entry expires
- client doesn't know about that
- client always polls followup request а gets error

When user clicks "Login" in the app (after cache expiry), they visit
invalid URL and get "node not found in registration cache". Some clients
on Windows for e.g. can't get a new AuthUrl without restart the app.

To fix that we can issue a new reg id and return user a new valid
AuthUrl.

RegisterNode is refactored to be created with NewRegisterNode() to
autocreate channel and other stuff.
2025-10-11 05:57:39 +02:00
Andrey Bobelev
022098fe4e chore: make reg cache expiry tunable
Mostly for the tests, opts:

- tuning.register_cache_expiration
- tuning.register_cache_cleanup
2025-10-11 05:57:39 +02:00
Florian Preinstorfer
bd35fcf338 Add FAQ entry about policy migration in the database 2025-09-17 16:32:29 +02:00
Florian Preinstorfer
2d680b5ebb Misc typos and spelling 2025-09-17 16:32:29 +02:00
Kristoffer Dalby
ed3a9c8d6d mapper: send change instead of full update (#2775) 2025-09-17 14:23:21 +02:00
Kristoffer Dalby
4de56c40d8 flake: goreleaser doesnt follow go nix convention (#2779) 2025-09-17 09:41:05 +02:00
github-actions[bot]
40b3d54c1f flake.lock: Update (#2755) 2025-09-14 16:15:51 +00:00
Florian Preinstorfer
30d12dafed Add FAQ entry about the recommended upgrade path 2025-09-13 08:15:01 +02:00
Kristoffer Dalby
2b30a15a68 cmd: add option to get and set policy directly from database (#2765) 2025-09-12 16:55:15 +02:00
Kristoffer Dalby
2938d03878 policy: reject unsupported fields (#2764) 2025-09-12 14:47:56 +02:00
Kristoffer Dalby
1b1c989268 {policy, node}: allow return paths in route reduction (#2767) 2025-09-12 11:47:51 +02:00
Kristoffer Dalby
3950f8f171 cli: use gobuild version handling (#2770) 2025-09-12 11:47:31 +02:00
Kristoffer Dalby
ee0ef396a2 policy: fix ssh usermap, fixing autogroup:nonroot (#2768) 2025-09-12 09:12:30 +02:00
Kristoffer Dalby
7056fbb63b derp: fix flaky shuffle test (#2772) 2025-09-11 13:49:02 +00:00
Kristoffer Dalby
c91b9fc761 poll: add missing godoc (#2763) 2025-09-11 14:15:19 +02:00
Kristoffer Dalby
d41fb4d540 app: fix sigint hanging
When the node notifier was replaced with batcher, we removed
its closing, but forgot to add the batchers so it was never
stopping node connections and waiting forever.

Fixes #2751

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-11 11:53:26 +02:00
Kristoffer Dalby
01c1f6f82a policy: validate error message for asterix in ssh (#2766) 2025-09-10 18:41:43 +02:00
Oleksii Samoliuk
3f6657ae57 fix: documentation 2025-09-09 20:54:47 +02:00
Kristoffer Dalby
0512f7c57e .github/ISSUE_TEMPLATE: add node number to environment
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 19:04:23 +02:00
Florian Preinstorfer
c6427aa296 Use group id instead of group name for Entra ID 2025-09-09 12:23:34 +02:00
Florian Preinstorfer
4e6d42d5bd Keycloak's group format is configurable 2025-09-09 12:23:34 +02:00
Florian Preinstorfer
8ff5baadbe Refresh OIDC docs
The UserInfo endpoint is always queried since 5d8a2c2.

This allows to use all OIDC related features without any extra
configuration on Authelia.

For Keycloak, its sufficient to add the groups mapper to the userinfo
endpoint.
2025-09-09 12:23:34 +02:00
Florian Preinstorfer
2f3c365b68 Describe how to remove a DERP region
Add documentation for d29feaef.

Fixes: #2450
2025-09-09 11:05:30 +02:00
Kristoffer Dalby
4893cdac74 integration: make timestamp const
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
476f30ab20 state: ensure netinfo is preserved and not removed
the client will send a lot of fields as `nil` if they have
not changed. NetInfo, which is inside Hostinfo, is one of those
fields and we often would override the whole hostinfo meaning that
we would remove netinfo if it hadnt changed.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
233dffc186 lint and leftover
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
39443184d6 gen: new proto version
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
0303b76e1f postgres uses more memory
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
684239e015 cmd/mapresponses: add mini tool to inspect mapresp state from integration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
81b3e8f743 util: harden parsing of traceroute
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
50ed24847b debug: add json and improve
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
9b962956b5 integration: Eventually, debug output, lint and format
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
3b16b75fe6 integration: rework retry for waiting for node sync
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
9d236571f4 state/nodestore: in memory representation of nodes
Initial work on a nodestore which stores all of the nodes
and their relations in memory with relationship for peers
precalculated.

It is a copy-on-write structure, replacing the "snapshot"
when a change to the structure occurs. It is optimised for reads,
and while batches are not fast, they are grouped together
to do less of the expensive peer calculation if there are many
changes rapidly.

Writes will block until commited, while reads are never
blocked.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
38be30b6d4 derp: allow override to ip for debug
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
7f8b14f6f3 .github/workflows: remove integration retry
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
3326c5b7ec cmd/hi: lint and format
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
b6d5788231 mapper: produce map before poll
Before this patch, we would send a message to each "node stream"
that there is an update that needs to be turned into a mapresponse
and sent to a node.

Producing the mapresponse is a "costly" afair which means that while
a node was producing one, it might start blocking and creating full
queues from the poller and all the way up to where updates where sent.

This could cause updates to time out and being dropped as a bad node
going away or spending too time processing would cause all the other
nodes to not get any updates.

In addition, it contributed to "uncontrolled parallel processing" by
potentially doing too many expensive operations at the same time:

Each node stream is essentially a channel, meaning that if you have 30
nodes, we will try to process 30 map requests at the same time. If you
have 8 cpu cores, that will saturate all the cores immediately and cause
a lot of wasted switching between the processing.

Now, all the maps are processed by workers in the mapper, and the number
of workers are controlable. These would now be recommended to be a bit
less than number of CPU cores, allowing us to process them as fast as we
can, and then send them to the poll.

When the poll recieved the map, it is only responsible for taking it and
sending it to the node.

This might not directly improve the performance of Headscale, but it will
likely make the performance a lot more consistent. And I would argue the
design is a lot easier to reason about.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
33e9e7a71f CLAUDE: split into agents
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
ccd79ed8d4 mcp: add some standard mcp server
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
f6c4b338fd .github/workflows: add generate check
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
306d8e1bd4 integration: validate expected online status in ping
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
4927e9d590 fix: improve mapresponses and profiles extraction in hi tool
- Fix directory hierarchy flattening by using full paths instead of filepath.Base()
- Remove redundant container hostname prefixes from directory names
- Strip top-level directory from tar extraction to avoid nested structure
- Ensure parent directories exist before creating files
- Results in clean structure: control_logs/mapresponses/1-ts-client/file.json
2025-09-09 09:40:00 +02:00
Kristoffer Dalby
8e25f7f9dd bunch of qol (#2748) 2025-08-27 17:09:13 +02:00
github-actions[bot]
1a7a2f4196 flake.lock: Update (#2699) 2025-08-24 12:07:32 +00:00
Dylan Blanqué
860a8a597f Update tools.md
Share/Contribute Headscale Zabbix Monitoring scripts and templates.

Thank you for the awesome application to everyone involved in Headscale's development!
2025-08-24 06:05:21 +02:00
cuiweixie
a2a6d20218 Refactor to use reflect.TypeFor 2025-08-23 20:43:49 +02:00
Andrey Bobelev
d29feaef79 chore(derp): allow nil regions in DERPMaps
Previously, nil regions were not properly handled. This change allows users to disable regions in DERPMaps.

Particularly useful to disable some official regions.
2025-08-23 06:54:14 +02:00
Andrey Bobelev
630bfd265a chore(derp): prioritize loading DERP maps from URLs
This allows users to override default entries provided via URL
2025-08-23 06:54:14 +02:00
Florian Preinstorfer
e949859d33 Add DERP docs 2025-08-22 12:09:31 +02:00
Florian Preinstorfer
4d61da30d0 Use an IPv4 address range suitable for documentation 2025-08-22 12:09:31 +02:00
Kristoffer Dalby
b87567628a derp: increase update frequency and harden on failures (#2741) 2025-08-22 10:40:38 +02:00
dotlambda
51c6367bb1 Correctly document the default for dns.override_local_dns 2025-08-19 15:02:49 +02:00
Florian Preinstorfer
be337c6a33 Enable derp.server.verify_clients by default
This setting is already enabled in example-config.yaml but would default
to false if no key is set.
2025-08-19 11:30:44 +02:00
Shourya Gautam
086fcad7d9 Fix Internal server error on /verify (#2735)
* converted the returned error to an httpError
2025-08-18 14:39:42 +00:00
afranco
3e3c72ea6f docs(acls): Add example for allow/deny all acl policy 2025-08-18 16:13:14 +02:00
afranco
43f90d205e fix: allow all traffic if acls field is omited from the policy 2025-08-18 16:13:14 +02:00
Florian Preinstorfer
7b8b796a71 docs: connect Android using a preauthkey
Fixes: #2616
2025-08-18 16:06:17 +02:00
nblock
fa619ea9f3 Fix CHANGELOG for autogroup:member and autogroup:tagged (#2733) 2025-08-18 08:59:03 +02:00
Florian Preinstorfer
30a1f7e68e Log registrationID to simplify interactive node registration
Some clients such as Android make it hard to transfer the registrationID
to the server, its easier to get it from the server logs.
2025-08-15 17:11:38 +02:00
Florian Preinstorfer
30cec3aa2b Document ports in use
Ref: #1767
2025-08-14 09:24:09 +02:00
Fredrik Ekre
5d8a2c25ea OIDC: Query userinfo endpoint before verifying user
This patch includes some changes to the OIDC integration in particular:
 - Make sure that userinfo claims are queried *before* comparing the
   user with the configured allowed groups, email and email domain.
 - Update user with group claim from the userinfo endpoint which is
   required for allowed groups to work correctly. This is essentially a
   continuation of #2545.
 - Let userinfo claims take precedence over id token claims.

With these changes I have verified that Headscale works as expected
together with Authelia without the documented escape hatch [0], i.e.
everything works even if the id token only contain the iss and sub
claims.

[0]: https://www.authelia.com/integration/openid-connect/headscale/#configuration-escape-hatch
2025-08-11 17:51:16 +02:00
Jeff Emershaw
b4f7782fd8 support force flag for nodes backfillips 2025-08-10 13:31:24 +02:00
eyjhb
d77874373d feat: add robots.txt 2025-08-10 10:57:45 +02:00
Kristoffer Dalby
a058bf3cd3 mapper: produce map before poll (#2628) 2025-07-28 11:15:53 +02:00
Luke Watts
b2a18830ed docs: fix typos 2025-07-28 10:28:49 +02:00
Kristoffer Dalby
9779adc0b7 integration: run headscale with delve and debug symbols (#2689) 2025-07-24 17:44:09 +02:00
nblock
e7fe645be5 Fix invocation of golangci-lint (#2703) 2025-07-24 08:41:20 +02:00
Florian Preinstorfer
bcd80ee773 Add debugging and troubleshooting guide 2025-07-22 14:56:45 +02:00
Florian Preinstorfer
c04e17d82e Document valid log levels
Also change the order as the level seems more important than the format.
2025-07-22 14:56:45 +02:00
Florian Preinstorfer
98fc0563ac Bump version in docs 2025-07-22 14:56:45 +02:00
Kian-Meng Ang
3123d5286b Fix typos
Found via `codespell -L shs,hastable,userr`
2025-07-21 12:06:07 +02:00
Kristoffer Dalby
7fce5065c4 all: remove 32 bit support (#2692) 2025-07-16 13:32:59 +02:00
Florian Preinstorfer
a98d9bd05f The preauthkeys commands expect a user id instead of a username 2025-07-16 09:53:05 +02:00
Florian Preinstorfer
46c59a3fff Fix command in bug report template 2025-07-15 21:12:32 +02:00
Kristoffer Dalby
044193bf34 integration: Use Eventually around external calls (#2685) 2025-07-13 17:37:11 +02:00
Mohammad Javad Naderi
a8f2eebf66 Fix config param name in TLS doc 2025-07-13 12:56:25 +02:00
github-actions[bot]
6220e64978 flake.lock: Update (#2669) 2025-07-13 06:36:04 +00:00
Kristoffer Dalby
c6d7b512bd integration: replace time.Sleep with assert.EventuallyWithT (#2680) 2025-07-10 23:38:55 +02:00
Kristoffer Dalby
b904276f2b poll: use nodeview everywhere
There was a bug in HA subnet router handover where we used stale node data
from the longpoll session that we handed to Connect. This meant that we got
some odd behaviour where routes would not be deactivated correctly.

This commit changes to the nodeview is used through out, and we load the
current node to be updated in the write path and then handle it all there
to be consistent.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-08 21:05:15 +02:00
Kristoffer Dalby
4a8d2d9ed3 .github/workflows: reduce integration retry to 3
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-08 07:28:35 +01:00
Kristoffer Dalby
22e6094a90 golangci: disable varnamelen
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 21:28:59 +01:00
Kristoffer Dalby
73023c2ec3 all: use immutable node view in read path
This commit changes most of our (*)types.Node to
types.NodeView, which is a readonly version of the
underlying node ensuring that there is no mutations
happening in the read path.

Based on the migration, there didnt seem to be any, but the
idea here is to prevent it in the future and simplify other
new implementations.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 21:28:59 +01:00
Kristoffer Dalby
5ba7120418 .github/workflows: prettier
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 15:48:38 +01:00
Kristoffer Dalby
d311d2e206 flake: dont override gopls
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 15:48:38 +01:00
Kristoffer Dalby
05996a5048 .github/workflow: only run a few selected postgres tests
We are already being punished by github actions, there seem to be
little value in running all the tests for both databases, so only
run a few key tests to check postgres isnt broken.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 15:48:38 +01:00
Kristoffer Dalby
4668e5dd96 changelog: add entry for db
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 15:48:38 +01:00
Kristoffer Dalby
c6736dd6d6 db: add sqlite "source of truth" schema
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-07-07 15:48:38 +01:00
Stavros Kois
855c48aec2 remove unneeded check (#2658) 2025-07-04 15:47:01 +00:00
Stavros Kois
ded049b905 don't crash if config file is missing (#2656) 2025-07-04 12:58:17 +00:00
github-actions[bot]
3bad5d5590 flake.lock: Update (#2585)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-07-04 12:00:59 +00:00
Florian Preinstorfer
d461db3abd Refactor OpenID Connect documentation
Restructure and rewrite the OpenID Connect documentation. Start from the
most minimal configuration and describe what needs to be done both in
Headscale and the identity provider. Describe additional features such
as PKCE and authorization filters in a generic manner with examples.

Document how Headscale populates its user profile and how it relates to
OIDC claims. This is a revised version from the table in the changelog.
Document the validation rules for fields and extend known limitations.

Sort the provider specific section alphabetically and add a section for
Authelia, Authentik, Kanidm and Keycloak. Also simplify and rename Azure
to Entra ID.

Update the description for the oidc section in the example
configuration. Give a short explanation of each configuration setting.

All documentend features were tested with Headscale 0.26 (using a fresh
database each time) using the following identity providers:

* Authelia
* Authentik
* Kanidm
* Keycloak

Fixes: #2295
2025-07-04 10:51:37 +02:00
eyJhb
efc6974017 fix typo in parseCapabilityVersion, and removed unused error (#2644) (#2644) 2025-07-04 09:40:29 +02:00
Fredrik Ekre
3f72ee9de8 Clarify SIGHUP log message (#2661) 2025-07-04 09:30:51 +02:00
nblock
e73b2a9fb9 Ensure that a username starts with a letter (#2635) 2025-06-24 14:45:44 +02:00
Kristoffer Dalby
081af2674b ci: fix golangci-lint flag for v2 compatibility (#2654) 2025-06-24 08:14:50 +02:00
Kristoffer Dalby
1553f0ab53 state: introduce state
this commit moves all of the read and write logic, and all different parts
of headscale that manages some sort of persistent and in memory state into
a separate package.

The goal of this is to clearly define the boundry between parts of the app
which accesses and modifies data, and where it happens. Previously, different
state (routes, policy, db and so on) was used directly, and sometime passed to
functions as pointers.

Now all access has to go through state. In the initial implementation,
most of the same functions exists and have just been moved. In the future
centralising this will allow us to optimise bottle necks with the database
(in memory state) and make the different parts talking to eachother do so
in the same way across headscale components.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-06-24 07:58:54 +02:00
Kristoffer Dalby
a975b6a8b1 hscontrol: remove go-grpc-middleware v1 dependency (#2653)
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-23 16:57:20 +02:00
Kristoffer Dalby
afc11e1f0c cmd/hi: fixes and qol (#2649) 2025-06-23 13:43:14 +02:00
Kristoffer Dalby
ea7376f522 cmd/hi: add integration test runner CLI tool (#2648)
* cmd/hi: add integration test runner CLI tool

Add a new CLI tool 'hi' for running headscale integration tests
with Docker automation. The tool replaces manual Docker command
composition with an automated solution.

Features:
- Run integration tests in golang:1.24 containers
- Docker context detection (supports colima and other contexts)
- Test isolation with unique run IDs and isolated control_logs
- Automatic Docker image pulling and container management
- Comprehensive cleanup operations for containers, networks, images
- Docker volume caching for Go modules
- Verbose logging and detailed test artifact reporting
- Support for PostgreSQL/SQLite selection and various test flags

Usage: go run ./cmd/hi run TestPingAllByIP --verbose

The tool uses creachadair/command and flax for CLI parsing and
provides cleanup subcommands for Docker resource management.

Updates flake.nix vendorHash for new Go dependencies.

* ci: update integration tests to use hi CLI tool

Replace manual Docker command composition in GitHub Actions
workflow with the new hi CLI tool for running integration tests.

Changes:
- Replace complex docker run command with simple 'go run ./cmd/hi run'
- Remove manual environment variable setup (handled by hi tool)
- Update artifact paths for new timestamped log directory structure
- Simplify command from 15+ lines to 3 lines
- Maintain all existing functionality (postgres/sqlite, timeout, test patterns)

The hi tool automatically handles Docker context detection, container
management, volume mounting, and environment variable setup that was
previously done manually in the workflow.

* makefile: remove test integration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-06-18 11:22:15 +02:00
seiuneko
d325211617 feat: add verify client config for embedded DERP (#2260)
* feat: add verify client config for embedded DERP

* refactor: embedded DERP no longer verify clients via HTTP

- register the `headscale://` protocol in `http.DefaultTransport` to intercept network requests
- update configuration to use a single boolean option `verify_clients`

* refactor: use `http.HandlerFunc` for type definition

* refactor: some renaming and restructuring

* chore: some renaming and fix lint

* test: fix TestDERPVerifyEndpoint

- `tailscale debug derp` use random node private key

* test: add verify clients integration test for embedded DERP server

* fix: apply code review suggestions

* chore: merge upstream changes

* fix: apply code review suggestions

---------

Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2025-06-18 09:24:53 +02:00
Mustafa Enes Batur
bad783321e Fix /machine/map endpoint vulnerability (#2642)
* Improve map auth logic

* Bugfix

* Add comment, improve error message

* noise: make func, get by node

this commit splits the additional validation into a
separate function so it can be reused if we add more
endpoints in the future.

It swaps the check, so we still look up by NodeKey, but before
accepting the connection, we validate the known machinekey from
the db against the noise connection.

The reason for this is that when a node logs in or out, the node key
is replaced and it will no longer be possible to look it up, breaking
reauthentication.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* noise: add comment to remind future use of getAndVal

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog: add entry

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-06-06 12:14:11 +02:00
Hannes
b8044c29dd Replace magic-nix-cache-action (#2575) 2025-05-26 23:05:08 +02:00
Shubham Hibare
df69840f92 feat(tools): Add Go client implementation 2025-05-23 17:52:31 +02:00
lucarickli
76ca7a2b50 Add headscale-console 2025-05-22 06:52:02 +02:00
Florian Preinstorfer
cd704570be Drop support for Ubuntu 20.04
Its old and our service file logs warning about unsupported options.
2025-05-21 15:40:32 +02:00
Florian Preinstorfer
43c9c50af4 Drop syslog.target and systemd-managed /var/run
The systemd target "syslog.target" and not required because syslog is
socket activated.

The directory /var/run is usually a symlink to /run and its created by
systemd via the RuntimeDirectory=headscale option. System creates and
handles permissions, no need to manually mark it as a read-write path.
2025-05-21 15:40:32 +02:00
Florian Preinstorfer
4a941a2cb4 Refactor Debian/Ubuntu package
Move files for packaging outside the docs directory into its own
packaging directory. Replace the existing postinstall and postremove
scripts with Debian maintainerscripts to behave more like a typical
Debian package:

* Start and enable the headscale systemd service by default
* Does not print informational messages
* No longer stop and disable the service on updates

This package also performs migrations for all changes done in previous
package versions on upgrade:

* Set login shell to /usr/sbin/nologin
* Set home directory to /var/lib/headscale
* Migrate to system UID/GID

The package is lintian-clean with a few exceptions that are documented
as excludes and it passes puipars (both tested on Debian 12).

The following scenarious were tested on Ubuntu 22.04, Ubuntu 24.04,
Debian 11, Debian 12:

* Install
* Install same version again
* Install -> Remove -> Install
* Install -> Purge -> Install
* Purge
* Update from 0.22.0
* Update from 0.26.0

See: #2278
See: #2133
Fixes: #2311
2025-05-21 15:40:32 +02:00
Greg Dietsche
d2879b2b36 web: change node registration parameter order (#2607)
This change makes editing the generated command easier.
For example, after pasting into a terminal, the cursor position will be
near the username portion which requires editing.
2025-05-21 11:18:53 +02:00
Kristoffer Dalby
a52f1df180 policy: remove v1 code (#2600)
* policy: remove v1 code

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* db: update test with v1 removal

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: start moving to v2 policy

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: add ssh unmarshal tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog: add entry

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: remove v1 comment

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: remove comment out case

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* cleanup skipv1

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: remove v1 prefix workaround

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: add all node ips if prefix/host is ts ip

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-20 13:57:26 +02:00
azrikahar
1605e2a7a9 fix typo in TailSQL's log 2025-05-18 07:15:41 +02:00
Vitalij Dovhanyc
6750414db1 feat: add autogroup:member, autogroup:tagged (#2572) 2025-05-17 11:07:34 +02:00
Florian Preinstorfer
b50e10a1be Document breaking change for dns.override_local_dns
See: #2438
2025-05-16 19:33:00 +02:00
Florian Preinstorfer
c15aa541bb Document HEADSCALE_CONFIG 2025-05-16 19:33:00 +02:00
Florian Preinstorfer
49b3468845 Do not ignore config-example.yml
Various tools (e.g ripgrep) skip files ignored by Git. Do not ignore
config-example.yml to include it in searches.
2025-05-16 19:33:00 +02:00
Kristoffer Dalby
bd6ed80936 policy/v2: error on missing or zero port (#2606)
* policy/v2: error on missing or zero port

Fixes #2605

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog: add entry

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-16 17:30:47 +02:00
Kristoffer Dalby
30525cee0e goreleaser: always do draft (#2595)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-16 10:23:22 +02:00
Kristoffer Dalby
2dc2f3b3f0 users: harden, test, and add cleaner of identifier (#2593)
* users: harden, test, and add cleaner of identifier

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* db: migrate badly joined provider identifiers

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-14 16:45:14 +02:00
Kristoffer Dalby
d7a503a34e changelog: entry for 0.26 (#2594)
* changelog: entry for 0.26

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* docs: bump version

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-14 16:32:56 +02:00
jasonrepos
62b489dc68 fix: change FormatUint base from 64 to 10 in preauthkeys list command (#2588) 2025-05-13 18:40:17 +00:00
nblock
8c7e650616 Remove map_legacy_users from example configuration (#2590) 2025-05-13 21:38:52 +03:00
Kristoffer Dalby
43943aeee9 bring back last_seen in database (#2579)
* db: add back last_seen to the database

Fixes #2574

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: ensure last_seen is set

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-10 09:49:08 +02:00
nblock
d81b0053e5 Simplify policy migration (#2582)
These steps are easier to accomplish and require only Headscale 0.26.
They also work when a user has already upgraded the database.

See: #2567
2025-05-10 08:04:42 +02:00
nblock
dd0cbdf40c Add migration steps when policy is stored in the database (#2581)
Fixes: #2567
2025-05-09 23:30:39 +02:00
Kristoffer Dalby
37dc0dad35 policy/v2: separate exit node and 0.0.0.0/0 routes (#2578)
* policy: add tests for route auto approval

Reproduce #2568

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: separate exit node and 0.0.0.0/0 routes

Fixes #2568

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-09 23:20:04 +02:00
Kristoffer Dalby
377b854dd8 cli: policy check, dont require config or log (#2580)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-09 23:19:47 +02:00
Kristoffer Dalby
56db4ed0f1 policy/v2: validate that no undefined group or tag is used (#2576)
* policy/v2: allow Username as ssh source

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: validate that no undefined group or tag is used

Fixes #2570

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: fixup tests which violated tag constraing

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-09 11:51:30 +02:00
nblock
833e0f66f1 Remove subnet router visibility workaround from docs (#2569)
Previous Headscale versions required a dedicated rule to make a subnet
router visible to clients. This workaround is no longer required.
2025-05-05 15:24:59 +02:00
Kristoffer Dalby
1dddd3e93b app: throw away not found body (#2566)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-04 22:06:44 +02:00
nblock
9a86ffc102 Misc doc fixes (#2562)
* Link to stable and development docs in the README

* Add Tailscale SSH and autogroup:nonroot to features page

* Use @ when referencing users in policy

* Remove unmaintained headscale-webui

The project seems to be unmaintained (last commit: 2023-05-08) and it
only supports Headscale 0.22 or earlier.

* Use full image URL in container docs

This makes it easy to switch the container runtime from docker <->
podman.

* Remove version from docker-compose.yml example

This is now deprecated and yields a warning.
2025-05-04 21:55:08 +02:00
Kristoffer Dalby
45e38cb080 policy: reduce routes sent to peers based on packetfilter (#2561)
* notifier: use convenience funcs

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: reduce routes based on policy

Fixes #2365

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* hsic: more helper methods

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: more test cases

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: add route with filter acl integration test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: correct route reduce test, now failing

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* mapper: compare peer routes against node

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* hs: more output to debug strings

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* types/node: slice.ContainsFunc

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: more reduce route test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog: add entry for route filter

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-04 21:52:47 +02:00
Kristoffer Dalby
b9868f6516 Make more granular SSH tests for both Policies (#2555)
* policy/v1: dont consider empty if ssh has rules

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: replace time.Duration with model.Duration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: add autogroup and ssh validation

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: replace time.Duration with model.Duration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: replace old ssh tests with more granular test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: skip v1 tests expected to fail (missing error handling)

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: skip v1 group tests, old bugs wont be fixed

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: user valid policy for ssh

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Changelog, add ssh section

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* nix update

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-04 12:05:41 +00:00
Kristoffer Dalby
f317a85ab4 go.mod: update rest of deps (#2559)
* flake: update go hash

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* go.mod: update more deps

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-03 16:36:08 +02:00
Alexey Tarasov
53d9c95160 Update container.md 2025-05-03 12:51:46 +02:00
Jacob Yundt
03a91693ac feat: Create headscale user and group as system user/groups (#2322)
When creating the headscale user and group, create both as system groups
rather than creating them as 'user' groups.

FIXES #2278
2025-05-03 09:13:54 +00:00
nblock
cb7c0173ec Fix deprecation warnings (#2558)
See https://goreleaser.com/deprecations/#archivesformat and
https://goreleaser.com/deprecations/#nfpmsbuilds
2025-05-03 10:18:49 +02:00
nblock
18d21d3585 Add documentation for routes (#2496)
* Add documentation for routes

* Rename exit-node to routes and add redirects
* Add a new section on subnet routers
* Extend the existing exit-node documentation
* Describe auto approvers for subnet routers and exit nodes
* Provide ACL examples for subnet routers and exit nodes
* Describe HA and its current limitations
* Add a troubleshooting section with IP forwarding

* Update features page for 0.26

Add auto approvers and link to our documentation if available.

* Prefer the console lexer when commandline and output mixed
2025-05-03 10:16:45 +02:00
Kristoffer Dalby
e7d2d79134 update capmap and deps for release (#2522)
* generate new capver map

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* replace old sort func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* nix: flake update

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* capgen: update

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* capgen: update

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* go.mod: update tailscale

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* go.mod: update other deps

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-02 22:12:29 +02:00
Kristoffer Dalby
d810597414 policy/matcher: fix bug using contains instead of overlap (#2556)
* policy/matcher: slices.ContainsFunc

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/matcher: slices.ContainsFunc, correct contains vs overlap

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: add tests to validate fix for 2181

Fixes #2181

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-02 22:08:56 +02:00
Kristoffer Dalby
93afb03f67 cmd: add policy check command (#2553) 2025-05-02 13:58:30 +03:00
Kristoffer Dalby
e4d10ad964 policy/v2: validate autogroup:interet only in dst (#2552) 2025-05-02 13:58:12 +03:00
Janne Johansson
7dc86366b4 Update source.md
If we assume someone doesn't already have the required go package, they might also not have the required git package installed either, so pkg_add both of them.
2025-05-02 10:43:56 +02:00
Kristoffer Dalby
c923f461ab error on undefined host in policy (#2490)
* add testcases

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: add validate to do post marshal validation

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-01 14:30:52 +02:00
Kristoffer Dalby
a4a203b9a3 cli/nodes: filter nodes without any routes (#2551) 2025-05-01 13:27:54 +03:00
aergus-tng
4651d06fa8 Make matchers part of the Policy interface (#2514)
* Make matchers part of the Policy interface

* Prevent race condition between rules and matchers

* Test also matchers in tests for Policy.Filter

* Compute `filterChanged` in v2 policy correctly

* Fix nil vs. empty list issue in v2 policy test

* policy/v2: always clear ssh map

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Aras Ergus <aras.ergus@tngtech.com>
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-01 07:06:30 +02:00
Kristoffer Dalby
eb1ecefd9e auth: ensure that routes are autoapproved when the node is stored (#2550)
* integration: ensure route is set before node joins, reproduce

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* auth: ensure that routes are autoapproved when the node is stored

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-05-01 07:05:42 +02:00
Kristoffer Dalby
6b6509eeeb notify nodes after owner change (#2543)
* proto: user id as identifier for move node

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* gen: regenr

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* grpc: move, use userid, one tx, send update

Updates #2467

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: update move cli tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-30 18:33:38 +02:00
Kristoffer Dalby
cfe9bbf829 oidc: try to get username from userinfo (#2545)
* oidc: try to get username from userinfo

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-30 11:54:13 +02:00
Kristoffer Dalby
8f9fbf16f1 types/authkey: include user object in response (#2542)
* types/authkey: include user object, not string

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* make preauthkeys use id

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: wire up user id for auth keys

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-30 11:45:08 +02:00
Kristoffer Dalby
f1206328dc fix webauth + autoapprove routes (#2528)
* types/node: add helper funcs for node tags

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* types/node: add DebugString method for node

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: add String func to AutoApprover interface

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: simplify, use slices.Contains

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: debug, use nodes.DebugString

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v1: fix potential nil pointer in NodeCanApproveRoute

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v1: slices.Contains

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration/tsic: fix diff in login commands

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: fix webauth running with wrong scenario

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: move common oidc opts to func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: require node count, more verbose

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* auth: remove uneffective route approve

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* .github/workflows: fmt

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration/tsic: add id func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: remove call that might be nil

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: test autoapprovers against web/authkey x group/tag/user

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: unique network id per scenario

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Revert "integration: move common oidc opts to func"

This reverts commit 7e9d165d4a900c304f1083b665f1a24a26e06e55.

* remove cmd

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: clean docker images between runs in ci

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: run autoapprove test against differnt policy modes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration/tsic: append, not overrwrite extra login args

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* .github/workflows: remove polv2

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-30 07:54:04 +02:00
Kristoffer Dalby
57861507ab integration: remove failing resolvconf tests (#2549)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-30 07:52:23 +02:00
Kristoffer Dalby
2b38f7bef7 policy/v2: make default (#2546)
* policy/v2: make default

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* integration: do not run v1 tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy/v2: fix potential nil pointers

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* mapper: fix test failures in v2

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-29 16:27:41 +02:00
github-actions[bot]
9a4d0e1a99 flake.lock: Update (#2518)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/18dd725c29603f582cf1900e0d25f9f1063dbf11?narHash=sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38%3D' (2025-04-13)
  → 'github:NixOS/nixpkgs/ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c?narHash=sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs%3D' (2025-04-17)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-24 11:02:09 +00:00
Kristoffer Dalby
30539b2e26 config: disallow same server url and base_domain (#2544)
* config: disallow same server url and base_domain

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-23 16:24:38 +02:00
Kristoffer Dalby
098ab0357c add casbin user test (#2474)
* add casbin user test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Delete double slash

* types/users: use join url on iss that are ursl

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2025-04-23 13:21:51 +02:00
Relihan Myburgh
56d085bd08 Fix panic on fast reconnection of node (#2536)
* Fix panic on fast reconnection of node

* Use parameter captured in closure as per review request
2025-04-23 11:52:24 +02:00
Relihan Myburgh
92e587a82c Fix goroutine leak in EphemeralGC on node cancel (#2538)
* Fix goroutine leak in EphemeralGC on node cancel

* Deal with timer firing whilst the GC is shutting down. Fix typos.
2025-04-23 11:44:24 +02:00
Pamplemousse
f3a1e693f2 Mention "Network flow logs" as a missing feature 2025-04-22 11:28:41 +02:00
Kristoffer Dalby
f783555469 integration: clean up unreferenced hs- networks (#2534) 2025-04-18 12:06:28 +02:00
Kristoffer Dalby
710d75367e policy/v2: fix host validation, consistent pattern (#2533) 2025-04-18 11:35:04 +02:00
Kristoffer Dalby
c30e3a4762 flake: add golang-lint lsp (#2507)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-18 11:15:02 +02:00
alteriks
3287aa8bba Update oidc.md
Authelia docs
2025-04-18 10:16:08 +02:00
Kristoffer Dalby
8e7e52cf3a some clarifications for tags (#2531)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-04-18 09:33:02 +02:00
nblock
1e0516b99d Restore support for "Override local DNS" (#2438)
Tailscale allows to override the local DNS settings of a node via
"Override local DNS" [1]. Restore this flag with the same config setting
name `dns.override_local_dns` but disable it by default to align it with
Tailscale's default behaviour.

Tested with Tailscale 1.80.2 and systemd-resolved on Debian 12.

With `dns.override_local_dns: false`:

```
Link 12 (tailscale0)
Current Scopes: DNS
     Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 100.100.100.100
    DNS Domain: tn.example.com ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa [snip]
```

With `dns.override_local_dns: true`:

```
Link 12 (tailscale0)
Current Scopes: DNS
     Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
   DNS Servers: 100.100.100.100
    DNS Domain: tn.example.com ~.
```

[1] https://tailscale.com/kb/1054/dns#override-local-dns

Fixes: #2256
2025-04-17 17:16:59 +02:00
Kristoffer Dalby
0fbe392499 more wait, more retry (#2532) 2025-04-16 12:42:26 +02:00
Nick
109989005d ensure final dot on node name (#2503)
* ensure final dot on node name

This ensures that nodes which have a base domain set, will have a dot appended to their FQDN.

Resolves: https://github.com/juanfont/headscale/issues/2501

* improve OIDC TTL expire test

Waiting a bit more than the TTL of the OIDC token seems to remove some flakiness of this test. This furthermore makes use of a go func safe buffer which should avoid race conditions.
2025-04-11 12:39:08 +02:00
Enkelmann
0d3134720b Only read relevant nodes from database in PeerChangedResponse (#2509)
* Only read relevant nodes from database in PeerChangedResponse

* Rework to ensure transactional consistency in PeerChangedResponse again

* An empty nodeIDs list should return an empty nodes list

* Add test to ListNodesSubset

* Link PR in CHANGELOG.md

* combine ListNodes and ListNodesSubset into one function

* query for all nodes in ListNodes if no parameter is given

* also add optional filtering for relevant nodes to ListPeers
2025-04-08 14:56:44 +02:00
Christoph
d2a6356d89 Add unraid-headscale-admin web UI to docs (#2515)
* Add unraid-headscale-admin link
2025-04-02 20:54:32 +02:00
Kristoffer Dalby
5a18e91317 fix auto approver on register and new policy (#2506)
* fix issue auto approve route on register bug

This commit fixes an issue where routes where not approved
on a node during registration. This cause the auto approval
to require the node to readvertise the routes.

Fixes #2497
Fixes #2485

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* hsic: only set db policy if exist

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* policy: calculate changed based on policy and filter

v1 is a bit simpler than v2, it does not pre calculate the auto approver map
and we cannot tell if it is changed.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-31 15:55:07 +02:00
Kristoffer Dalby
e3521be705 allow users to be defined with @ in v1 (#2495)
* allow users to be defined with @ in v1

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove integration test rewrite hack

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove test rewrite hack

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add @ to integration tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* a bit to agressive removeals

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix last test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-30 11:19:05 +00:00
github-actions[bot]
f52f15ff08 flake.lock: Update (#2510) 2025-03-30 06:18:37 +00:00
Kristoffer Dalby
cbc99010f0 populate serving from primary routes (#2489)
* populate serving from primary routes

Depends on #2464
Fixes #2480

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* also exit

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix route update outside of connection

there was a bug where routes would not be updated if
they changed while a node was connected and it was not part of an
autoapprove.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update expected test output, cli only shows service node

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-28 13:22:15 +01:00
Benjamin Staffin
b5953d689c OIDC: Fetch UserInfo to get EmailVerified if necessary (#2493) 2025-03-27 10:39:29 +01:00
github-actions[bot]
badbb68217 flake.lock: Update (#2468) 2025-03-23 08:34:03 +00:00
Kristoffer Dalby
603f3ad490 Multi network integration tests (#2464) 2025-03-21 11:49:32 +01:00
Florian Preinstorfer
707438f25e Mention that private keys generated if needed 2025-03-19 06:12:14 +01:00
Florian Preinstorfer
24ad235917 Explicitly handle /headscale/{config,lib,run} in container docs
Fixes: #2304
2025-03-19 06:12:14 +01:00
Oleksii Samoliuk
00d5d647ed add third-party tool headscale-pf 2025-03-17 13:20:24 +01:00
Florian Preinstorfer
cbce8f6011 Remove coderabbit
It is no longer available for free and didn't do anything in the past
months.
2025-03-16 14:02:33 +01:00
Florian Preinstorfer
05202099f7 Set content-type to JSON for some debug endpoints
Some endpoints in /debug send JSON data as string. Set the Content-Type
header to "application/json" which renders nicely in Firefox.

Mention the /debug route in the example configuration.
2025-03-16 14:02:12 +01:00
Kristoffer Dalby
800456018a update bug template with debug (#2481)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-16 13:43:13 +01:00
Florian Preinstorfer
586a20fbff Add a FAQ entry about two nodes seeing each other
See: #2431
See: #2174
2025-03-14 18:48:44 +01:00
Kristoffer Dalby
818046f240 add faq section on scaling/performance (#2476)
* add faq section on scaling/performance

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Apply suggestions from code review

Co-authored-by: nblock <nblock@users.noreply.github.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: nblock <nblock@users.noreply.github.com>
2025-03-14 17:09:30 +00:00
nblock
fe06a00d45 Container images are also available on GHCR (#2470)
Fixes: #2456
2025-03-11 06:54:05 +01:00
Kristoffer Dalby
0b5c29e875 remove policy handling for old capver (#2429)
* remove policy handling for old capver

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-10 18:19:25 +00:00
Florian Preinstorfer
0a243b4162 Remove leftover printf 2025-03-10 16:52:29 +01:00
Florian Preinstorfer
29ba29478b Add usage example to routes flag 2025-03-10 16:52:29 +01:00
Florian Preinstorfer
e52f1e87ce Drop routes table 2025-03-10 16:52:29 +01:00
Kristoffer Dalby
87326f5c4f Experimental implementation of Policy v2 (#2214)
* utility iterator for ipset

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* split policy -> policy and v1

This commit split out the common policy logic and policy implementation
into separate packages.

policy contains functions that are independent of the policy implementation,
this typically means logic that works on tailcfg types and generic formats.
In addition, it defines the PolicyManager interface which the v1 implements.

v1 is a subpackage which implements the PolicyManager using the "original"
policy implementation.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use polivyv1 definitions in integration tests

These can be marshalled back into JSON, which the
new format might not be able to.

Also, just dont change it all to JSON strings for now.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* formatter: breaks lines

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove compareprefix, use tsaddr version

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove getacl test, add back autoapprover

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use policy manager tag handling

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* rename display helper for user

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* introduce policy v2 package

policy v2 is built from the ground up to be stricter
and follow the same pattern for all types of resolvers.

TODO introduce
aliass
resolver

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* wire up policyv2 in integration testing

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* split policy v2 tests into seperate workflow to work around github limit

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add policy manager output to /debug

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-03-10 16:20:29 +01:00
github-actions[bot]
b6fbd37539 flake.lock: Update (#2454)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/dad564433178067be1fbdfcce23b546254b6d641?narHash=sha256-vn285HxnnlHLWnv59Og7muqECNMS33mWLM14soFIv2g%3D' (2025-02-20)
  → 'github:NixOS/nixpkgs/199169a2135e6b864a888e89a2ace345703c025d?narHash=sha256-igS2Z4tVw5W/x3lCZeeadt0vcU9fxtetZ/RyrqsCRQ0%3D' (2025-03-01)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-02 19:59:44 +00:00
Kristoffer Dalby
7891378f57 Redo route code (#2422)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-26 16:22:55 +01:00
Kristoffer Dalby
16868190c8 fix double login URL with OIDC (#2445)
* factor out login url parser

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* move to not trigger test gen checker

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* return regresp or err after waiting for registration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-25 18:16:07 +01:00
Kristoffer Dalby
da2ca054b1 fix routes not being saved when new nodes registers (#2444)
* add test to validate exitnode propagation

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* save routes on register

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* no nil

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add missing integration tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-23 23:10:25 +01:00
Kristoffer Dalby
bcff0eaae7 handle register auth errors (#2435)
* handle register auth errors

This commit handles register auth errors as the
Tailscale clients expect. It returns the error as
part of a tailcfg.RegisterResponse and not as a
http error.

In addition it fixes a nil pointer panic triggered
by not handling the errors as part of this chain.

Fixes #2434

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-23 17:02:46 +01:00
github-actions[bot]
b220fb7d51 flake.lock: Update (#2440) 2025-02-23 01:23:16 +00:00
github-actions[bot]
2cce3a99eb flake.lock: Update (#2430) 2025-02-16 20:40:20 +00:00
Kristoffer Dalby
bbe57f6cd4 use tailscale version in all unsupported errs (#2426) 2025-02-14 13:43:33 +01:00
Kristoffer Dalby
604f7f6282 update to go 1.24 (#2427) 2025-02-14 10:56:03 +01:00
badsmoke
c61fbe9c5f activate json logs (#2424)
Co-authored-by: jan.sulimma <jan.sulimma@zigpos.com>
2025-02-12 15:31:24 +01:00
Kristoffer Dalby
b943cce868 set 0.25.0 changelog date (#2423)
* date in changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update docs version

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-11 16:25:53 +01:00
Kristoffer Dalby
6403c8d5d2 use tsweb debugger (#2420)
This PR switches the homegrown debug endpoint to using tsweb.Debugger, a neat toolkit with batteries included for pprof and friends, and making it easy to add additional debug info:

I've started out by adding a bunch of "introspect" endpoints
image

So users can see the acl, filter, config, derpmap and connected nodes as headscale sees them.
2025-02-11 11:18:59 +01:00
github-actions[bot]
b3fa16fbda flake.lock: Update (#2419) 2025-02-09 08:45:06 +00:00
Kristoffer Dalby
1f0110fe06 use helper function for constructing state updates (#2410)
This helps preventing messages being sent with the wrong update type
and payload combination, and it is shorter/neater.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 13:49:59 +01:00
Kristoffer Dalby
b92bd3d27e remove oidc migration (#2411)
* remove oidc migration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 13:49:45 +01:00
Kristoffer Dalby
3bf7d5a9c9 add git hash to binary, print on startup (#2415)
* add git hash to binary, print on startup

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 13:49:34 +01:00
Kristoffer Dalby
1d65865425 make version info in bug template more explicit (#2413)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 11:36:00 +01:00
Kristoffer Dalby
c53ff2ce00 update changelog (#2414)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 11:32:01 +01:00
Kristoffer Dalby
b4ac8cd9a3 hscontrol/db: add migration setting non existing pak on nodes to null (#2412)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-07 10:22:23 +01:00
Nbelles
22277d1fc7 Spell check 2025-02-05 17:29:30 +01:00
Kristoffer Dalby
9ae3570154 drop versions older than 1.62 (#2405) 2025-02-05 12:02:32 +01:00
Florian Preinstorfer
f12cb2e048 Headscale now updates the user profile 2025-02-05 11:05:03 +01:00
Florian Preinstorfer
8c09afe20c Headscale implements a single tailnet
Separate the term "tailnet" from user and be more explicit about
providing a single tailnet.

Also be more explicit about users. Refer to "headscale users" when
mentioning commandline invocations and use the term "local users" when
discussing unix accounts.

Fixes: #2335
2025-02-05 11:05:03 +01:00
Kristoffer Dalby
8b92c017ec add 1.80 to capver and update deps (#2394) 2025-02-05 07:17:51 +01:00
github-actions[bot]
9a7890d56b flake.lock: Update (#2402) 2025-02-02 07:58:12 +00:00
Kristoffer Dalby
45752db0f6 Return better web errors to the user (#2398)
* add dedicated http error to propagate to user

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* classify user errors in http handlers

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* move validation of pre auth key out of db

This move separates the logic a bit and allow us to
write specific errors for the caller, in this case the web
layer so we can present the user with the correct error
codes without bleeding web stuff into a generic validate.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-01 15:25:18 +01:00
Kristoffer Dalby
1c7f3bc440 no edit of oidc users, minimum hostname length (#2393)
* return an error when renaming users from OIDC
* set minimum hostname length of 2
2025-02-01 09:40:37 +00:00
Kristoffer Dalby
9bd143852f do not allow preauth keys to be deleted if assigned to node (#2396)
* do not allow preauth keys to be deleted if assigned to node

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-02-01 10:31:13 +01:00
Kristoffer Dalby
d57a55c024 Rewrite authentication flow (#2374) 2025-02-01 09:16:51 +00:00
Kristoffer Dalby
e172c29360 initial capver packet tracking version (#2391)
* initial capver packet tracking version

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Log the minimum version as client version, not only capver

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove old versions

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use capver for integration tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* patch through m and n key

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-30 21:49:09 +00:00
Kristoffer Dalby
cd3b8e68ff clean up handler methods, common logging (#2384)
* clean up handler methods, common logging

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* streamline http.Error calls

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-30 22:40:29 +01:00
nblock
f44b1d37c4 Remove routes without a node_id (#2386)
The routes table has a NOT NULL constraint on node_id.

Fixes: #2376
2025-01-30 14:57:06 +01:00
Kristoffer Dalby
7ba6ad3489 simplify findUserByToken in ACL, add missing testcases (#2388)
* update users doc on unique constraints

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* simplify finduser func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add initial tests for findUserFromToken

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-30 11:35:49 +01:00
Kristoffer Dalby
2c279e0a7b create and rename usernames validated by new func (#2381)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-27 22:58:10 +01:00
Kristoffer Dalby
4c8e847f47 use dedicated registration ID for auth flow (#2337) 2025-01-26 22:20:11 +01:00
github-actions[bot]
97e5d95399 flake.lock: Update (#2378) 2025-01-26 15:33:51 +00:00
Kristoffer Dalby
d1dbe4ece9 fix panic if derp update is 0 (#2368)
* fix panic if derp update is 0

Fixes #2362

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-23 16:16:12 +01:00
Kristoffer Dalby
9e3f945eda fix postgres migration issue with 0.24 (#2367)
* fix postgres migration issue with 0.24

Fixes #2351

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add postgres migration test for 2351

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-23 14:58:42 +01:00
Kristoffer Dalby
615ee5df75 make it harder to insert invalid routes (#2371)
* make it harder to insert invalid routes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* dont panic if node is not available for route

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-23 13:40:23 +01:00
Kristoffer Dalby
c1f42cdf4b relax user validation to allow emails, add tests from various oidc providers (#2364)
* relax user validation to allow emails, add tests from various oidc providers

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-22 18:10:15 +01:00
github-actions[bot]
aa76980b43 flake.lock: Update (#2353) 2025-01-19 09:59:29 +00:00
Kristoffer Dalby
5b986ed0a7 set oidc.map_legacy_users false (#2350) 2025-01-17 14:44:04 +00:00
Kristoffer Dalby
8076c94444 Release docs 0.24 (#2349)
* correct changelog date

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update docs version and copyright

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix deprecated goreleaser key and DRY

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-17 12:57:13 +00:00
Kristoffer Dalby
e88406e837 set changelog date (#2347) 2025-01-17 12:01:06 +01:00
Kristoffer Dalby
e4a3dcc3b8 use headscale server url as domain instead of base_domain (#2338) 2025-01-16 18:05:20 +01:00
Kristoffer Dalby
caad5c613d fix nil pointer deref (#2339) 2025-01-16 18:05:05 +01:00
Kristoffer Dalby
38aef77e54 allow @ and Log if OIDC username is not consider valid (#2340) 2025-01-16 18:04:54 +01:00
Dmitry Gordin
1ab7b315a2 Update apple.md for latest version of iOS (#2321)
The official iOS app now has a simpler login process for custom instances, directly within the app.
2025-01-13 12:09:53 +00:00
github-actions[bot]
610597bfb7 flake.lock: Update (#2342) 2025-01-12 18:54:59 +00:00
Stefan Majer
ede4f97a16 Fix typos 2025-01-09 10:38:25 +01:00
Kristoffer Dalby
fa641e38b8 Set CSRF cookies for OIDC (#2328)
* set state and nounce in oidc to prevent csrf

Fixes #2276

* try to fix new postgres issue

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2025-01-08 16:29:37 +01:00
github-actions[bot]
41bad2b9fd flake.lock: Update (#2324) 2025-01-05 07:35:18 +00:00
github-actions[bot]
f9bbfa5eab flake.lock: Update (#2320) 2024-12-29 11:41:52 +00:00
Rorical
b81420bef1 feat: Add PKCE Verifier for OIDC (#2314)
* feat: add PKCE verifier for OIDC

* Update CHANGELOG.md
2024-12-22 16:46:36 +00:00
github-actions[bot]
9313e5b058 flake.lock: Update (#2313) 2024-12-22 07:07:26 +00:00
Kristoffer Dalby
770f3dcb93 fix tags not resolving to username if email is present (#2309)
* ensure valid tags is populated on user gets too

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* ensure forced tags are added

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove unused envvar in test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* debug log auth/unauth tags in policy man

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* defer shutdown in tags test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add tag test with groups

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add email, display name, picture to create user

Updates #2166

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add ability to set display and email to cli

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add email to test users in integration

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix issue where tags were only assigned to email, not username

Fixes #2300
Fixes #2307

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* expand principles to correct login name

and if fix an issue where nodeip principles might not expand to all
relevant IPs instead of taking the first in a prefix.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix ssh unit test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update cli and oauth tests for users with email

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* index by test email

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix last test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-19 13:10:10 +01:00
Kristoffer Dalby
af4508b9dc bump deps (#2308)
* Bump go crypto

Closes #2281

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* upgrade tailscale

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* upgrade rest

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* nix: flake update

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-17 15:35:42 +01:00
Florian Preinstorfer
bbc93a90a2 Set title for code listings 2024-12-17 14:08:34 +01:00
Florian Preinstorfer
0acb2b5647 Misc doc updates 2024-12-17 14:08:34 +01:00
Florian Preinstorfer
3269cfdca0 Mention reload and SIGHUP when editing the ACL policy file
Fixes: #2284
2024-12-17 14:08:34 +01:00
Florian Preinstorfer
319ce67c87 Update DNS documentation for dns.extra_records_path
* Describe both ways to add extra DNS records
* Use "extra" instead of "custom" to align with the configuration file
* Include dns.extra_records_path in the configuration file
2024-12-17 14:08:34 +01:00
Florian Preinstorfer
47b405d6c6 Changelog: support client verify for DERP
and fix some links

Ref: #2304
2024-12-17 14:08:34 +01:00
Florian Preinstorfer
65304a0ce7 Remove sealos documentation
The referenced version is outdated (0.23.0-beta1) and seems
unmaintained.
2024-12-17 14:08:34 +01:00
Dongjun Na
e270169c13 Add -race Flag to GitHub Action and Fix Data Race in CreateTailscaleNodesInUser (#2038)
* Add -race flag to Makefile and integration tests; fix data race in CreateTailscaleNodesInUser

* Fix data race in ExecuteCommand by using local buffers and mutex

Signed-off-by: Dongjun Na <kmu5544616@gmail.com>

* lint

Signed-off-by: Dongjun Na <kmu5544616@gmail.com>

---------

Signed-off-by: Dongjun Na <kmu5544616@gmail.com>
2024-12-17 14:06:57 +01:00
Shaw Drastin
7d937c6bd0 Correct macOS GUI connect guide because there's no ALT key on a mac (#2306)
* Correct macOS GUI connect guide because there's no ALT key on a mac
* also correct macOS GUI connect in hscontrol text
2024-12-17 12:11:27 +00:00
Kristoffer Dalby
ccc895b4c6 fixes to extra-record file watcher (#2298)
* Fix excess error message during writes

Fixes #2290

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* retry filewatcher on removed files

This should handled if files are deleted and added again, and for rename
scenarios.

Fixes #2289

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* test more write and remove in filewatcher

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-16 11:26:56 +01:00
Kristoffer Dalby
5345f19693 fix issue where some oidc claim bools are sent as string (#2297)
Jumpcloud send invalid json, so we need to handle it.

Fixes #2293

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-16 11:26:32 +01:00
Kristoffer Dalby
ec8729b772 fix sighup issue with empty acl (#2296)
Fixes #2291

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-16 07:48:19 +01:00
github-actions[bot]
e00b9d9a91 flake.lock: Update (#2294) 2024-12-15 06:46:14 +00:00
Kristoffer Dalby
58d089ce0a fix deletion of exit routes without nodes (#2286)
Fixes #2259

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-13 20:15:24 +01:00
Kristoffer Dalby
76d26a7eec update oidc part of changelog for 0.24.0 (#2285) 2024-12-13 12:35:24 +00:00
Kristoffer Dalby
380fcdba17 Add worker reading extra_records_path from file (#2271)
* consolidate scheduled tasks into one goroutine

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* rename Tailcfg dns struct

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add dns.extra_records_path option

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* prettier lint

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* go-fmt

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-13 07:52:40 +00:00
Florian Preinstorfer
89a648c7dd Remove use_username_in_magic_dns option
Upgrade the use of dns.use_username_in_magic_dns or
dns_config.use_username_in_magic_dns to a fatal error and remove the
option from the example configuration and integration tests.

Fixes: #2219
2024-12-11 18:39:35 +01:00
Vitalij Dovhanyc
697d80d5a8 chore: configure some actions to be skipped for forks (#2005)
* chore: configure some actions to be skipped for forks

* chore: build docs only when it changes
2024-12-11 16:44:37 +01:00
Kristoffer Dalby
757defa2f2 run cross compile of headscale as part of build (#2270) 2024-12-10 16:26:53 +01:00
Kristoffer Dalby
64fd1f9483 restructure command/api to use stable IDs (#2261) 2024-12-10 16:23:55 +01:00
Kristoffer Dalby
08bd4b9bc5 fix docker network caps (#2273)
Docker releases a patch release which changed the required permissions to be able to do tun devices in containers, this caused all containers to fail in tests causing us to fail all tests. This fixes it, and adds some tools for debugging in the future.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-12-09 16:15:38 +00:00
github-actions[bot]
26d91ae513 flake.lock: Update (#2266) 2024-12-08 09:10:42 +00:00
Florian Preinstorfer
75e74117db Add FAQ entry on which database to use
Fixes: #2257
2024-12-04 06:08:44 +01:00
Florian Preinstorfer
d2a86b1ef2 Fix broken indent 2024-12-04 06:08:44 +01:00
Florian Preinstorfer
0d3cf74098 Fix README links to point to the stable version 2024-12-04 06:08:44 +01:00
Florian Preinstorfer
44456497b0 Add versioned documentation
Setup mike to provide versioned builds of the documentation.

The goal is to have versioned docs for stable releases (0.23.0, 0.24.0)
and development docs that can progress along with the code. This allows
us to tailor docs to the next upcoming version as we no longer need to
care about diversion between rendered docs and the latest release.

Versions:
* development (alias: unstable) on each push to the main branch
* MAJOR.MINOR.PATCH (alias: stable, latest for the newest version)
  * for each "final" release tag
  * for each push to doc maintenance branches: doc/MAJOR.MINOR.PATCH

The default version should the current stable version. The doc
maintenance branches may be used to update the version specific
documentation when issues arise after a release.
2024-12-02 16:51:50 +01:00
Florian Preinstorfer
7512e236d6 Bump deprecated github actions 2024-11-27 09:52:08 +01:00
Kristoffer Dalby
f7b0cbbbea wrap policy in policy manager interface (#2255)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-26 15:16:06 +01:00
github-actions[bot]
2c1ad6d11a flake.lock: Update (#2254) 2024-11-24 09:42:22 +00:00
Kristoffer Dalby
fffd23602b Resolve user to stable unique ID in policy (#2205) 2024-11-24 00:13:27 +01:00
Kristoffer Dalby
3a2589f1a9 rename dockerfile to integration to avoid confusion (#2225)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:36 +00:00
Kristoffer Dalby
f6276ab9d2 fix postgres constraints, add postgres testing
This commit fixes the constraint syntax so it is both valid for
sqlite and postgres.

To validate this, I've added a new postgres testing library and a
helper that will spin up local postgres, setup a db and use it in
the constraints tests. This should also help testing db stuff in
the future.

postgres has been added to the nix dev shell and is now required
for running the unit tests.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
7d9b430ec2 fix constraints
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
3780c9fd69 fix nil in test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
281025bb16 fix constraints
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
5e7c3153b9 nits
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
7ba0c3d515 use userID instead of username everywhere
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
4b58dc6eb4 make preauthkey tags test stable
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
4dd12a2f97 fix oidc test, add tests for migration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
2fe65624c0 restore strip_email_domain for migration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
35b669fe59 add iss to identifier, only set email if verified
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
dc07779143 add @ to end of username if not present
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
d72663a4d0 remove log print
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
0a82d3f17a update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
78214699ad Harden OIDC migration and make optional
This commit hardens the migration part of the OIDC from
the old username based approach to the new sub based approach
and makes it possible for the operator to opt out entirely.

Fixes #1990

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-23 21:14:55 +01:00
Kristoffer Dalby
64bb56352f make configurable wal auto checkpoint (#2242) 2024-11-23 21:03:48 +01:00
nblock
dc17b4d378 Documentation dependencies (#2252)
* Use a trailing slash
recommended by mkdocs-material

* Update doc requirements
Let mkdocs-material resolve its imaging dependencies (cairosvg and
pillow) and fix a dependabot warning along the way.

Reference compatible versions by major.minor.
2024-11-22 16:52:36 +00:00
Kristoffer Dalby
a6b19e85db more linter fixups (#2212)
* linter fixes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* conf

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update nix hash

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-22 15:54:58 +00:00
ArcticLampyrid
edf9e25001 feat: support client verify for derp (add integration tests) (#2046)
* feat: support client verify for derp

* docs: fix doc for integration test

* tests: add integration test for DERP verify endpoint

* tests: use `tailcfg.DERPMap` instead of `[]byte`

* refactor: introduce func `ContainsNodeKey`

* tests(dsic): use string builder for cmd args

* ci: fix tests order

* tests: fix derper failure

* chore: cleanup

* tests(verify-client): perfer to use `CreateHeadscaleEnv`

* refactor(verify-client): simplify error handling

* tests: fix `TestDERPVerifyEndpoint`

* refactor: make `doVerify` a seperated func

---------

Co-authored-by: 117503445 <t117503445@gmail.com>
2024-11-22 13:23:05 +01:00
Motiejus Jakštys
c6336adb01 config: loosen up BaseDomain and ServerURL checks (#2248)
* config: loosen up BaseDomain and ServerURL checks

Requirements [here][1]:

> OK:
> server_url: headscale.com, base: clients.headscale.com
> server_url: headscale.com, base: headscale.net
>
> Not OK:
> server_url: server.headscale.com, base: headscale.com
>
> Essentially we have to prevent the possibility where the headscale
> server has a URL which can also be assigned to a node.
>
> So for the Not OK scenario:
>
> if the server is: server.headscale.com, and a node joins with the name
> server, it will be assigned server.headscale.com and that will break
> the connection for nodes which will now try to connect to that node
> instead of the headscale server.

Fixes #2210

[1]: https://github.com/juanfont/headscale/issues/2210#issuecomment-2488165187

* server_url and base_domain: re-word error message, fix a one-off bug and add a test case for the bug.

* lint

* lint again
2024-11-22 13:21:44 +01:00
enoperm
5fbf3f8327 Websocket derp test fixes (#2247)
* integration testing: add and validate build-time options for tailscale head

* fixup! integration testing: add and validate build-time options for tailscale head

integration testing: comply with linter

* fixup! fixup! integration testing: add and validate build-time options for tailscale head

integration testing: tsic.New must never return nil

* fixup! fixup! fixup! integration testing: add and validate build-time options for tailscale head

* minor fixes
2024-11-22 11:57:01 +01:00
Nathan Sweet
6275399327 Update tls.md to mention using the full cert chain (#2243) 2024-11-18 06:12:12 +00:00
nblock
29119bb7f4 Misc doc fixes (#2240)
* Link back to node registration docs
* adjust wording in apple docs
* Mention client specific page to check if headscale works

Ref: #2238
2024-11-18 05:46:58 +01:00
github-actions[bot]
93ba21ede5 flake.lock: Update (#2239) 2024-11-17 19:38:50 +00:00
nblock
a7874af3d0 Use discord server invite link (#2235)
Replace channel links with links to discord invite link and remove
channel list.

Fixes: #1521
2024-11-16 07:06:15 +01:00
nblock
e7245856c5 Refresh remote CLI documentation (#2216)
* Document to either use a minimal configuration file or environment
  variables to connect with a remote headscale instance.
* Document a workaround specific for headscale 0.23.0.
* Remove reference to ancient headscale version.
* Use `cli.insecure: true` or `HEADSCALE_CLI_INSECURE=1` to skip
  certificate verification.
* Style and typo fixes

Ref: #2193
2024-11-13 18:35:42 +01:00
nblock
2345c38e1e Add a page for third-party tools (#2217)
* Remove status from web-ui docs

Rename the title to indicate that there multiple web interfaces
available. Do not track the status of each web interface here as their
status is subject to change over time.

* Add page for third-party tools and scripts
2024-11-12 16:53:30 +01:00
github-actions[bot]
8cfaa6bdac flake.lock: Update (#2222) 2024-11-12 13:27:49 +00:00
docgalaxyblock
4e44d57bf7 fix: missing stable-debug tag (#2232)
Fixes #2171
2024-11-11 06:06:44 +00:00
Philip Henning
0089ceaf1d Feature tvos documentation (#2226)
* Add usage documentation for tvOS

* lint and format

* Change admonition to mkdocs flavoured style

* fix typos

* Update hscontrol/templates/apple.go

Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>

* change outer quoting for where quoting in-text is used

---------

Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2024-11-07 14:56:18 +00:00
nblock
9a46c5763c Handle /derp/latency-check (#2227)
According to 15fc6cd966
the routes `/derp/probe` and `/derp/latency-check` are the same and
different versions of the tailscale client use one or the other
endpoint.

Also handle /derp/latency-check

Fixes: #2211
2024-11-06 15:59:38 +01:00
Kristoffer Dalby
a71a933705 add nblock to doc owners (#2207)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-11-04 10:12:50 -06:00
github-actions[bot]
0c98d09783 Update flake.lock (#2195)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/e2f08f4d8b3ecb5cf5c9fd9cb2d53bb3c71807da?narHash=sha256-CAZF2NRuHmqTtRTNAruWpHA43Gg2UvuCNEIzabP0l6M%3D' (2024-10-05)
  → 'github:NixOS/nixpkgs/41dea55321e5a999b17033296ac05fe8a8b5a257?narHash=sha256-WvLXzNNnnw%2BqpFOmgaM3JUlNEH%2BT4s22b5i2oyyCpXE%3D' (2024-10-25)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 14:16:10 +00:00
Kristoffer Dalby
e2d5ee0927 cleanup linter warnings (#2206)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-23 10:45:59 -05:00
Kristoffer Dalby
028d9aab73 add new user fields to grpc and list command (#2202)
Updates #2166

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-18 14:20:03 +00:00
hopleus
b6dc6eb36c #2140 Fixed reflection of hostname change (#2199)
* #2140 Fixed updating of hostname and givenName when it is updated in HostInfo

* #2140 Added integration tests

* #2140 Fix unit tests

* Changed IsAutomaticNameMode to GivenNameHasBeenChanged. Fixed errors in files according to golangci-lint rules
2024-10-17 09:45:33 -06:00
Goran Draganić
45c9585b52 feat: derpmap field in config (#1823) 2024-10-17 05:34:20 -06:00
hopleus
cc42fc394a #2177 Added conversion of 'Hostname' to 'givenName' in a node with FQDN rules applied (#2198) 2024-10-15 18:33:03 +02:00
hopleus
52a3b54ba2 Fixed loginUrl with "WithTLS()" used. Added "WithTLS()" to scenario integration tests (#2187) 2024-10-15 14:38:43 +02:00
Pepper
0602304cea Add headplane
Add headplane to the list of UI's
2024-10-11 16:32:33 +02:00
nblock
8c7d8ee34f Restructure headscale documentation (#2163)
* Setup mkdocs-redirects

* Restructure existing documentation

* Move client OS support into the documentation

* Move existing Client OS support table into its own documentation page
* Link from README.md to the rendered documentation
* Document minimum Tailscale client version

* Reuse CONTRIBUTING.md" in the documentation

* Include "CONTRIBUTING.md" from the repository root
* Update FAQ and index page and link to the contributing docs

* Add configuration reference

* Add a getting started page and explain the first steps with headscale

* Use the existing "Using headscale" sections and combine them into a
  single getting started guide with a little bit more explanation.
* Explain how to get help from the command line client.
* Remove duplicated sections from existing installation guides

* Document requirements and assumptions

* Document packages provided by the community

* Move deb install guide to official releases

* Move manual install guide to official releases

* Move container documentation to setup section

* Move sealos documentation to cloud install page

* Move OpenBSD docs to build from source

* Simplify DNS documentation

* Add sponsor page

* Add releases page

* Add features page

* Add help page

* Add upgrading page

* Adjust mkdocs nav

* Update wording

Use the term headscale for the project, Headscale on the beginning of a
sentence and `headscale` when refering to the CLI.

* Welcome to headscale

* Link to existing documentation in the FAQ

* Remove the goal header and use the text as opener

* Indent code block in OIDC

* Make a few pages linter compatible

Also update ignored files for prettier

* Recommend HTTPS on port 443

Fixes: #2164

* Use hosts in acl documentation

thx @efficacy38 for noticing this

Ref: #1863

* Use mkdocs-macros to set headscale version once
2024-10-10 15:24:04 +02:00
hopleus
b3cda08af6 #2178 Fixed processing of fields in post request in MoveNode rpc (#2179)
* #2178 Fixed processing of fields in post request in MoveNode rpc

* #2178 Updated CHANGELOG.md
2024-10-09 09:36:47 +02:00
github-actions[bot]
101ca7f4a2 Update flake.lock (#2173)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/b5b2fecd0cadd82ef107c9583018f381ae70f222?narHash=sha256-k6YxGj08voz9NvuKExojiGXAVd69M8COtqWSKr6sQS4%3D' (2024-09-28)
  → 'github:NixOS/nixpkgs/e2f08f4d8b3ecb5cf5c9fd9cb2d53bb3c71807da?narHash=sha256-CAZF2NRuHmqTtRTNAruWpHA43Gg2UvuCNEIzabP0l6M%3D' (2024-10-05)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-06 12:00:59 +00:00
Amha Mersha
24e7851a40 Changed all the html into go using go-elem (#2161)
* Changed all the HTML into go using go-elem

            Created templates package in ./hscontrol/templates.
            Moved the registerWebAPITemplate into the templates package as a function to be called.

            Replaced the apple and windows html files with go-elem.

* update flake

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-04 11:39:24 +00:00
Kristoffer Dalby
9515040161 make reauth test compat with tailscale head (#2167)
* make reauth test compat with tailscale head

tailscale/tailscale@1eaad7d broke our reauth test as it makes the client
retry with https/443 if it reconnects within 2 minutes.

This commit fixes this by running the test as a two part,
- with https, to confirm instant reconnect works
- with http, and a 3 min wait, to check that it work without.

The change is not a general consern as headscale in prod is ran
with https.

Updates #2164

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* sort test for stable order

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-03 12:01:48 +02:00
Kristoffer Dalby
e16ea2ee69 set hostinfo,ipv* columns explicitly (#2165)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 18:12:25 +02:00
Kristoffer Dalby
218138afee Redo OIDC configuration (#2020)
expand user, add claims to user

This commit expands the user table with additional fields that
can be retrieved from OIDC providers (and other places) and
uses this data in various tailscale response objects if it is
available.

This is the beginning of implementing
https://docs.google.com/document/d/1X85PMxIaVWDF6T_UPji3OeeUqVBcGj_uHRM5CI-AwlY/edit
trying to make OIDC more coherant and maintainable in addition
to giving the user a better experience and integration with a
provider.

remove usernames in magic dns, normalisation of emails

this commit removes the option to have usernames as part of MagicDNS
domains and headscale will now align with Tailscale, where there is a
root domain, and the machine name.

In addition, the various normalisation functions for dns names has been
made lighter not caring about username and special character that wont
occur.

Email are no longer normalised as part of the policy processing.

untagle oidc and regcache, use typed cache

This commits stops reusing the registration cache for oidc
purposes and switches the cache to be types and not use any
allowing the removal of a bunch of casting.

try to make reauth/register branches clearer in oidc

Currently there was a function that did a bunch of stuff,
finding the machine key, trying to find the node, reauthing
the node, returning some status, and it was called validate
which was very confusing.

This commit tries to split this into what to do if the node
exists, if it needs to register etc.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 14:50:17 +02:00
Kristoffer Dalby
bc9e83b52e use gorm serialiser instead of custom hooks (#2156)
* add sqlite to debug/test image

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* test using gorm serialiser instead of custom hooks

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 11:41:58 +02:00
Kristoffer Dalby
3964dec1c6 use tsaddr library and cleanups (#2150)
* resuse tsaddr code instead of handrolled

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* ensure we dont give out internal tailscale IPs

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use prefix instead of string for routes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove old custom compare func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* trim unused util code

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-10-02 09:06:09 +02:00
Jacob Yundt
63035cdb5a Update headscale user creation settings in .deb (#2134)
* Update headscale user creation settings in .deb

Update the headscale user settings to:
- shell = /usr/sbin/nologin
- home-dir = /var/lib/headscale

This syncs the .deb installation behavior with the current Linux docs:
fe68f50328/docs/running-headscale-linux-manual.md (L39-L45)

Fixes juanfont/headscale#2133

* slight refactor to use existing variables.

* Fixup for HOME_DIR var
2024-09-29 11:00:52 +00:00
Kristoffer Dalby
5eda9c8d2d denormalise PreAuthKey tags (#2155)
this commit denormalises the Tags related to a Pre auth key
back onto the preauthkey table and struct as a string list.

There was not really any real normalisation here as we just added
a bunch of duplicate tags with new IDs and preauthkeyIDs, lots of
GORM cermony but no actual advantage.

This work is the start to fixup tags which currently are not working
as they should.

Updates #1369

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-29 13:00:27 +02:00
github-actions[bot]
49ce5734fc flake.lock: Update (#2158) 2024-09-29 08:24:08 +00:00
Hazel Atkinson
204a102389 Add ouroboros to web ui list (#2154) 2024-09-27 09:16:18 +02:00
Robert Schütz
2c974dd72d MagicDNS no longer requires nameservers (#1681)
According to https://tailscale.com/kb/1081/magicdns#accessing-devices-over-magicdns,

> MagicDNS does not require a DNS nameserver if running Tailscale v1.20 or later.
2024-09-26 19:09:19 +00:00
nblock
e367454745 Add -it to docker exec (#2148)
Some commands such as `nodes delete` require user interaction and they
fail if `-it` is no supplied to `docker exec`. Use `docker exec -it` in
documentation examples to also make them work in interactive commands.
2024-09-25 09:52:28 +02:00
Kristoffer Dalby
4f2fb65929 remove versions older than 1.56 (#2149)
* remove versions older than 1.56

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove code no longer needed for new clients

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-24 18:34:20 +02:00
David Mell
07b596d3cc Allow nodes to use SSH agent forwarding (#2145) 2024-09-23 11:59:16 +02:00
github-actions[bot]
f3fca8302a flake.lock: Update (#2143) 2024-09-22 09:46:04 +00:00
enoperm
1e61084898 Add compatibility with only websocket-capable clients (#2132)
* handle control protocol through websocket

The necessary behaviour is already in place,
but the wasm build only issued GETs, and the handler was not invoked.

* get DERP-over-websocket working for wasm clients

* Prepare for testing builtin websocket-over-DERP

Still needs some way to assert that clients are connected through websockets,
rather than the TCP hijacking version of DERP.

* integration tests: properly differentiate between DERP transports

* do not touch unrelated code

* linter fixes

* integration testing: unexport common implementation of derp server scenario

* fixup! integration testing: unexport common implementation of derp server scenario

* dockertestutil/logs: remove unhelpful comment

* update changelog

---------

Co-authored-by: Csaba Sarkadi <sarkadicsa@tutanota.de>
2024-09-21 12:05:36 +02:00
Kristoffer Dalby
10a72e8d54 update changelog for 0.23 release (#2138)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-18 09:43:08 +01:00
Kristoffer Dalby
ed78ecda12 add shutdown that asserts if headscale had panics (#2126)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-17 11:44:55 +02:00
github-actions[bot]
6cbbcd859c flake.lock: Update (#2135) 2024-09-16 06:15:45 +00:00
nblock
e9d9c0773c Exclude irrelevant files from mkdocs rendering (#2136) 2024-09-16 06:13:45 +00:00
nblock
fe68f50328 Use headscale.example.com (#2122) 2024-09-11 16:46:06 +00:00
nblock
c3ef90a7f7 Update documentation for Apple (#2117)
* Rename docs/ios-client.md to docs/apple-client.md. Add instructions
  for macOS; those are copied from the /apple endpoint and slightly
  modified. Fix doc links in the README.
* Move infoboxes for /apple and /windows under the "Goal" section to the
  top. Those should be seen by users first as they contain *their*
  specific headscale URL.
* Swap order of macOS and iOS to move "Profiles" further down.
* Remove apple configuration profiles
* Remove Tailscale versions hints
* Mention /apple and /windows in the README along with their docs

See: #2096
2024-09-11 18:43:59 +02:00
Kristoffer Dalby
064c46f2a5 move logic for validating node names (#2127)
* move logic for validating node names

this commits moves the generation of "given names" of nodes
into the registration function, and adds validation of renames
to RenameNode using the same logic.

Fixes #2121

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix double arg

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-11 18:27:49 +02:00
Kristoffer Dalby
64319f79ff make stream shutdown if self-node has been removed (#2125)
* add shutdown that asserts if headscale had panics

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add test case producing 2118 panic

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* make stream shutdown if self-node has been removed

Currently we will read the node from database, and since it is
deleted, the id might be set to nil. Keep the node around and
just shutdown, so it is cleanly removed from notifier.

Fixes #2118

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-11 12:00:32 +02:00
Kristoffer Dalby
4b02dc9565 make cli mode respect log.level (#2124)
Fixes #2119

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-11 10:43:22 +02:00
Kristoffer Dalby
7be8796d87 dont override golangci go (#2116)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-09 14:29:09 +02:00
curlwget
99f18f9cd9 chore: fix some comments (#2069) 2024-09-09 14:17:25 +02:00
github-actions[bot]
c3b260a6f7 flake.lock: Update (#2111) 2024-09-09 14:16:35 +02:00
Kristoffer Dalby
60b94b0467 Fix slow shutdown (#2113)
* rearrange shutdown

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* http closed is fine

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* logging while shutting

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-09 14:10:22 +02:00
nblock
bac7ea67f4 Simplify windows setup instructions (#2114)
* Simplify /windows to the bare minimum. Also remove the
  /windows/tailscale.reg endpoint as its generated file is no longer
  valid for current Tailscale versions.
* Update and simplify the windows documentation accordingly.
* Add a "Unattended mode" section to the troubleshooting section
  explaining how to enable "Unattended mode" in the via the Tailscale
  tray icon.
* Add infobox about /windows to the docs

Tested on Windows 10, 22H2 with Tailscale 1.72.0

Replaces: #1995
See: #2096
2024-09-09 13:18:16 +02:00
nblock
5597edac1e Remove version and update setup instructions for Android (#2112) 2024-09-09 06:57:50 +00:00
Kristoffer Dalby
8a3a0fee3c Only load needed part of configuration (#2109) 2024-09-07 09:23:58 +02:00
Kristoffer Dalby
f368ed01ed 2068 AutoApprovers tests (#2105)
* replace old suite approved routes test with table driven

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add test to reproduce issue

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add integration test for 2068

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-05 16:46:20 +02:00
Kristoffer Dalby
adc084f20f add no stalebot exception (#2107)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-05 14:00:36 +02:00
Kristoffer Dalby
42d2c27853 fix goreleaser warnings (#2106)
* add version to goreleaser config

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* rename deprected setting

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-09-05 14:00:19 +02:00
nblock
1c34101e72 Remove outdated proposals (#2104)
Fixes: #2101
2024-09-05 11:50:10 +00:00
greizgh
6609f60938 actually lint file on CI (#2018)
* replace deprecated golangci-lint output format

CI was producing this kind of messages:
> [config_reader] The output format `github-actions` is deprecated, please use `colored-line-number`

* Actually lint files on CI
2024-09-05 13:37:05 +02:00
nblock
35bfe7ced0 Add support for service reload and sync service file (#2102)
* Add support for service reload and sync service file

* Copy the systemd.service file to the manual linux docs and adjust the
  path to the headscale binary to match with the previous documentation
  blocks. Unfortunately, there seems to be no easy way to include a
  file in mkdocs.
* Remove a redundant "deprecation" block. The beginning of the
  documentation already states that.
* Add `ExecReload` to the systemd.service file.

Fixes: #2016

* Its called systemd

* Fix link to systemd homepage
2024-09-05 10:08:50 +00:00
nblock
e43d6a0361 Move flags after the command (#2100)
The built-in help also shows flags to given after the command. Align
documentation examples accordingly.
2024-09-04 12:38:38 +00:00
Kristoffer Dalby
f039caf134 update godeps (#2098) 2024-09-04 07:55:16 +02:00
nblock
d66c5e144f Update documentation for 0.23 (#2096)
* docs/acl: fix path to policy file

* docs/exit-node: fixup for 0.23

* Add newlines between commands to improve readability
* Use nodes instead on name
* Remove query parameter from link to Tailscale docs

* docs/remote-cli: fix formatting

* Indent blocks below line numbers to restore numbering
* Fix minor typos

* docs/reverse-proxy: remove version information

* Websocket support is always required now
* s/see detail/see details

* docs/exit-node: add warning to manual documentation

* Replace the warning section with a warning admonition
* Fix TODO link back to the regular linux documentation

* docs/openbsd: fix typos

* the database is created on-the-fly

* docs/sealos: fix typos

* docs/container: various fixes

* Remove a stray sentence
* Remove "headscale" before serve
* Indent line continuation
* Replace hardcoded 0.22 with <VERSION>
* Fix path in debug image to /ko-app/headscale

Fixes: #1822

aa
2024-09-03 11:04:20 +00:00
Mike Poindexter
3101f895a7 Fix 764 (#2093)
* Fix KeyExpiration when a zero time value has a timezone

When a zero time value is loaded from JSON or a DB in a way that
assigns it the local timezone, it does not roudtrip in JSON as a
value for which IsZero returns true. This causes KeyExpiry to be
treated as a far past value instead of a nilish value.

See https://github.com/golang/go/issues/57040

* Fix whitespace

* Ensure that postgresql is used for all tests when env var is set

* Pass through value of HEADSCALE_INTEGRATION_POSTGRES env var

* Add option to set timezone on headscale container

* Add test for registration with auth key in alternate timezone
2024-09-03 09:22:17 +02:00
nblock
aa0f3d43cc Fix typo in example config (#2095) 2024-09-02 06:18:16 +00:00
nblock
ed71d230eb Remove references to tests/acls from the documentation (#2088) 2024-09-01 13:09:47 +00:00
github-actions[bot]
976cbfa630 flake.lock: Update (#2078) 2024-09-01 13:08:57 +00:00
nblock
a9a1a07e37 Use dns: as config key (#2092) 2024-09-01 13:08:06 +00:00
Kristoffer Dalby
1193a50e9e oldest client supported, not latest (#2086) 2024-08-30 16:59:37 +02:00
Kristoffer Dalby
cb0e2e4476 various doc updates in prep for 0.23 (#2091)
* various doc updates in prep for 0.23

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add note discouraging postgresql

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Update docs/faq.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* remove entry for glossary in doc

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix typo

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2024-08-30 16:59:24 +02:00
Kristoffer Dalby
2b5e52b08b validate policy against nodes, error if not valid (#2089)
* validate policy against nodes, error if not valid

this commit aims to improve the feedback of "runtime" policy
errors which would only manifest when the rules are compiled to
filter rules with nodes.

this change will in;

file-based mode load the nodes from the db and try to compile the rules on
start up and return an error if they would not work as intended.

database-based mode prevent a new ACL being written to the database if
it does not compile with the current set of node.

Fixes #2073
Fixes #2044

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* ensure stderr can be used in err checks

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* test policy set validation

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add new integration test to ghaction

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add back defer for cli tst

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-08-30 16:58:29 +02:00
Roman Zabaluev
fffd9d7ee9 Update ACLs file format docs (#2066) 2024-08-30 07:20:07 +00:00
Mike Poindexter
76515d12d6 Fix self notification on expiry update via oidc relogin (#2080) 2024-08-30 08:20:29 +02:00
Mike Poindexter
34361c6f82 Fix FKs on sqlite migrations (#2083) 2024-08-30 08:08:54 +02:00
Thomas Gautvedt
f4427dd29e fix: correct a small spelling mistake (#2081) 2024-08-28 07:50:09 +00:00
Kristoffer Dalby
cf6a606d74 fix route table migration wiping routes 0.22 -> 0.23 (#2076) 2024-08-27 18:54:28 +02:00
dragon2611
827e3e83ae Issue 2045, Feature Request (#2071)
Requiring someone to write a design doc/contribute to the feature shouldn't be a requirement for raising a feature request as users may lack the skills required to do this.
2024-08-27 11:03:51 +02:00
Kristoffer Dalby
9c4c286696 fix warning errs from beta2 (#2075)
* remove default false for use usernames causing warning

Fixes #2065

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Ensure DoH warnings are only emitted if err

Fixes #2064

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-08-23 17:17:37 +02:00
Kristoffer Dalby
a68854ac33 upgrade go (1.23) and tailscale (1.72.1) (#2077) 2024-08-23 15:28:54 +02:00
github-actions[bot]
9bed76d481 flake.lock: Update (#2059) 2024-08-19 11:05:43 +00:00
Kristoffer Dalby
84cb5d0aed make acl_policy_path fatal if policy.path is not set (#2041) 2024-08-19 13:03:01 +02:00
Kristoffer Dalby
f99497340b add coderabbit config (#2060)
Code Rabbit is one of these new fancy LLM code review tools. I am skeptical
but we can try it for free and it might provide us with some value to let
people get feedback while waiting for other people.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-08-19 10:06:55 +00:00
nadongjun
fdc034e8ae Integrate GORM Logger with Zerolog and Add Configuration Options for Logging and Performance (#2040)
* Integrate GORM logger with zerolog and add custom GORM configuration options

* Add GormConfig struct to group GORM-related settings

* Update debug mode instruction in config-example.yaml

Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>

---------

Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2024-08-19 11:47:52 +02:00
Kristoffer Dalby
ac8491efec Redo DNS configuration (#2034)
this commit changes and streamlines the dns_config into a new
key, dns. It removes a combination of outdates and incompatible
configuration options that made it easy to confuse what headscale
could and could not do, or what to expect from ones configuration.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-08-19 11:41:05 +02:00
Chuangbo Li
022fb24cd9 Fix command get policy works with relative policy path (#2051) 2024-08-12 12:11:59 +02:00
github-actions[bot]
fcd1183805 flake.lock: Update (#2052) 2024-08-11 05:48:15 +00:00
Kristoffer Dalby
ece907d878 test embedded derp with derp updater, check client health (#2030) 2024-08-11 07:44:59 +02:00
github-actions[bot]
948d53f934 flake.lock: Update (#2042) 2024-08-04 06:35:46 +00:00
github-actions[bot]
06f07053eb flake.lock: Update (#2035) 2024-07-28 08:42:38 +00:00
nadongjun
4ad3f3c484 Fix data race issues in EphemeralGarbageCollector tests (#2023)
* Fix data race issues in EphemeralGarbageCollector tests

* Add defer for mutex unlock in TestEphemeralGarbageCollectorOrder

* Fix mutex unlock order in closure by updating defer placement
2024-07-22 15:11:57 +00:00
Steven Honson
db7a4358e9 config-example.yaml: Remove reference to yaml for policy files (#2022) 2024-07-22 13:38:42 +00:00
github-actions[bot]
b799245f1e flake.lock: Update (#2021) 2024-07-22 08:56:21 +02:00
greizgh
8571513e3c reformat code (#2019)
* reformat code

This is mostly an automated change with `make lint`.
I had to manually please golangci-lint in routes_test because of a short
variable name.

* fix start -> strategy which was wrongly corrected by linter
2024-07-22 08:56:00 +02:00
Kristoffer Dalby
ca47d6f353 small cleanups (#2017) 2024-07-19 09:21:14 +02:00
Kristoffer Dalby
11fde62b8c remove custom contains funcs for slices.Contains (#2015)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-07-19 09:04:04 +02:00
Kristoffer Dalby
9e523d4687 move userprofiles into method on user struct (#2014)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-07-19 09:03:18 +02:00
Kristoffer Dalby
7e62031444 replace ephemeral deletion logic (#2008)
* replace ephemeral deletion logic

this commit replaces the way we remove ephemeral nodes,
currently they are deleted in a loop and we look at last seen
time. This time is now only set when a node disconnects and
there was a bug (#2006) where nodes that had never disconnected
was deleted since they did not have a last seen.

The new logic will start an expiry timer when the node disconnects
and delete the node from the database when the timer is up.

If the node reconnects within the expiry, the timer is cancelled.

Fixes #2006

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use uint64 as authekyid and ptr helper in tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add test db helper

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add list ephemeral node func

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* schedule ephemeral nodes for removal on startup

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix gorm query for postgres

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add godoc

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-07-18 10:01:59 +02:00
Pallab Pain
58bd38a609 feat: implements apis for managing headscale policy (#1792) 2024-07-18 07:38:25 +02:00
Rubens Peculis
00ff288f0c fix(1996): Implement register method enum converter (#2013)
Added a new function `RegisterMethodToV1Enum()` to Node, converting the internal register method string to the corresponding V1 Enum value. Included corresponding unit test in `node_test.go` to ensure correct conversion for various register methods.
2024-07-17 13:12:16 +02:00
greizgh
8823778d05 Add gofumpt to dev dependencies (#2010) 2024-07-17 13:12:02 +02:00
greizgh
74d27ee5fa Remove deprecated linters from golangci-lint (#2009)
When running lints, golangci-lint complained about removed
linters (which were already disabled).
This removes the relevant warnings.
2024-07-17 10:08:41 +02:00
github-actions[bot]
3f60ab23a6 Update flake.lock (#2011)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/4284c2b73c8bce4b46a6adf23e16d9e2ec8da4bb?narHash=sha256-i4vJL12/AdyuQuviMMd1Hk2tsGt02hDNhA0Zj1m16N8%3D' (2024-07-05)
  → 'github:NixOS/nixpkgs/8b5a3d5a1d951344d683b442c0739010b80039db?narHash=sha256-po3TZO9kcZwzvkyMJKb0WCzzDtiHWD34XeRaX1lWXp0%3D' (2024-07-12)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-14 06:20:22 +00:00
github-actions[bot]
eb1591df35 flake.lock: Update (#2000) 2024-07-07 06:16:36 +00:00
github-actions[bot]
89ada557bc flake.lock: Update (#1991) 2024-07-01 05:46:33 +00:00
Kristoffer Dalby
14a3f94f0c fix search domains and remove username from magicdns (#1987) 2024-06-26 13:44:40 +02:00
Kristoffer Dalby
4a34cfc4a6 Make write-ahead-log default and configurable for sqlite (#1985)
* correctly enable WAL log for sqlite

this commit makes headscale correctly enable write-ahead-log for
sqlite and adds an option to turn it on and off.

WAL is enabled by default and should make sqlite perform a lot better,
even further eliminating the need to use postgres.

It also adds a couple of other useful defaults.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-06-23 22:06:59 +02:00
Kristoffer Dalby
8f8f469c0a Remove allocations of lists before use (#1989)
* policy: remove allocs before appends in acls

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* notifier: make batcher tests stable/non-flaky

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* {db,derp,mapper}: dont allocate until append

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-06-23 22:06:50 +02:00
Lars Kiesow
69c33658f6 Fix android docs (#1976)
The current Tailscale app for Android looks and behaves differently.
This patch updates the documentation for that.
2024-06-23 00:52:23 +02:00
Kyhwana Pardus
99e91a9d8a Update reverse-proxy.md (#1986)
Add blurb about how cloudflare proxy/tunnels is not supported/will not work
2024-06-23 00:47:26 +02:00
github-actions[bot]
dfc089ed6a flake.lock: Update (#1979) 2024-06-16 20:24:08 +00:00
Lars Kiesow
51676c668b Make registration screen easier to use (#1975) 2024-06-15 09:40:49 +02:00
github-actions[bot]
1f4b59566a flake.lock: Update (#1958) 2024-06-09 07:23:16 +00:00
Dongjun Na
5f9c26930c fixed typo and path (#1960) 2024-05-28 16:11:39 +00:00
Kristoffer Dalby
5a4e52b727 remove last_successful_update error check (#1959)
most of the time we dont even check this error and checking
the string for particular errors is very flake as different
databases (sqlite and psql) use different error messages, and
some users might have it in other languages.

Fixes #1956

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-27 11:53:37 +01:00
github-actions[bot]
51b56ba447 Update flake.lock (#1952)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/b3fcfcfabd01b947a1e4f36622bbffa3985bdac6?narHash=sha256-iJYnKMtLi5u6hZhJm94cRNSDG5Rz6ZzIkGbhPFtDRm0%3D' (2024-05-15)
  → 'github:NixOS/nixpkgs/02923630b89aa1ab36ef8e422501a6f4fd4b2016?narHash=sha256-OhysviwHQz4p2HZL4g7XGMLoUbWMjkMr/ogaR3VUYNA%3D' (2024-05-18)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-05-24 10:30:11 +01:00
Kristoffer Dalby
c8ebbede54 Simplify map session management (#1931)
This PR removes the complicated session management introduced in https://github.com/juanfont/headscale/pull/1791 which kept track of the sessions in a map, in addition to the channel already kept track of in the notifier.

Instead of trying to close the mapsession, it will now be replaced by the new one and closed after so all new updates goes to the right place.

The map session serve function is also split into a streaming and a non-streaming version for better readability.

RemoveNode in the notifier will not remove a node if the channel is not matching the one that has been passed (e.g. it has been replaced with a new one).

A new tuning parameter has been added to added to set timeout before the notifier gives up to send an update to a node.

Add a keep alive resetter so we wait with sending keep alives if a node has just received an update.

In addition it adds a bunch of env debug flags that can be set:

- `HEADSCALE_DEBUG_HIGH_CARDINALITY_METRICS`: make certain metrics include per node.id, not recommended to use in prod. 
- `HEADSCALE_DEBUG_PROFILING_ENABLED`: activate tracing 
- `HEADSCALE_DEBUG_PROFILING_PATH`: where to store traces 
- `HEADSCALE_DEBUG_DUMP_CONFIG`: calls `spew.Dump` on the config object startup
- `HEADSCALE_DEBUG_DEADLOCK`: enable go-deadlock to dump goroutines if it looks like a deadlock has occured, enabled in integration tests.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-24 10:15:34 +02:00
Stefan Majer
8185a70dc7 Fix typos (#1860)
* Fix typos

* trigger GitHub actions

* remove kdiff3 orig files

* fix unicode

* remove unnecessary function call

* remove unnecessary comment

* remove unnecessary comment

---------

Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-05-19 23:49:27 +02:00
ohdearaugustin
2dc62e981e move debug image to distroless (#1950) 2024-05-19 11:17:37 +02:00
Kristoffer Dalby
5ad0aa44cb update tailscale go dep (#1948)
* update tailscale go dep

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update gorm go dep

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update grpc go dep

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update golang.org go dep

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update rest of go dep

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-17 08:58:33 -04:00
github-actions[bot]
723a0408a3 flake.lock: Update (#1897) 2024-05-16 14:01:34 +00:00
dependabot[bot]
30986c29cd Bump github.com/docker/docker (#1947) 2024-05-16 04:42:53 +00:00
dependabot[bot]
faa57ddc28 Bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 (#1945) 2024-05-16 03:02:12 +00:00
dependabot[bot]
fff229f4f6 Bump github.com/jackc/pgx/v5 from 5.5.3 to 5.5.4 (#1946) 2024-05-16 03:01:18 +00:00
dependabot[bot]
fd4f921281 Bump google.golang.org/protobuf from 1.32.0 to 1.33.0 (#1944) 2024-05-16 02:15:41 +00:00
dependabot[bot]
151f224a98 Bump golang.org/x/net from 0.22.0 to 0.23.0 (#1943)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-16 01:22:02 +00:00
Sandro
a9763c9692 Initialize log config earlier to prevent trace messages being printed early on (#1939)
like TRC DNS configuration loaded dns_config={....}
2024-05-15 20:40:30 -04:00
MichaelKo
7fd2485000 Restore foreign keys and add constraints (#1562)
* fix #1482, restore foregin keys, add constraints

* #1562, fix tests, fix formatting

* #1562: fix tests

* #1562: fix local run of test_integration
2024-05-15 20:40:14 -04:00
Dan Pastusek
2bac80cfbf [DOCS] Make linux installation instructions more clear (#1927)
* Make linux installation instructions more clear

* Update running-headscale-linux.md
2024-05-06 20:06:30 +02:00
Michael Savage
93a915c096 Update OpenBSD installation docs for 2024 (#1915) 2024-05-06 20:03:21 +02:00
Kristoffer Dalby
622aa82da2 ensure expire routines are cleaned up (#1924)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-02 15:57:53 +00:00
Kristoffer Dalby
a9c568c801 trace log and notifier shutdown (#1922)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-02 13:39:19 +02:00
Kristoffer Dalby
1c6bfc503c fix preauth key logging in as previous user (#1920)
* add test case to reproduce #1885

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix preauth key issue logging in as wrong user

Fixes #1885

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* add test to gh

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-02 11:53:16 +02:00
Kristoffer Dalby
55b35f4160 fix issue preveting get node when disco is missing (#1919)
Fixed #1816

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-05-01 17:06:42 +02:00
Kristoffer Dalby
d5ed8bc074 error if no prefix is configured (#1918)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-30 11:11:29 +02:00
Kristoffer Dalby
87e2ae4d52 add autogroup:internet, fix reduce filter rules (#1917) 2024-04-30 07:23:16 +02:00
Juan Font
ff427ccb78 Apply suggestions from code review
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-29 23:04:02 +02:00
Juan Font
39277844dd Apply suggestions from code review
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-29 23:04:02 +02:00
Juan Font
50a7d15769 Update CONTRIBUTING.md
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-29 23:04:02 +02:00
Juan Font
d740ee489e Update CONTRIBUTING.md
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-29 23:04:02 +02:00
Juan Font
10e37ec28d Add contributing document 2024-04-29 23:04:02 +02:00
Kristoffer Dalby
cb0b495ea9 batch updates in notifier (#1905) 2024-04-27 10:47:39 +02:00
Juan Font
fef8261339 Do not access node ID when node is not found (#1912) 2024-04-24 07:44:35 +02:00
Kristoffer Dalby
c62d5570f2 remove multistep build, build go last, allowing cached build layers (#1903) 2024-04-24 07:44:07 +02:00
Kristoffer Dalby
318d5d2b21 replace issue templates with github issue forms
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-22 23:10:34 +02:00
Kristoffer Dalby
9229d17bbe remove examples/, and kustomize (#1906)
this directory is unmaintained and not verified, if it should be restored, it should end up
under the community docs effort.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-22 21:21:38 +02:00
Juan Font
aba4b36030 Clarify relation with Tailscale (#1908) 2024-04-22 20:37:59 +02:00
Juan Font
bd047928f7 Move pprof to metrics router (#1902) 2024-04-21 22:08:59 +02:00
ChengenH
9375b09206 chore: use errors.New to replace fmt.Errorf with no parameters will much better
Signed-off-by: ChengenH <hce19970702@gmail.com>
2024-04-21 20:23:25 +02:00
Kristoffer Dalby
ba614a5e6c metrics, tuning in tests, db cleanups, fix concurrency issue (#1895) 2024-04-21 18:28:17 +02:00
oftenoccur
7d8178406d chore: fix function names in comment (#1866)
* chore: fix function names in comment

Signed-off-by: oftenoccur <ezc5@sina.com>

---------

Signed-off-by: oftenoccur <ezc5@sina.com>
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-21 18:19:38 +02:00
ohdearaugustin
8394208856 fix prettier 2024-04-21 17:32:41 +02:00
Arnaud Dezandee
803269a64c docs(readme): change contributors section (#1889) 2024-04-21 16:48:33 +02:00
Carson Yang
d6ec31c4e0 docs: Add docs for running headscale on sealos (#1666)
* docs: Add docs for running headscale on sealos

Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>

* run prettier

---------

Signed-off-by: Carson Yang <yangchuansheng33@gmail.com>
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-21 16:43:31 +02:00
Juan Font
68503581a0 Add test stage to docs (#1893)
* Add test stage to docs

Add new file with docs tets

Run only in pulls

* set explicit python version

* Revert "set explicit python version"

This reverts commit 4dd7b81f26.

* docs/requirements: update mkdocs-material

---------

Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-21 16:33:22 +02:00
Juan Font
e2afd30b1c Add the latest UI to the website 2024-04-18 14:55:59 +02:00
Juan Font
c906aaf927 Allow to remove forced tags of a node
Set as empty StringList
2024-04-18 09:55:55 +02:00
Juan Font
580f96ce83 Remove unused node check interval 2024-04-17 20:20:44 +02:00
Juan Font
c4c8cfe5ea Fix crash when a prefix family was empty 2024-04-17 15:28:06 +02:00
Kristoffer Dalby
40953727cf fix ip migration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-17 12:36:12 +02:00
Cas de Reuver
d4af0c386c Log available update as warning (#1877) 2024-04-17 11:22:53 +02:00
Kristoffer Dalby
2ce23df45a Migrate IP fields in database to dedicated columns (#1869) 2024-04-17 07:03:06 +02:00
Sergey Aksenov
85cef84e17 use newer fork of termcolor (#1842)
jagottsicher's fork fixed a bug in Windows implementation. While Windows may be not intended as a target platform,
some contributors may prefer it for development.

Also ran go mod tidy, thus two more unnecessary packages are removed from go.sum
2024-04-16 23:48:51 +02:00
Kristoffer Dalby
7d62e9fce5 move "embedded derp" settings into With options for integration tests (#1872) 2024-04-16 21:37:25 +02:00
Kristoffer Dalby
60f0cf908c more log.Error -> fmt.Errorf cleanup 2024-04-15 12:31:53 +02:00
Kristoffer Dalby
1704977e76 improve testing of route failover logic
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-15 12:31:53 +02:00
Kristoffer Dalby
bf4fd078fc clean up use of log.Error where errors could be wrapped
Replace a lot of occurences of log.Error with fmt.Errorf,
bubbling the error up the chain instead.
2024-04-15 12:31:53 +02:00
Kristoffer Dalby
58c94d2bd3 Rework map session
This commit restructures the map session in to a struct
holding the state of what is needed during its lifetime.

For streaming sessions, the event loop is structured a
bit differently not hammering the clients with updates
but rather batching them over a short, configurable time
which should significantly improve cpu usage, and potentially
flakyness.

The use of Patch updates has been dialed back a little as
it does not look like its a 100% ready for prime time. Nodes
are now updated with full changes, except for a few things
like online status.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-04-15 12:31:53 +02:00
github-actions[bot]
dd693c444c flake.lock: Update (#1848) 2024-04-14 06:46:44 +00:00
Brandon Schneider
2858ab402a Add new shasum for flake.nix 2024-04-11 08:01:26 +02:00
Brandon Schneider
7bea885b8c Updated dependencies, fixing segfault for OpenBSD
Fixes #1857
2024-04-11 08:01:26 +02:00
Riezebos
84de1854f8 Run prettier 2024-04-10 22:19:47 +02:00
Riezebos
6efc50789d Update docs/faq.md
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2024-04-10 22:19:47 +02:00
Riezebos
0fcfd643fa More concise 2024-04-10 22:19:47 +02:00
Riezebos
bdf54e802e Update answer based on comment 2024-04-10 22:19:47 +02:00
Riezebos
dbe32829a1 Update docs/faq.md
Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-04-10 22:19:47 +02:00
Riezebos
2fb7428ba9 Add FAQ question about using the same machine as server and client 2024-04-10 22:19:47 +02:00
Daniel Lo Nigro
8a8e25a8d1 [docs] Use modern Apt command to install package (#1420) 2024-03-22 20:32:01 +01:00
Maja Bojarska
4d9021047f Fix/improve documentation formatting (#1575)
* Fix docs formatting

* .prettierignore add docu

---------

Co-authored-by: ohdearaugustin <ohdearaugustin@users.noreply.github.com>
2024-03-22 19:55:20 +01:00
ohdearaugustin
74ff14eb30 update docs workflow (#1832) 2024-03-22 19:50:35 +01:00
Sandro
c1d4fef194 Downgrade update sent to debug (#1843) 2024-03-22 09:28:10 +00:00
George Pchelkin
785b150467 Fix typo in docs - DB file path (#1546) 2024-03-19 22:43:43 +01:00
github-actions[bot]
20bf3777d3 docs(README): update contributors (#1834)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-19 22:39:43 +01:00
github-actions[bot]
c29eddded3 flake.lock: Update (#1833) 2024-03-17 09:01:57 +01:00
zx
b477e5f366 Update docker related doc (#1421) 2024-03-14 23:01:33 +01:00
Sandro
95004de5e8 docs/reverse-proxy: use standard map, correct X-Forwarded-Proto variable for nginx (#1790) 2024-03-14 22:56:55 +01:00
Vitalij Dovhanyc
ef26f58085 update gh workflow actions (#1809) 2024-03-13 13:43:06 +01:00
github-actions[bot]
1d3eae8861 Update flake.lock (#1657)
Flake lock file updates:

• Updated input 'flake-utils':
    'github:numtide/flake-utils/1ef2e671c3b0c19053962c07dbda38332dcebf26' (2024-01-15)
  → 'github:numtide/flake-utils/d465f4819400de7c8d874d50b982301f28a84605' (2024-02-28)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/442d407992384ed9c0e6d352de75b69079904e4e' (2024-02-09)
  → 'github:NixOS/nixpkgs/0e7f98a5f30166cbed344569426850b21e4091d4' (2024-03-09)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-10 12:40:31 +01:00
Juan Font
a244eabd03 Ephemeral keys can now be reusable and non-reusable
Fixes the issue reported in #1712. In Tailscale SaaS, ephemeral keys can be single-user or reusable. Until now, our ephemerals were only reusable. This PR makes us adhere to the .com behaviour.
2024-03-03 12:37:37 +01:00
Vitalij Dovhanyc
e15a08326c simplify integration testing with matrix jobs (#1799)
* simplify integration testing with matrix jobs

* check if all of the integration tests are in the test-integration workflow
2024-03-02 22:36:38 +01:00
Kristoffer Dalby
c9966ba6c2 fix postgres migration (#1802) 2024-03-01 19:11:46 +01:00
Kristoffer Dalby
7a920ee701 move debug inside if in docker goreleaser tag (#1783)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-19 14:03:31 +01:00
Kristoffer Dalby
8b2c31aabc fix ifs in goreleaser gotemplate (#1781)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-19 10:50:52 +01:00
Kristoffer Dalby
5dbd59ca55 Get integration test netmap from watch-ipn command (#1729) 2024-02-18 23:22:18 +01:00
Kristoffer Dalby
3f162c212c drop unused last_successful_update field from node table (#1754) 2024-02-18 23:22:07 +01:00
Kristoffer Dalby
384ca03208 new IP allocator and add postgres to integration tests. (#1756) 2024-02-18 19:31:29 +01:00
Kristoffer Dalby
f581d4d9c0 replace linter actions with nix to ensure consistent version (#1773) 2024-02-18 13:33:40 +01:00
Kristoffer Dalby
b60ee9db54 improve errors for missing directories (#1765)
* improve errors for missing directories

Fixes #1761
Updates #1760

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update container docs

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog with /var changes

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-17 13:36:19 +01:00
Kristoffer Dalby
c73e8476b9 make database configuration change breaking (#1766)
A lot of things are breaking in 0.23 so instead of having this
be a long process, just rip of the plaster.

Updates #1758

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-17 13:18:15 +01:00
Kristoffer Dalby
6055d0b397 rollback gorm, broke migration #1755 (#1762)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-17 13:17:16 +01:00
Kristoffer Dalby
1904d79e90 rework docker tags (#1763)
* rework docker tags

This commit tries to align the new docker tags with the old schema

A prerelease will end up with the following tags:

- unstable
- v0.23.0-alpha3
- 0.23.0.alpha3
- sha-1234adsfg

A release will end up with:

- latest
- stable
- v0.23.0
- v0.23
- v0
- 0.23.0
- 0.23
- 0
- sha-1234adsfg

All of the builds will also have a `-debug` version.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-17 13:15:31 +01:00
Sandro
1b01b9e14f Reduce poll logging to debug (#1746)
Before this logged ~10 messages every few seconds and quite spamming the
log.
2024-02-16 10:32:23 +01:00
Calvin Figuereo-Supraner
5717c8255a Use result of fmt.Errorf call (#1668) 2024-02-15 11:02:54 +01:00
Kristoffer Dalby
c42f25bd72 fix ko dockerhub builds (#1751)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-15 10:23:01 +01:00
Stepan
82c64f682c Docs: fix path to nologin shell (#1610) 2024-02-15 08:43:50 +01:00
Linus
7afc2fd180 TLS documentation updates (#1733)
* TLS documentation updates

Move "Bring your own certificates" to the top
  since the letsencrypt section is now much longer, it seems wrong to
  keep such a short section way down at the bottom.

Restructure "Challenge types" into separate sections

Add technical description of letsencrypt renewals
  this aims to answer:
  - what can be expected in terms of renewals
  - what logs can be expected (none)
  - how to validate that renewal happened successfully
  - the reason for some of the 'acme/autocert' logs, or at least
    some best-effort assumptions

* +prettier
2024-02-15 08:43:01 +01:00
Kristoffer Dalby
5109af94a3 login to docker registries (#1744)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-12 16:01:56 +01:00
Kristoffer Dalby
905fdaa409 remove quotes from command (#1742)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-12 15:09:19 +01:00
Kristoffer Dalby
0333e97630 Build docker images with ko (goreleaser) (#1716)
* make dockerfiles testing only note

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* setup ko image builder for goreleaser

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove release-docker

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove non-debug Dockerfile

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Comments and lint

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

* build debug images based on debian

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

* remove debug flag for goreleaser

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2024-02-12 14:53:07 +01:00
DeveloperDragon
e3553aae50 Allow when user has only a subnet route (#1734)
* Add test because of issue 1604

* Add peer for routes

* Revert previous change to try different way to add peer

* Add traces

* Remove traces

* Make sure tests have IPPrefix comparator

* Get allowedIps before loop

* Remove comment

* Add composite literals :)
2024-02-12 11:44:37 +01:00
Pallab Pain
47405931c6 feat(apikey): adds command to delete api keys (#1702)
We currently do not have a way to clean up api keys. There may be cases
where users of headscale may generate a lot of api keys and these may
end up accumulating in the database. This commit adds the command to
delete an api key given a prefix.
2024-02-12 11:31:21 +01:00
foosinn
c4beb0b8af document setting oidc client secret cia env (#1649) 2024-02-12 09:20:31 +01:00
Kristoffer Dalby
3f2b238a46 Upgrade to Go 1.22 and update deps (#1728)
* go 1.22

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* upgrade tailscale

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

* add script for getting nix sri for go

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update deps and only build headscale

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

* add script to update all deps

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

* get golines and golangci from upstream nix

Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2024-02-12 09:11:33 +01:00
Kristoffer Dalby
68a8ecee7a Prepare notify channel before sending first update (#1730)
* create channel before sending first update

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* do not notify on register, wait for connect

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-12 09:11:17 +01:00
lööps
c3257e2146 docs(windows-client): add Windows registry command (#1658)
Add Windows registry command to create the `Tailscale IPN` path before setting properties.
2024-02-09 19:16:17 +01:00
Pallab Pain
9047c09871 feat: add pqsql configs for open and idle connections (#1583)
When Postgres is used as the backing database for headscale,
it does not set a limit on maximum open and idle connections
which leads to hundreds of open connections to the Postgres
server.

This commit introduces the configuration variables to set those
values and also sets default while opening a new postgres connection.
2024-02-09 17:34:28 +01:00
Kristoffer Dalby
91bb85e7d2 Update bug_report.md (#1672) 2024-02-09 07:27:13 +01:00
Kristoffer Dalby
94b30abf56 Restructure database config (#1700) 2024-02-09 07:27:00 +01:00
Kristoffer Dalby
00e7550e76 Add assert func for verifying status, netmap and netcheck (#1723) 2024-02-09 07:26:41 +01:00
Kristoffer Dalby
83769ba715 Replace database locks with transactions (#1701)
This commits removes the locks used to guard data integrity for the
database and replaces them with Transactions, turns out that SQL had
a way to deal with this all along.

This reduces the complexity we had with multiple locks that might stack
or recurse (database, nofitifer, mapper). All notifications and state
updates are now triggered _after_ a database change.


Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-02-08 17:28:19 +01:00
DeveloperDragon
cbf57e27a7 Login with OIDC after having been logged out (#1719) 2024-02-05 10:45:35 +01:00
derelm
4ea12f472a Fix failover to disabled route #1706 (#1707)
* fix #1706 - failover should disregard disabled routes during failover

* fixe tests for failover; all current tests assume routes to be enabled

* add testcase for #1706 - failover to disabled route
2024-02-03 15:30:15 +01:00
danielalvsaaker
b4210e2c90 Trim client secret after reading from file (#1697)
Reading from file will include a line break, which results in a mismatching client secret
compared to reading directly from the config.
2024-01-25 09:53:34 +01:00
dyz
a369d57a17 fix node expire error due to type in gorm model Update (#1692)
Fixes #1674

Signed-off-by: fortitude.zhang <fortitude.zhang@gmail.com>
2024-01-21 17:38:24 +01:00
Kristoffer Dalby
1e22f17f36 node selfupdate and fix subnet router when ACL is enabled (#1673)
Fixes #1604

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-01-18 17:30:25 +01:00
Kristoffer Dalby
65376e2842 ensure renabled auto-approve routes works (#1670) 2024-01-18 16:36:47 +01:00
Alexander Halbarth
7e8bf4bfe5 Add Customization Options to DERP Map entry of integrated DERP server (#1565)
Co-authored-by: Alexander Halbarth <alexander.halbarth@alite.at>
Co-authored-by: Bela Lemle <bela.lemle@alite.at>
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2024-01-16 16:04:03 +01:00
Kristoffer Dalby
3b103280ef implement selfupdate and pass expiry (#1647) 2024-01-05 10:41:56 +01:00
Kristoffer Dalby
a592ae56b4 fix issue where advertise tags causes hang (#1669)
Fixes #1665

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-01-04 21:26:49 +01:00
Kristoffer Dalby
054b06d45d add 1.54 and 1.56 to integration tests (#1652)
* add 1.54 and 1.56 to integration tests

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix bug where we tested random versions, now sorted

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2024-01-02 10:41:40 +01:00
Kristoffer Dalby
55ca078f22 embed (hidden) tailsql for debugging (#1663)
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2023-12-20 21:47:48 +01:00
Kristoffer Dalby
6049ec758c add versioned migrations (#1644) 2023-12-10 15:46:14 +01:00
Kristoffer Dalby
ac910fd44c make stale shorter (#1646) 2023-12-10 15:30:30 +01:00
Kristoffer Dalby
9982ae5f09 add breaking entry of derp priv key (#1641) 2023-12-10 15:23:23 +01:00
Kristoffer Dalby
cf8ffea154 turn off grpc communication logging (#1640) 2023-12-10 15:22:59 +01:00
Kristoffer Dalby
790bbe5e8d fix hostinfo db column spelling (#1642) 2023-12-10 15:22:26 +01:00
github-actions[bot]
2c8fc9b061 Update flake.lock (#1632)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-12-10 09:50:39 +01:00
github-actions[bot]
b359939812 docs(README): update contributors (#1639)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-10 08:56:01 +01:00
Kristoffer Dalby
f65f4eca35 ensure online status and route changes are propagated (#1564) 2023-12-09 18:09:24 +01:00
Kristoffer Dalby
0153e26392 upgrade go dependencies (#1628) 2023-11-30 14:41:31 +01:00
Andrei Pechkurov
6c9c55774b Update xsync to v3.0.2 (#1597)
Co-authored-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-11-29 15:47:14 +01:00
github-actions[bot]
2f558bee80 Update flake.lock (#1598)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-29 15:11:28 +01:00
Azamat H. Hackimov
4c608a4b58 Fix Github Actions docs pipeline (#1622) 2023-11-29 15:11:00 +01:00
JesseBot
f13cf64578 Docs: Update running-headscale-container.md - fix link to example config (#1618) 2023-11-29 15:10:21 +01:00
MichaelKo
85e92db505 Enhance pipeline stability and automatically retry unstable tests (#1566)
* add test retry to action

* add test retry to action
2023-11-27 18:32:52 +01:00
Kristoffer Dalby
a59aab2081 Remove support for non-noise clients (pre-1.32) (#1611) 2023-11-23 08:31:33 +01:00
Kristoffer Dalby
b918aa03fc move to use tailscfg types over strings/custom types (#1612)
* rename database only fields

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use correct endpoint type over string list

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* remove HostInfo wrapper

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* wrap errors in database hooks

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-11-21 18:20:06 +01:00
Kristoffer Dalby
ed4e19996b Use tailscale key types instead of strings (#1609)
* upgrade tailscale

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* make Node object use actualy tailscale key types

This commit changes the Node struct to have both a field for strings
to store the keys in the database and a dedicated Key for each type
of key.

The keys are populated and stored with Gorm hooks to ensure the data
is stored in the db.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use key types throughout the code

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* make sure machinekey is concistently used

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use machine key in auth url

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix web register

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* use key type in notifier

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* fix relogin with webauth

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

---------

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-11-19 22:37:04 +01:00
Kristoffer Dalby
c0fd06e3f5 remove the use key stripping and store the proper keys (#1603) 2023-11-16 17:55:29 +01:00
github-actions[bot]
2af71c9e31 docs(README): update contributors (#1592)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-06 14:07:35 +01:00
Lucalux
42b7f8f65a redundant line removed from systemd.service (#1587) 2023-11-06 13:45:34 +01:00
github-actions[bot]
48c7d763d5 Update flake.lock (#1589)
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/e12483116b3b51a185a33a272bf351e357ba9a99' (2023-09-21)
  → 'github:NixOS/nixpkgs/a0b3b06b7a82c965ae0bb1d59f6e386fe755001d' (2023-11-05)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-11-06 12:57:06 +01:00
Kristoffer Dalby
d0d6438337 Add workflow to autoupdate flake.lock deps (#1588)
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-11-06 12:48:52 +01:00
Kristoffer Dalby
fb4ed95ff6 Upgrade Go 1.21, Tailscale 1.50 and add Capability version support (#1563) 2023-09-28 12:33:53 -07:00
Kristoffer Dalby
01b85e5232 remove readonly case for mapresponse, dont think it is used (#1556) 2023-09-25 14:27:24 -07:00
Kristoffer Dalby
64c0a6523f Set online status in lite requests (#1555) 2023-09-25 14:27:14 -07:00
Kristoffer Dalby
84fbca97f7 add note about db backup to changelog (#1560) 2023-09-25 14:27:03 -07:00
Kristoffer Dalby
56cf4b082e Add github stale action (#1559) 2023-09-25 09:33:31 -05:00
github-actions[bot]
6cd0f77511 docs(README): update contributors (#1558)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-09-24 14:34:53 -07:00
Kristoffer Dalby
b27e8ab5a1 add 0.23.0 changelog entry (#1557) 2023-09-24 14:18:19 -07:00
Juan Font
0030af3fa4 Rename Machine to Node (#1553) 2023-09-24 06:42:05 -05:00
Kristoffer Dalby
096ac31bb3 handle route updates correctly
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
c957f893bd Return simple responses immediatly
This commit rearranges the poll handler to immediatly accept
updates and notify its peers and return, not travel down the
function for a bit. This reduces the DB calls and other
holdups that isnt necessary to send a "lite response", a
map response without peers, or accepting an endpoint update.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
217ccd6540 improve debug logging, rw lock for notifier
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
3bef63bb80 Remove LastSuccessfulUpdate from Machine
This field is no longer used, it was used in our old state
"algorithm" to determine if we should send an update.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
591ff8d347 add pprof endpoint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
14f8c1ba34 order path
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
ca4a48afbb gitignore infolder tailscale
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
9ccf87c566 add lock around saving ts clients
Closes #1544

Co-Authored-By: Patrick Huang <huangxiaoman@gmail.com>
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
4c12c02e71 Upgrade go and debian in headscale docker
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
2434d76ade give ci more tollerance for timeouts
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
432e975a7f move MapResponse peer logic into function and reuse
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
387aa03adb Remove database from Mapper
This commit changes the internals of the mapper to
track all the changes to peers over its lifetime.

This means that it no longer depends on the database
and this should hopefully help with locks and timing issues.
When the mapper is created, it needs the current list of peers,
the world view, when the polling session was started. Then as
update changes are called, it tracks the changes and generates
responses based on its internal list.

As a side, the types.Machines and types.MachinesP, as well as
types.Machine being passed as a full struct and pointer has been
changed to always be pointers, everywhere.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
3b0749a320 Update packetfilter when peers change
Previously we did not update the packet filter
when nodes changed, which would cause new nodes
to be missing from packet filters of old nodes.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
a8079a2096 rearrange poll, lock, notify
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
593b3ad981 filter out peers without endpoints
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
e90a669951 remove retries for pings in tsic
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
9c5301ee2e add maprequest to all mapper calls
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
13a7285658 fix lint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
e55fe0671a only send lite map responses when omitpeers
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
e0ba325b3b additional debug logging, use mapper pointer
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
eff529f2c5 introduce rw lock for db, ish...
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
a1a3ff4ba8 disable online map by default for now
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
78268d78a0 add debug option to save all map responses
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
f73172fb21 add less/jq to hs debug container
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
b7c6e0ec88 add annoying linter to golangci
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
2d87085cbc rearrange channel closing defers
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
13fe4ec91b add script to run integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
53a9e28faf Add missing return in shutdown
Co-Authored-By: Jason <armooo@armooo.net>
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
4b65cf48d0 Split up MapResponse
This commits extends the mapper with functions for creating "delta"
MapResponses for different purposes (peer changed, peer removed, derp).

This wires up the new state management with a new StateUpdate struct
letting the poll worker know what kind of update to send to the
connected nodes.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
66ff1fcd40 Replace the timestamp based state system
This commit replaces the timestamp based state system with a new
one that has update channels directly to the connected nodes. It
will send an update to all listening clients via the polling
mechanism.

It introduces a new package notifier, which has a concurrency safe
manager for all our channels to the connected nodes.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
056d3a81c5 format with prettier 3.0
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-19 10:20:21 -05:00
Kristoffer Dalby
7edc953d35 Update tsic.go 2023-09-05 08:47:43 +02:00
Kristoffer Dalby
12a04f9459 fix relogin test, pass accept route flag
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Kristoffer Dalby
1766e6b5df General fixups discovered by checking errors
There was a lot of tests that actually threw a lot of errors and that did
not pass all the way because we didnt check everything. This commit should
fix all of these cases.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Kristoffer Dalby
f8a58aa15b introduce a version subset we must test against
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Kristoffer Dalby
b4a4d0f760 Handle errors in integration test setups
Thanks @kev-the-dev

Closes #1460

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Kristoffer Dalby
63caf9a222 update flake, fix prettier lint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Kristoffer Dalby
47255d267e add script to run integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-09-05 08:47:43 +02:00
Juan Font
e3acc95859 Send logs to stderr, rather than stdout 2023-08-11 20:59:38 +02:00
qzydustin
fb203a2e45 Format code 2023-07-23 23:46:02 +02:00
qzydustin
6567af7730 Fix IP Address Order Bug 2023-07-23 23:46:02 +02:00
Kristoffer Dalby
23a3adf8d2 use cmp.Diff instead of reflect.DeepEqual
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-07-01 21:55:47 +02:00
Kristoffer Dalby
665a3cc666 add generic logerr func to shorten code
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-07-01 08:35:04 +01:00
Kristoffer Dalby
fe75b71620 use nix caching and docker caching in CI
Also bumps tailscale version to trigger build and fixes a CLI test
that had the wrong capitalisation

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-29 12:49:36 +01:00
Kristoffer Dalby
19dc0ac702 rename acl "get" funcs to "expand" for consistency
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
155cc072f7 migrate last acl tests away from database
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
e2c08db3b5 reduce filter rules at the end, so we filter nodes correctly
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
fcdc7a6f7d remove redundant tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
88ca2501d1 only send relevant filterrules to nodes
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
2675ff4b94 make parse destination string into a func
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
717abe89c1 remove "stripEmailDomain" argument
This commit makes a wrapper function round the normalisation requiring
"stripEmailDomain" which has to be passed in almost all functions of
headscale by loading it from Viper instead.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
161243c787 make generateFilterRules take machine and peers
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
9c425a1c08 Finish SSH
This commit allows SSH rules to be assigned to each relevant not and
by doing that allow SSH to be rejected, completing the initial SSH
support.

This commit enables SSH by default and removes the experimental flag.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
db6cf4ac0a make GenerateFilterRules take machine and peers
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Kristoffer Dalby
35770278f7 upgrade tailscale
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-21 10:31:48 +02:00
Viacheslav Sychov
36c9b5ce74 Adjust the template for the OIDC callback login page 2023-06-14 18:08:49 +02:00
Kristoffer Dalby
0562260fe0 rename handler files
This commit renames a bunch of files to try to make it a bit less confusing;

protocol_ is now auth as they contained registration, auth and login/out flow
protocol_.*_poll is now poll.go
api.go and other generic handlers are now handlers.go

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
c1218ad3c2 move reminder of dns funcs to util
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
d36336a572 fix lint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
80ea87c032 move derp_server to derp server module
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
8c4c4c8633 move derp.go to derp module
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
2289a2acbf move Config definitions into types
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Viacheslav Sychov
c72401a99b Fix IPv6 in ACLs 2023-06-08 16:34:15 +02:00
Kristoffer Dalby
725bbd7408 Remove variables and leftovers of pregenerated ACL content
Prior to the code reorg, we would generate rules from the Policy and
store it on the global object. Now we generate it on the fly for each node
and this commit cleans up the old variables to make sure we have no
unexpected side effects.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
084d1d5d6e Add initial test for mapresponse
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
f9f6e1557a Remove complicated testcase obsoleated by tailNode test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
5bad48a24e remove DB dependency of tailNode conversion, add test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
bce8427423 Map route into machine
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
Kristoffer Dalby
f7f472ae07 introduce mapper package
The mapper package contains functions related to creating and marshalling
reponses to machines.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-06-08 16:34:15 +02:00
github-actions[bot]
699655a93f docs(README): update contributors 2023-05-27 16:21:42 +02:00
Kristoffer Dalby
feb15365b5 Split code into modules
This is a massive commit that restructures the code into modules:

db/
    All functions related to modifying the Database

types/
    All type definitions and methods that can be exclusivly used on
    these types without dependencies

policy/
    All Policy related code, now without dependencies on the Database.

policy/matcher/
    Dedicated code to match machines in a list of FilterRules

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-26 12:24:50 +02:00
Kristoffer Dalby
14e29a7bee create DB struct
This is step one in detaching the Database layer from Headscale (h). The
ultimate goal is to have all function that does database operations in
its own package, and keep the business logic and writing separate.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-05-26 12:24:50 +02:00
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
Kristoffer Dalby
64ebe6b0c8 change date in changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 08:13:38 +02:00
Kristoffer Dalby
e6b26499f7 release source code with vendored dependencies
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-20 08:13:38 +02:00
Kristoffer Dalby
977eb1dee3 Update flakes, add some quality of life improvements (#1346) 2023-04-20 07:56:53 +02:00
Kristoffer Dalby
b2e2b02210 set release date
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:47:31 +02:00
Kristoffer Dalby
2abff4bb08 update changelog for #1339
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:45:27 +02:00
Kristoffer Dalby
54c00645d1 update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
cad5ce0ebd lint fix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
b12a167fa2 remove rpm, might add back later
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
667295e15e add new documentation on how to install on debian/ubuntu
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
bea52678e3 move current linux documentation into "manual"
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
307cfc3304 add systemd enable to postinstall script
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-19 20:04:58 +02:00
Kristoffer Dalby
5e74ca9414 Fix IPv6 in ACLs (#1339) 2023-04-16 12:26:35 +02:00
Juan Font
9836b097a4 Make sure all clients of a user are ready (#1335) 2023-04-12 09:25:51 +02:00
Juan Font
d0b3b1bfc4 Fix binary releases 2023-04-08 09:21:27 +02:00
Juan Font
6eea96eabc Added 1.38.4 in the new tests 2023-04-07 19:45:46 +02:00
github-actions[bot]
d08fee78c3 docs(README): update contributors (#1325) 2023-04-07 17:31:29 +02:00
Andriy Kushnir (Orhideous)
bb5f0d456c Change primary color for light mode to white 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
c186c49e25 Removed custom accents, going with defaults 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
4ec6894773 Build with strict mode 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
dd9b4b1cb7 Move examples out of docs/ directory 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
a43bb9c958 Replace placeholder link with actual one 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
ba905ff6fc Add GHA CI to build and deploy docs 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
99bd09f688 Add new index page 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
a6bc792a61 Move admonitions to relevant sections 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
6381d3660a Add admonitions marking community-provided docs 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
66c5f74d78 Add admonitions marking community-provided docs 2023-04-07 15:24:13 +02:00
Andriy Kushnir (Orhideous)
1723a6bf40 Configure MkDocs Material scaffold 2023-04-07 15:24:13 +02:00
Juan Font
353f191e4f Update changelog 2023-04-07 13:25:34 +02:00
Juan Font
8d865bb61b Target Go 1.20 in flake.nix 2023-04-07 13:25:34 +02:00
Juan Font
c6815c5334 Target Go 1.20 and Tailscale 1.38 2023-04-07 13:25:34 +02:00
Kristoffer Dalby
b684ac0668 Simplify goreleaser, package deb and rpm
This commit simplifies the goreleaser configuration and then adds nfpm
support which allows us to build .deb and .rpm for each of the ARCH we
support.

The deb and rpm packages adds systemd services and users, creates
directories etc and should in general give the user a working
environment. We should be able to remove a lot of the complicated,
PEBCAK inducing documentation after this.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-04-07 11:06:42 +02:00
Juan Font
dfc5d861c7 Fix CIDR calculation in expandACLPeerAddr 2023-04-05 09:44:46 +02:00
Juan Font
50b706eeed Remove deprecated linters + one casuing issues with imports 2023-04-04 22:37:27 +02:00
Sean Reifschneider
036ff1cbb9 Adding Powershell commands to Windows instructions (#1299)
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2023-04-04 08:58:32 +02:00
Kristoffer Dalby
ceeef40cdf Add tests to verify "Hosts" aliases in ACL (#1304) 2023-04-03 10:08:48 +02:00
Julien Zweverink
681c86cc95 ACL Doc's (#1288) 2023-03-28 18:41:23 +02:00
Kristoffer Dalby
c7b459b615 Fix issue where ACL * would filter out returning connections (#1279) 2023-03-27 19:19:32 +02:00
Gabe Cook
56a7b1e349 Add SVG logos (#1286) 2023-03-27 15:33:25 +02:00
Antonio Fernandez
f1eee841cb updated to ACL doc (#1278) 2023-03-27 11:25:55 +02:00
Stefan Majer
45fbd34480 Do not use yaml.v2 and yaml.v3 as direct dependency (#1281) 2023-03-27 10:48:39 +02:00
Juan Font
248abcf353 Add missing entry to changelog and prepare for 0.22
Add missing entry to changelog
2023-03-20 13:48:56 +01:00
Kurnia D Win
2560c32378 adding some sleep on re-registration after machine expired (#1256) 2023-03-20 11:14:34 +01:00
Kristoffer Dalby
e38efd3cfa Add ACL test for limiting a single port. (#1258) 2023-03-20 08:52:52 +01:00
Moritz Poldrack
d12f247490 document running exit nodes
Currently the only kind-of documentation is #210 which is outdated. To remedy
this, add a document describing the process.
2023-03-19 11:02:01 +01:00
nicholas-yap
003036a779 Update iOS compatibility and added iOS docs (#1264) 2023-03-17 15:56:15 +01:00
github-actions[bot]
ed79f977a7 docs(README): update contributors (#1262) 2023-03-17 10:07:39 +01:00
Kristoffer Dalby
8012e1cbd2 Add instructions on how to login to iOS (#1261) 2023-03-15 11:31:38 +00:00
Kristoffer Dalby
a5562850a7 MapResponse optimalisations, peer list integration tests (#1254)
Co-authored-by: Allen <979347228@qq.com>
2023-03-06 17:50:26 +01:00
Stefan Majer
bb786ac8e4 github.com/gofrs/uuid/v5 is now go modules compatible, use it (#1224) 2023-03-06 09:54:24 +01:00
Juan Font
ea82035222 Allow to delete routes (#1244) 2023-03-06 09:05:40 +01:00
Albert Copeland
c9ecdd6ef1 Add Graphical Control Panels section to README (#1226) 2023-03-03 19:05:12 +01:00
Juan Font
54f5c249f1 Fix various linting issues + golang-lint upgrade (#1245) 2023-03-03 18:22:47 +01:00
dnaq
a82a603db6 Return 404 on unmatched routes (#1201) 2023-03-03 17:14:30 +01:00
Sean Reifschneider
f49930c514 Add "configtest" CLI command. (#1230)
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
Fixes https://github.com/juanfont/headscale/issues/1229
2023-03-03 14:55:29 +01:00
Kristoffer Dalby
2baeb79aa0 changelog: prep for 0.21 (#1246) 2023-03-03 13:42:45 +01:00
Kristoffer Dalby
b3f78a209a Post PR comment when nix vendor sum breaks
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-03-02 18:17:59 +01:00
Josh Taylor
5e6868a858 Run prettier 2023-02-27 10:28:49 +01:00
Josh Taylor
5caf848f94 Add steps for Google OAuth for OIDC 2023-02-27 10:28:49 +01:00
Juan Font
3e097123bf Target ts 1.36 in integration tests 2023-02-26 15:35:27 +01:00
Juan Font
74447b02e8 Target Tailscale 1.36 when building 2023-02-26 15:35:27 +01:00
Juan Font
20e96de963 Update dependencies 20230226 2023-02-26 14:39:37 +01:00
Juan Font
7c765fb3dc Update prettier action
Update prettier action
2023-02-26 13:54:30 +01:00
Michael Savage
dcc246c869 Fix OpenBSD build docs
- OpenBSD 7.2 installs go 1.19 by default
- Doubt you can run nix so skip the makefile/nix build and just go build directly
2023-02-26 12:54:47 +01:00
Àlex Torregrosa
cf7767d8f9 Add css to the /windows and /apple templates (#1211)
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2023-02-09 08:49:05 -05:00
Maxim Gajdaj
61c578f82b Update running-headscale-linux.md
Option WorkingDirectory for headscale.service added
2023-02-09 08:17:28 -05:00
Kristoffer Dalby
6950ff7841 Add list of talks to the readme
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-07 17:04:14 +01:00
Kristoffer Dalby
e65ce17f7b Add documentation to integration test framework
so tsic, hsic and scenario

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 16:25:58 +01:00
Kristoffer Dalby
b190ec8edc Add section about running locally
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 16:25:58 +01:00
Kristoffer Dalby
c39085911f Add node expiry test
This commits adds a test to verify that nodes get updated if a node in
their network expires.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
3c20d2a178 Update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
9187e4287c Remove unused components from old integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
2b7bcb77a5 Stop using deprecated string function
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
97a909866d Use pingAll helper for all integration pinging
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
feeb5d334b Populate the tags field on node
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
a840a2e6ee Sort tailcfg.Node creation as upstream
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
4183345020 Do not collect services, we dont support it
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
50fb7ad6ce Add TODOs for only sending patch updates
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
88a9f4b44c Send control time in map response
This gives all the nodes the same constant time to work from

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-03 09:26:22 +01:00
Kristoffer Dalby
00fbd8dd93 Remove all tests before generating new ones
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-02 17:55:19 +01:00
Kristoffer Dalby
ce587d2421 Update test workflows
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-01 10:58:37 +01:00
Kristoffer Dalby
e1eb30084d Remove new line at start of test template
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-01 10:58:37 +01:00
Kristoffer Dalby
673638afe7 Use ripgrep to find list of tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-02-01 10:58:37 +01:00
Kristoffer Dalby
da48cf64b3 Set OpenID Connect Expiry
This commit adds a default OpenID Connect expiry to 180d to align with
Tailscale SaaS (previously infinite or based on token expiry).

In addition, it adds an option use the expiry time from the Token sent
by the OpenID provider. This will typically cause really short expiry
and you should only turn on this option if you know what you are
desiring.

This fixes #1176.

Co-authored-by: Even Holthe <even.holthe@bekk.no>
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-31 18:55:16 +01:00
Dominic Bevacqua
385fd93e73 Update changelog 2023-01-31 00:15:48 +01:00
Dominic Bevacqua
26edf24477 Allow split DNS configuration without requiring global nameservers
Align behaviour of dns_config.restricted_nameservers to tailscale.

Tailscale allows split DNS configuration without requiring global nameservers.

In addition, as per [the docs](https://tailscale.com/kb/1054/dns/#using-dns-settings-in-the-admin-console):

> These nameservers also configure search domains for your devices

This commit aligns headscale to tailscale by:

 * honouring dns_config.restricted_nameservers regardless of whether any global resolvers are configured
 * adding a search domain for each restricted_nameserver
2023-01-31 00:15:48 +01:00
Kristoffer Dalby
83a538cc95 Rename IP specific function, add missing test case
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-30 15:56:38 +01:00
Kristoffer Dalby
cffa040474 Cancel old builds if new commits appear
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-30 14:57:10 +01:00
Kristoffer Dalby
727d95b477 Improve generated integration tests
- Save logs from control(headscale) on every run to tmp
- Upgrade nix-actions
- Cancel builds if new commit is pushed
- Fix a sorting bug in user command test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-30 14:43:03 +01:00
Juan Font
640bb94119 Do not show IsPrimary field as false in exit nodes 2023-01-29 14:54:09 +01:00
Juan Font
0f65918a25 Update tests
Fixed linting
2023-01-29 12:25:37 +01:00
Juan Font
3ac2e0b253 Enable both exit node routes (IPv4 and IPv6) at the same time.
As indicated by bradfitz in https://github.com/juanfont/headscale/issues/804#issuecomment-1399314002,
both routes for the exit node must be enabled at the same time. If a user tries to enable one of the exit node routes,
the other gets activated too.

This commit also reduces the API surface, making private a method that didnt need to be exposed.
2023-01-29 12:25:37 +01:00
Juan Font
b322cdf251 Updated changelog for v0.20.0 2023-01-29 11:46:37 +01:00
Johan Siebens
e128796b59 use smallzstd and sync pool 2023-01-27 12:03:24 +01:00
Kristoffer Dalby
6d669c6b9c Migrate namespace_id to user_id column in machine and pak
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-26 11:07:26 +01:00
Kristoffer Dalby
8dadb045cf Mark -n and --namespace as deprecated
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-26 10:22:38 +01:00
Christian Heusel
9f6e546522 modify the test to reflect the changes on the webinterface
related to 2d44a1c99c17

Signed-off-by: Christian Heusel <christian@heusel.eu>
2023-01-26 08:33:44 +01:00
Juan Font
9714900db9 Target Tailscale 1.36.0 2023-01-26 07:50:03 +01:00
Jan Hartkopf
cb25f0d650 Add hint for reverse proxying with Apache 2023-01-23 15:51:20 +01:00
caelansar
9c2e580ab5 put Where before Find 2023-01-20 10:50:29 +01:00
Christian Heusel
0ffff2c994 Update the node join instruction to reference "username"
related to https://github.com/juanfont/headscale/pull/1144

Signed-off-by: Christian Heusel <christian@heusel.eu>
2023-01-20 09:50:49 +01:00
Christian Heusel
c720af66d6 permalink in the limitations section to tailscale
The relative link was broken after one commit to the file

Signed-off-by: Christian Heusel <christian@heusel.eu>
2023-01-20 09:19:26 +01:00
Kristoffer Dalby
86a7129027 Update changelog, more explicit backup note
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-19 12:54:34 +01:00
Kristoffer Dalby
9eaa8dd049 Migrate DB: rename table is plural, order matters
The calls to AutoMigrate to other classes that refer to users will
create the table and it will break, it needs to be done before
everything else.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-19 12:54:34 +01:00
Kristoffer Dalby
81441afe70 update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
f19e8aa7f0 Fix failing tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
90287a6735 gofumpt
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
fb3e2dcf10 Rename namespace to user in docs
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
bf0b85f382 Rename acl test file
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
5da0963aac Migrate DB: rename namespace, automigrate user
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
da5c051d73 Lint fix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
b98bf199dd Regenerate go from proto
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
428d7c86ce Rename namespace in protobuf files
While this truly breaks the point of the backwards compatible stuff with
protobuf, it does not seem worth it to attempt to glue together a
compatible API.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
af1ec5a593 Rename .go namespace files
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Kristoffer Dalby
e3a2593344 Rename [Nn]amespace -> [Uu]ser in go code
Use gopls, ag and perl to rename all occurances of Namespace

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-18 15:40:04 +01:00
Motiejus Jakštys
bafb6791d3 oidc: allow reading the client secret from a file
Currently the most "secret" way to specify the oidc client secret is via
an environment variable `OIDC_CLIENT_SECRET`, which is problematic[1].
Lets allow reading oidc client secret from a file. For extra convenience
the path to the secret will resolve the environment variables.

[1]: https://systemd.io/CREDENTIALS/
2023-01-14 17:03:57 +01:00
Motiejus Jakštys
6edac4863a Makefile: remove a missing target
test_integration_oidc was removed in 0525bea593
2023-01-14 13:42:48 +01:00
Even Holthe
e27e01c09f nodes list: expose expiration time 2023-01-12 13:43:21 +01:00
Even Holthe
dd173ecc1f Refresh machines with correct new expiry 2023-01-12 13:43:21 +01:00
Kristoffer Dalby
8ca0fb7ed0 update ip_prefixes docs
we cant actually have arbitrary ip ranges, add a note about that.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-12 11:39:39 +01:00
Juan Font
6c714e88ee Added entry for performance improvements in ACLs 2023-01-11 08:58:03 +01:00
Allen
a6c8718a97 ToStringSlice will lead to high CPU usage, early conversion can reduce cpu usage 2023-01-11 08:45:54 +01:00
Even Holthe
26282b7a54 Fix SIGSEGV crash related to map of state changes
See https://github.com/juanfont/headscale/issues/1114#issuecomment-1373698441
2023-01-10 22:26:21 +01:00
Kristoffer Dalby
93aca81c1c Read integration test config from Env
This commit sets the Headscale config from env instead of file for
integration tests, the main point is to make sure that when we add per
test config, it properly replaces the config key and not append it or
something similar.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 23:06:43 +01:00
Kristoffer Dalby
81254cdf7a Limit run regex for generated workflows
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 18:36:31 +01:00
Kristoffer Dalby
b3a0c4a63b Add integration readme
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 12:32:24 +01:00
Kristoffer Dalby
376235c9de make prettier ignore generated test flows
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 12:32:24 +01:00
Kristoffer Dalby
7274fdacc6 Generate github action jobs for integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 12:32:24 +01:00
Kristoffer Dalby
91c1f54b49 Remove "run all v2 job"
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 12:32:24 +01:00
Kristoffer Dalby
efd0f79fbc Add script to generate integration test gitjobs
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2023-01-06 12:32:24 +01:00
Juan Font
2084464225 Update CHANGELOG.md
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2023-01-05 14:59:02 +01:00
Juan Font
66ebbf3ecb Preload AuthKey in machine getters 2023-01-05 14:59:02 +01:00
Juan Font
55a3885614 Added integration tests for ephemeral nodes
Fetch the machines from headscale
2023-01-05 14:59:02 +01:00
Juan Font
afae1ff7b6 Delete ephemeral machines on logout
Update changelog

Use dedicated method to delete
2023-01-05 14:59:02 +01:00
Juan Font
4de49f5f49 Add isEphemeral() method to Machine 2023-01-05 14:59:02 +01:00
Even Holthe
6db9656008 oidc: update changelog 2023-01-04 09:23:52 +01:00
Even Holthe
fecb13b24b oidc: add basic docs 2023-01-04 09:23:52 +01:00
Even Holthe
23a595c26f oidc: add test for expiring nodes after token expiration 2023-01-04 09:23:52 +01:00
Even Holthe
085912cfb4 expire machines after db expiry 2023-01-04 09:23:52 +01:00
Even Holthe
7157e14aff add expiration from OIDC token to machine 2023-01-04 09:23:52 +01:00
Allen
4e2c4f92d3 reflect.DeepEqual is a value copy that causes golang to continuously allocate memory 2023-01-03 18:09:18 +01:00
Juan Font
893b0de8fa Added tests on allowedip field for routing 2023-01-03 13:34:55 +01:00
Juan Font
9b98c3b79f Send in AllowedIPs both primary routes AND enabled exit routes 2023-01-03 13:34:55 +01:00
Even Holthe
6de26b1d7c Remove Tailscale v1.18.2 from test matrix 2023-01-02 16:06:12 +01:00
Christian Heusel
1f1931fb00 fix spelling mistakes 2023-01-01 22:45:16 +01:00
Christian Heusel
1f4efbcd3b add changelog entry 2023-01-01 22:45:16 +01:00
Christian Heusel
711fe1d806 enumerate the config 2023-01-01 22:45:16 +01:00
Christian Heusel
e2c62a7b0c document how to add new DNS records via extra_records 2023-01-01 22:45:16 +01:00
Christian Heusel
ab6565723e add the possibility for custom DNS records
related to https://github.com/juanfont/headscale/issues/762

Co-Authored-By: Jamie Greeff <jamie@greeff.me>
Signed-off-by: Christian Heusel <christian@heusel.eu>
2023-01-01 22:45:16 +01:00
John Axel Eriksson
7bb6f1a7eb domains/restricted_nameservers: check dnsConfig.Resolvers instead of dnsConfig.Nameservers 2022-12-31 19:06:32 +01:00
Avirut Mehta
549b82df11 Add Caddy instructions to reverse_proxy.md 2022-12-27 23:08:34 +01:00
Marc
036cdf922f templates: fix typo "custm" -> "custom" 2022-12-27 12:02:33 +01:00
jimyag
b4ff22935c update macos check 2022-12-25 15:45:45 +01:00
ma6174
5feadbf3fc fix goroutine leak 2022-12-25 14:11:16 +01:00
Juan Font
3e9ee816f9 Add integration tests for logout with authkey 2022-12-22 20:02:18 +01:00
Juan Font
2494e27a73 Make WaitForTailscaleLogout a Scenario method 2022-12-22 20:02:18 +01:00
Juan Font
8e8b65bb84 Add ko-fi sponsor button 2022-12-22 17:25:49 +01:00
Juan Font
b7d7fc57c4 Add logout method to tsic 2022-12-22 00:09:21 +01:00
Juan Font
b54c0e3d22 Add integration tests that check logout and relogin 2022-12-21 20:52:08 +01:00
Juan Font
593040b73d Run the Noise handlers under a new struct so we can access the noiseConn from the handlers
In TS2021 the MachineKey can be obtained from noiseConn.Peer() - contrary to what I thought before,
where I assumed MachineKey was dropped in TS2021.

By having a ts2021App and hanging from there the TS2021 handlers, we can fetch again the MachineKey.
2022-12-21 20:52:08 +01:00
Juan Font
6e890afc5f Minor linting fixes 2022-12-21 08:28:53 +01:00
Fatih Acar
2afba0233b fix(routes): ensure routes are correctly propagated
When using Tailscale v1.34.1, enabling or disabling a route does not
effectively add or remove the route from the node's routing table.
We must restart tailscale on the node to have a netmap update.

Fix this by refreshing last state change so that a netmap diff is sent.

Also do not include secondary routes in allowedIPs, otherwise secondary
routes might be used by nodes instead of the primary route.

Signed-off-by: Fatih Acar <facar@scaleway.com>
2022-12-20 15:39:59 +01:00
Anoop Sundaresh
91900b7310 Update remote-cli.md
Fixing the local binary path
2022-12-19 19:16:48 +01:00
Juan Font
55b198a16a Clients are offline when expired 2022-12-19 15:56:12 +01:00
Juan Font
ca37dc6268 Update changelog 2022-12-15 00:13:53 -08:00
Juan Font
000c02dad9 Show online in CLI & API when isOnline() reports so 2022-12-15 00:13:53 -08:00
Juan Font
4532915be1 Refresh autogenerated grpc stuff 2022-12-15 00:13:53 -08:00
Juan Font
4b8d6e7c64 Include online field in proto for machine 2022-12-15 00:13:53 -08:00
Kristoffer Dalby
579c5827b3 regenerate proto with new plugin
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-14 00:05:29 -08:00
Kristoffer Dalby
01628f76ff upgrade grpc-gateway plugin
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-14 00:05:29 -08:00
Kristoffer Dalby
53858a32f1 dont fail docker if nothing to delete
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-13 23:13:23 -08:00
Juan Font Alonso
2bf576ea8a Disable Tailscale 1.16 in integration tests 2022-12-09 19:11:24 +01:00
github-actions[bot]
1faac0b3d7 docs(README): update contributors 2022-12-07 15:18:37 +01:00
Kristoffer Dalby
134c72f4fb Set db_ssl to false by default, fixes #1043
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-07 14:58:47 +01:00
Zachary Newell
70f2f5d750 Added an OIDC AllowGroups option for authorization. 2022-12-07 08:53:16 +01:00
Kristoffer Dalby
4453728614 Murder docker container and network before run
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-06 08:52:21 +01:00
Juan Font
34107f9a0f Updated changelog 2022-12-06 08:17:14 +01:00
Juan Font
52862b8a22 Port integration tests routes CLI to v2
Fix options signature
2022-12-06 08:17:14 +01:00
Juan Font
946d38e5d7 Minor linting fixes
Remove magic number (base10...)
2022-12-06 08:17:14 +01:00
Juan Font
78819be03c Use the new routes API from the CLI 2022-12-06 08:17:14 +01:00
Juan Font
34631dfcf5 Refactored route grpc glue code 2022-12-06 08:17:14 +01:00
Juan Font
8fa9755b55 Updated generated pb code
Update swagger
2022-12-06 08:17:14 +01:00
Juan Font
1b557ac1ea Update protobuf definitions + support methods for the API
Add more logging

Updated protos with new routes API
2022-12-06 08:17:14 +01:00
Juan Font
8170f5e693 Removed unused code and linting fixes
Another bunch of gosec/golint related fixes

Remove method no longer used
2022-12-06 08:17:14 +01:00
Juan Font
a506d0fcc8 Run handlePrimarySubnetFailover() with a ticker when Serve 2022-12-06 08:17:14 +01:00
Juan Font
6718ff71d3 Added helper methods for subnet failover + unit tests
Added method to perform subnet failover

Added tests for subnet failover
2022-12-06 08:17:14 +01:00
Juan Font
b62acff2e3 Refactor machine.go, and move functionality to routes.go + unit tests
Port routes tests to new model

Mark as primary the first instance of subnet + tests

In preparation for subnet failover, mark the initial occurrence of a subnet as the primary one.
2022-12-06 08:17:14 +01:00
Juan Font
ac8bff716d Call processMachineRoutes when a new Map is received 2022-12-06 08:17:14 +01:00
Juan Font
fba77de4eb Add Route DB model and migration from existing field
Add migration from Machine.EnabledRoutes to the new Route table

Cleanup route.go and add helper methods to process HostInfo
2022-12-06 08:17:14 +01:00
github-actions[bot]
d1bca105ef docs(README): update contributors 2022-12-05 22:05:25 +01:00
Juan Font
6c2d6fa302 Do not explicitly set the protocols when ommited in ACL 2022-12-05 21:45:18 +01:00
Kristoffer Dalby
1015bc3e02 Upgrade to Tailscale 1.34.0
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
68c72d03b5 Prep changelog for new release
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
bd4b2da06e Add changelog entry to correct version
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
7b8cf5ef1a Add 1.34.0 to integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
638a3d48ec fix nix run
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
4de676c64e Add instructions for macOS GUI
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
a58a552f0e Update macos/windows doc
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 20:41:15 +01:00
Kristoffer Dalby
0db16c7bbe Update nix deps, get go 1.19.3 in
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 10:40:17 +01:00
Kristoffer Dalby
06f7e7cfd8 Tag dockerfiles to minor version so we dont have to care about patch
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 10:40:17 +01:00
Kristoffer Dalby
19f12f94c0 Make goreleaser use Nix
Eliminate one more place to make sure we use the same go version

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 10:40:17 +01:00
Kristoffer Dalby
95d3062c21 Add github action updater
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 10:40:17 +01:00
Kristoffer Dalby
86fa136a63 Upgrade go dependencies
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-05 10:40:17 +01:00
Juan Font
89c12072ba added changelog for 0.17.1 2022-12-03 16:34:23 +01:00
Juan Font
54f701ff92 generateACLPolicy() no longer a Headscale method 2022-12-03 15:43:40 +01:00
Juan Font
5a70ea7326 Correct typo on standalone (fixes #1021) 2022-12-01 17:01:20 +01:00
Kristoffer Dalby
63cd3122e6 Add breaking change about noise private path
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-01 14:47:19 +01:00
Kristoffer Dalby
6f4c6c1876 Ignore tparallel where it doesnt make sense
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-12-01 14:45:11 +01:00
Kristoffer Dalby
eb072a1a74 mark some changes as more important
Signed-off-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-11-26 12:01:12 +01:00
Kristoffer Dalby
36b8862e7c Add notes about current ssh status
Signed-off-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-11-26 11:53:31 +01:00
Even Holthe
d4e3bf184b Add experimental flag to unit test 2022-11-26 11:53:31 +01:00
Even Holthe
c28ca27133 Add SSH ACL to changelog 2022-11-26 11:53:31 +01:00
Kristoffer Dalby
c02e105065 Mark the flag properly experimental
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
22da5bfc1d Enable SSH for tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
c6d31747f7 Add feature flag for SSH, and warning
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
91ed6e2197 Allow WithEnv to be passed multiple times
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
d71aef3b98 Mark all tests with Parallel
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
8a79c2e7ed Do not retry on permission denied in ssh
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
f34e7c341b Strip newline from hostname
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
e28d308796 Add negative tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Even Holthe
f610be632e SSH: add test between namespaces 2022-11-26 11:53:31 +01:00
Even Holthe
fd6d25b5c1 SSH: Lint and typos 2022-11-26 11:53:31 +01:00
Kristoffer Dalby
3695284286 Make simple initial test case
This commit makes the initial SSH test a bit simpler:

- Use the same pattern/functions for all clients as other tests
- Only test within _one_ namespace/user to confirm the base case
- Use retry function, same as taildrop, there is some funky going on
  there...

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
cfaa36e51a Add method to expose container id
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Kristoffer Dalby
d207c30949 Ensure we have ssh in container
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-26 11:53:31 +01:00
Even Holthe
519f22f9bf SSH integration test setup 2022-11-26 11:53:31 +01:00
Even Holthe
52a323b90d Add SSH capability advertisement
Advertises the SSH capability, and parses the SSH ACLs to pass to the
tailscale client. Doesn’t support ‘autogroup’ ACL functionality.

Co-authored-by: Daniel Brooks <db48x@headline.com>
2022-11-26 11:53:31 +01:00
github-actions[bot]
91559d0558 docs(README): update contributors 2022-11-25 08:58:13 +01:00
Orville Q. Song
25195b8d73 Update CHANGELOG.md 2022-11-24 16:13:47 +01:00
Orville Q. Song
e69176e200 Tweak 2022-11-24 16:13:47 +01:00
Orville Q. Song
d29d0222af Add a note about the db_ssl field in the example config file 2022-11-24 16:13:47 +01:00
Orville Q. Song
72b9803a08 Change DBssl to string 2022-11-24 16:13:47 +01:00
Kristoffer Dalby
99e33181b2 Make displayName include basedomain if set
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-22 19:16:58 +01:00
Kristoffer Dalby
e7f322b9b6 Mark all tests to run in parallel
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-22 13:18:58 +01:00
Juan Font
1d36e1775f Remove OIDC action 2022-11-21 22:07:27 +01:00
Juan Font
0525bea593 Remove legacy OIDC tests 2022-11-21 22:07:27 +01:00
Juan Font
2770c7cc07 Initial proposal for better routing 2022-11-21 21:58:22 +01:00
Juan Font
1b0e80bb10 Add OIDC integration tests
* Port OIDC integration tests to v2

* Move Tailscale old versions to TS2019 list

* Remove Alpine Linux container

* Updated changelog

* Releases: use flavor to set the tag suffix

* Added more debug messages in OIDC registration

* Added more logging

* Do not strip nodekey prefix on handle expired

* Updated changelog

* Add WithHostnameAsServerURL option func

* Reduce the number of namespaces and use hsic.WithHostnameAsServerURL

* Linting fix

* Fix linting issues

* Wait for ready outside the up goroutine

* Minor change in log message

* Add prefix to env var

* Remove unused env var

Co-authored-by: Juan Font <juan.font@esa.int>
Co-authored-by: Steven Honson <steven@honson.id.au>
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-11-21 21:51:54 +01:00
Kristoffer Dalby
4ccc528d96 Remove some very verbose error outputs
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-21 14:37:50 +01:00
Juan Font
6a311f4ab6 Remove broken renovatebot 2022-11-20 17:19:50 +01:00
manju-rn
a49a405413 Correction in the sample config file
Added the db_type in the sample config.yaml  Without this entry, the container throws Unsupported DB error
`db_type: sqlite3`
2022-11-20 17:12:13 +01:00
Juan Font
24f946e2e9 Fix completion issues (fixes #839) 2022-11-20 13:57:38 +01:00
Juan Font
c3cdb340de Increase integration tests timeout to 120m 2022-11-20 12:56:07 +01:00
Juan Font
935319a218 Remove mTLS from doc and config example 2022-11-19 19:50:34 +01:00
Juan Font
4c7e15a7ce Remove mTLS config from integration config 2022-11-19 19:50:34 +01:00
Juan Font
d461097247 Remove mTLS stuff from code 2022-11-19 19:50:34 +01:00
Juan Font
f90a3c196c Move TS WaitForReady outside up goroutine 2022-11-19 17:16:08 +01:00
Juan Font Alonso
751cc173d4 Fix issue when CLI is configured in config file 2022-11-18 19:19:56 +01:00
Juan Font Alonso
ff134f2b8e Fix remote CLI when there is no config file present 2022-11-18 19:19:56 +01:00
Arnar Gauti Ingason
6d3ede1367 Add support for NextDNS resolver 2022-11-18 09:38:46 +01:00
Steven Honson
c0884f94b8 Release: tag every release with develop 2022-11-17 16:52:12 +01:00
Kristoffer Dalby
3d8dd68b14 default to localhost, not listen on all
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-16 17:37:35 +01:00
Kristoffer Dalby
b02e88364e Fix test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-16 17:37:35 +01:00
Kristoffer Dalby
9790831afb Make config example "local dev first"
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-16 17:37:35 +01:00
Juan Font Alonso
2d79179141 Updated changelog 2022-11-15 21:28:26 +01:00
Juan Font Alonso
275cc28193 Do not strip nodekey prefix on handle expired 2022-11-15 21:28:26 +01:00
Juan Font
c5ba7552c5 Added more logging 2022-11-15 21:28:26 +01:00
Juan Font
8909f801bb Added more debug messages in OIDC registration 2022-11-15 21:28:26 +01:00
Steven Honson
3d4af52b3a Releases: use flavor to set the tag suffix 2022-11-15 11:36:38 +01:00
Juan Font
6391555dab Updated changelog 2022-11-15 08:42:29 +01:00
Juan Font
8cc5b2174b Remove Alpine Linux container 2022-11-15 08:42:29 +01:00
Juan Font
9269dd01f5 Move Tailscale old versions to TS2019 list 2022-11-14 23:06:30 +01:00
Juan Font
ef68f17a96 Return the correct error on cache miss 2022-11-14 18:34:27 +01:00
Juan Font
f74266f8f8 OIDC code cleanup and harmonize with regular web auth 2022-11-14 18:34:27 +01:00
Kristoffer Dalby
46df219ed3 Add testname identifier to hs container
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
835288d864 Remove unused variable
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
93d56362af Lock and unify headscale start/get method
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
4799859be0 Fix renamed method
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
8e44596171 less verbose command output
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
d479234058 Split ts versions into 2019/2021 for dedicated tests later
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
3fc5866de0 Remove duplicate function
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
f3c40086ac Make TLS setup work automatically
This commit injects the per-test-generated tls certs into the tailscale
container and makes sure all can ping all. It does not test any of the
DERP isolation yet.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
09ed21edd8 Remove duplicate function
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
456479eaa1 Rename and move wait for headscale
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
cb87852825 Add nolint to gosec stuff that doesnt matter because test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
69440058bb Clean up cert function
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Kristoffer Dalby
9bc6ac0f35 Make TLS setup work automatically
This commit injects the per-test-generated tls certs into the tailscale
container and makes sure all can ping all. It does not test any of the
DERP isolation yet.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-14 16:50:28 +01:00
Juan Font Alonso
89ff5c83d2 Add web flow auth integration tests 2022-11-14 08:47:02 +01:00
Juan Font Alonso
0a47d694be Return the real port of the container 2022-11-14 08:47:02 +01:00
Juan Font Alonso
73c84d4f6a Print hostname of the machine registered 2022-11-14 08:47:02 +01:00
Juan Font Alonso
a9251d6652 Fixed linter issues 2022-11-13 22:33:41 +01:00
Juan Font Alonso
f9c44f11d6 Added method to run tailscale up without authkey 2022-11-13 22:33:41 +01:00
Juan Font Alonso
1f8bd24a0d Return stderr in tsic.Execute 2022-11-13 22:33:41 +01:00
Juan Font Alonso
7bf2eb3d71 Update Tailscale interface with new Execute signature 2022-11-13 22:33:41 +01:00
Juan Font Alonso
f5a5437917 disable interfacebloat linter 2022-11-13 18:30:00 +01:00
Juan Font Alonso
9989657c0f Wait for tailscale client to be ready after tailscale up 2022-11-13 18:30:00 +01:00
Juan Font Alonso
cb2790984f Added WaitForReady() to Tailscale interface
When using running `tailscale up` in the AuthKey flow process, the tailscale client immediately enters PollMap after registration - avoiding a race condition.

When using the web auth (up -> go to the Control website -> CLI `register`) the client is polling checking if it has been authorized. If we immediately ask for the client IP, as done in CreateHeadscaleEnv() we might have the client in NotReady status.

This method provides a way to wait for the client to be ready.

Signed-off-by: Juan Font Alonso <juanfontalonso@gmail.com>
2022-11-13 18:30:00 +01:00
Juan Font Alonso
18c0009a51 Fix oidc.go linting issues
Signed-off-by: Juan Font Alonso <juanfontalonso@gmail.com>
2022-11-13 15:42:54 +01:00
Juan Font Alonso
d038df2a88 Added ts2019 buildtag to CI config
Otherwise we are getting utils.go:119:6: `decode` is unused (deadcode)

Signed-off-by: Juan Font Alonso <juanfontalonso@gmail.com>
2022-11-13 15:42:54 +01:00
Mesar Hameed
d8e9d95a3b config-example.yaml: fix typos and improve english. 2022-11-10 15:52:57 +00:00
Grigoriy Mikhalkin
0e405c7ce0 remove private key constant errors from NewHeadscale 2022-11-10 15:35:22 +00:00
Anton Schubert
21f0e089b6 fix noise mapResponse updates, fixes #838 2022-11-10 14:44:44 +00:00
kyra
cfda804726 Provide LoginName when registering with pre-auth key 2022-11-06 19:09:52 +01:00
github-actions[bot]
d6b383dd2f docs(README): update contributors 2022-11-05 16:03:17 +01:00
LiuHanCheng
07f92e647c fix bug in #912 (#914) 2022-11-05 09:07:22 +01:00
LiuHanCheng
bf87b33292 feat: add information to the /apple page for the macOS standalone client user (#915)
Co-authored-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-11-04 12:27:23 +01:00
Kristoffer Dalby
527b580f5e Add build flag to enable TS2019 (#928) 2022-11-04 11:26:33 +01:00
Kristoffer Dalby
c31328a54a Fix bitrotted versions in gh ci
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-04 10:46:23 +01:00
Kristoffer Dalby
b2c0e37122 Run on correct change
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-04 10:35:19 +01:00
Kristoffer Dalby
889223e35f Add experimental kradalby gh runner
Remove old v2 runner in favour of self-hosted

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-04 10:35:19 +01:00
Kristoffer Dalby
6e83b7f06b Give workflows better names
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 14:59:15 +01:00
Kristoffer Dalby
31d427b655 Run more tests in parallel
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 13:31:51 +01:00
Kristoffer Dalby
d8c856e602 Add basic accept all acl to all test as example
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
aad4c90fe6 Add options to hsic, ACL and env overrides
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
4f9fe93146 golangci-lint --fix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
96fe6aa3a1 Remove unused func, comment out configobject way
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
947e961a3a Write headcsale config file from code, not depend on directory
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
43731cad2e Add helper function to add files to hs container
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
ac15b21720 Remove tab from YAML
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Kristoffer Dalby
dfc03a6124 Ditch stupid distroless image for debug/test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-11-03 12:53:00 +01:00
Benjamin Roberts
8a07381e3a Fix prefix length comparison bug in AutoApprovers route evaluation (#862) 2022-11-01 12:00:40 +01:00
Kristoffer Dalby
0cf9c4ce8e Add nolint since go os has weird casing
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-31 17:58:03 +01:00
Kristoffer Dalby
e8b3de494e Fix lint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-31 17:58:03 +01:00
Kristoffer Dalby
21ec543d37 Give user better feedback if headscale socket is unwritable
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-31 17:58:03 +01:00
Kristoffer Dalby
ca8bca98ed Add support for "override local DNS" (#905)
* Add support for "override local DNS"

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* Update cli dump test

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-31 16:26:18 +01:00
Jiang Zhu
4e8b95e6cd Fix issue 660 (#874)
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2022-10-31 15:59:50 +01:00
Kristoffer Dalby
ad31378d92 Update vendor sha in nix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-30 23:16:07 +01:00
Kristoffer Dalby
3a6257b193 Update everything else
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-30 23:16:07 +01:00
Kristoffer Dalby
fafa3f8211 Upgrade tailscale
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-30 23:16:07 +01:00
Kristoffer Dalby
62e3fa0011 Update nix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-30 23:16:07 +01:00
Kristoffer Dalby
94ad0a1555 Remove ip_prefix, its been deprecated for a long time (#899)
* Remove ip_prefix, its been deprecated for a long time

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

* update changelog

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2022-10-30 22:31:18 +01:00
Juan Font
c1c22a4b51 Merge pull request #897 from kradalby/integration-remove-v1-genera
Remove v1 general integration tests
2022-10-30 21:49:49 +01:00
Juan Font
611f7c374c Merge branch 'main' into integration-remove-v1-genera 2022-10-30 21:46:33 +01:00
Kristoffer Dalby
91c0a153b0 Merge pull request #890 from kradalby/integration-v2-cli 2022-10-28 18:46:04 +02:00
Kristoffer Dalby
73eae8e2cf Merge branch 'main' into integration-v2-cli 2022-10-28 16:13:21 +02:00
Kristoffer Dalby
341db0c5c9 Merge pull request #895 from puzpuzpuz/update-xsync-version 2022-10-28 16:12:38 +02:00
Kristoffer Dalby
2ca286ee8c Merge branch 'main' into integration-v2-cli 2022-10-28 15:29:43 +02:00
Kristoffer Dalby
dde39aa24c Remove general v1 makefile entry
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-28 15:21:11 +02:00
Kristoffer Dalby
bcdd34b01e Remove v1 general integration tests code
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-28 15:10:45 +02:00
Kristoffer Dalby
e45ba37ec5 Remove v1 general integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-28 15:10:30 +02:00
Kristoffer Dalby
d69a5f621e Merge branch 'main' into update-xsync-version 2022-10-28 10:21:08 +02:00
Kristoffer Dalby
7f69b08bc8 Merge pull request #896 from kradalby/update-golines 2022-10-28 10:20:20 +02:00
Kristoffer Dalby
5d3c02702b Update golines
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-28 09:30:56 +02:00
Kristoffer Dalby
1469425484 update flake vendor hash
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-28 09:28:20 +02:00
Andrey Pechkurov
0e12b66706 Simplify code around latest state change map updates 2022-10-27 23:22:33 +03:00
Kristoffer Dalby
7e6ab19270 Port preauthkey subcommand tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-26 10:23:44 +02:00
Kristoffer Dalby
5013187aaf Add some sort stability
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-26 10:23:44 +02:00
Kristoffer Dalby
239ef16ad1 Add preauthkey command test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-26 10:23:44 +02:00
Kristoffer Dalby
cb61a490e0 Add namespace command test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-26 10:23:44 +02:00
Kristoffer Dalby
2c0488da0b Add Execute helper for controlserver
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-26 10:23:44 +02:00
Kristoffer Dalby
a647e6af24 Merge pull request #889 from kradalby/integration-v2-resolve-magicdns 2022-10-25 17:56:06 +02:00
Kristoffer Dalby
fe4e05b0bc only print stdout on err
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-25 09:24:05 +02:00
Kristoffer Dalby
54e3a0d372 Test with a longer timeout
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-25 08:44:25 +02:00
Juan Font
e7e2c7804b Merge branch 'main' into integration-v2-resolve-magicdns 2022-10-25 00:10:28 +02:00
Juan Font
5c9c4f27fe Merge pull request #892 from kradalby/integration-v2-no-verbose
Remove verbose flag for v2 tests, increase timeout
2022-10-25 00:10:07 +02:00
Juan Font
21b06f603a Merge branch 'main' into integration-v2-no-verbose 2022-10-25 00:08:50 +02:00
Juan Font
a14f482ef7 Merge pull request #891 from kradalby/integration-ditch-retry
Integration, remove retry
2022-10-25 00:08:38 +02:00
Kristoffer Dalby
86c132c8b2 Remove verbose flag for v2 tests, increase timeout
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-24 17:14:55 +02:00
Kristoffer Dalby
2b10226618 Remove extra line
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-24 16:48:25 +02:00
Kristoffer Dalby
23a0946e76 Integration, remove retry
The retry has no real function as it will just fail on
"container exists" on the old tests and the new test will
just try forever before it eventually fails.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-24 16:46:21 +02:00
Kristoffer Dalby
7015d72911 port resolve magicdns test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-24 14:59:14 +02:00
Kristoffer Dalby
76689c221d remove fixed todo
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-24 14:58:02 +02:00
Kristoffer Dalby
8d46986a87 Merge pull request #888 from juanfont/update-contributors 2022-10-23 17:47:09 +02:00
github-actions[bot]
b22e628b49 docs(README): update contributors 2022-10-23 14:33:02 +00:00
Kristoffer Dalby
9c30939e3f Merge pull request #887 from kradalby/integration-v2-taildrop 2022-10-23 16:32:11 +02:00
Kristoffer Dalby
018b1d68f2 Migrate taildrop test to v2
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 14:13:22 +02:00
Kristoffer Dalby
ae189c03ac Merge pull request #884 from kradalby/integration-v2-ping-by-hostname 2022-10-23 14:12:06 +02:00
Kristoffer Dalby
7155b22043 Factor out some commonly used patterns
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 12:41:35 +02:00
Kristoffer Dalby
40c048fb45 Fix lint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 12:01:03 +02:00
Kristoffer Dalby
53b4bb220d Fixup after ts interface
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:55:37 +02:00
Kristoffer Dalby
d706c3516d Remove 1.16 from FQDN, bump 1.32.1
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:50:19 +02:00
Kristoffer Dalby
cbbf9fbdef Use FQDN from tailscale client
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:50:19 +02:00
Kristoffer Dalby
d8144ee2ed Add initial pingallbyhostname
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:50:16 +02:00
Kristoffer Dalby
fa3d21cbc0 Rename pingall test to signal ip
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:50:03 +02:00
Kristoffer Dalby
d242ceac46 Make hostname dns safe, allow string in ping command
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-23 11:50:01 +02:00
Juan Font
ecce82d44a Merge pull request #875 from thetillhoff/main
Unify code snippet comment location
2022-10-22 18:10:22 +02:00
Juan Font
463180cc2e Merge branch 'main' into main 2022-10-22 16:22:51 +02:00
Juan Font
129afdb157 Merge pull request #871 from kradalby/integration-ts-interface
Integration: make TailscaleClient interface
2022-10-22 16:22:40 +02:00
Till Hoffmann
701f990a23 Unify code snippet comment location 2022-10-22 00:12:24 +02:00
Kristoffer Dalby
e112514a3b Merge branch 'main' into integration-ts-interface 2022-10-21 15:37:21 +02:00
Kristoffer Dalby
babd303667 Merge pull request #771 from shanna/feature-random-suffix-on-collision 2022-10-21 15:14:28 +02:00
Kristoffer Dalby
2d170fe339 update tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-21 14:54:57 +02:00
Kristoffer Dalby
bc1c1f5ce8 Fix most nil pointers, actually make it check for unique across headscale
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-21 14:42:37 +02:00
Kristoffer Dalby
830d59fe8c Merge branch 'main' into feature-random-suffix-on-collision 2022-10-21 13:34:15 +02:00
Kristoffer Dalby
c9823ce347 Use TailscaleClient interface instead of tsic
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-21 13:17:54 +02:00
Kristoffer Dalby
8c4744acd9 make TailscaleClient interface
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-21 13:17:38 +02:00
Juan Font
9c16d5e511 Merge pull request #843 from phpmalik/patch-1
Fix spelling error
2022-10-21 06:23:59 +02:00
Juan Font
40b3de9894 Merge branch 'main' into feature-random-suffix-on-collision 2022-10-21 05:19:53 +02:00
Juan Font
1eea9c943c Merge branch 'main' into patch-1 2022-10-21 05:19:23 +02:00
Juan Font
399c3255ab Merge pull request #852 from kevin1sMe/main
Update document about reverse-proxy
2022-10-21 05:19:08 +02:00
Juan Font
852cb90fcc Merge branch 'main' into main 2022-10-21 05:13:37 +02:00
Juan Font
587a016b46 Merge pull request #856 from kradalby/integration-v2
Integration tests v2
2022-10-21 05:12:17 +02:00
Kristoffer Dalby
b2bca2ac81 Only run integration tests from dir in new tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 16:00:24 +02:00
Kristoffer Dalby
6d8c18d4de Fix golangcilint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
12ee9bc02d Fix golangcilint
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
8502a0acda dont request tty
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
36ad0003a9 golangci-lint --fix
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
4cb7d63e8b Set better names for different integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
2bf50bc205 Add new integration tests to ci
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
39bc6f7e01 Port PingAll test to new test suite
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
0db608a7b7 Add tailscale versions, waiters and helpers for scenario
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
3951f39868 Add wait for peers and status to tsic
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
c90d0dd843 remove the need to bind host port
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
84f9f604b0 go mod tidy
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
aef77a113c use variable for namespace
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
13aa845c69 Add comment about scenario test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
b0a4ee4dfe test login with one node
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
25e39d9ff9 Add get ips command to scenario
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
f109b54e79 Join test suite container to network, allowing seperate networks
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
eda4321486 Skip integration tests on short or lack of docker
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
a9c3b14f79 Define a "scenario", which is a controlserver with nodes
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
f68ba7504f Move some helper functions into dockertestutil package
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
b331e3f736 hsic: ControlServer implementation of headscale in docker
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
308b9e78a1 Defince control server interface
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Kristoffer Dalby
fa8b02a83f tsic: Tailscale in Container abstraction
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:37:11 +02:00
Juan Font
a39504510a Merge pull request #865 from kradalby/integration-no-build-tags
Do not use build tags for running integration tests
2022-10-18 15:36:09 +02:00
Kristoffer Dalby
2f36a11a8e use short flag for nix build test
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 15:08:48 +02:00
Kristoffer Dalby
4df47de3f2 add nolint to integrationtests, they are going away ™️
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 14:57:22 +02:00
Kristoffer Dalby
dfadb965b7 Use short test to signal that we dont run integration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 14:45:18 +02:00
Kristoffer Dalby
c6f82c3646 Switch from hacking buildtags to selecting tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 14:41:48 +02:00
Kristoffer Dalby
32c21a05f8 cache go mod in docker, speed up local
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-18 14:41:20 +02:00
kevinlin
79864e0165 Fmt md with prettier 2022-10-15 00:11:57 +08:00
kevinlin
06e12f7020 Update: tips about warnning log 2022-10-15 00:11:05 +08:00
kevinlin
3659461666 Update reverse-proxy document for istio/envoy 2022-10-15 00:11:05 +08:00
Juan Font
e96bceed4c Merge pull request #859 from kradalby/new-integration-versions
Add back head and unstable, ts 1.32.0
2022-10-14 10:44:25 +02:00
Kristoffer Dalby
ff217ccce8 Add back head and unstable, ts 1.32.0
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-14 09:12:13 +02:00
Juan Font
4dd2eef5d1 Merge pull request #855 from Donran/main
Fix no arguments panic
2022-10-13 22:50:49 +02:00
Juan Font
907aa07e51 Merge branch 'main' into main 2022-10-13 22:30:45 +02:00
Juan Font
0048ed07a2 Merge pull request #853 from zhzy0077/patch-1
Fix the proposed noise private_key_path
2022-10-13 22:30:02 +02:00
Juan Font
88d12873c5 Merge branch 'main' into patch-1 2022-10-13 22:28:24 +02:00
Pontus N
9f58eebfe1 Fix zero arguments error 2022-10-13 15:17:18 +02:00
Kristoffer Dalby
cf40d2a892 Merge pull request #854 from kradalby/integration-split 2022-10-13 10:15:13 +02:00
Kristoffer Dalby
21dd212349 Split integration tests into seperate jobs
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-13 05:50:41 +02:00
Zhiyuan Zheng
073308f1a3 Fix the proposed noise private_key_path
As indicated by the comment, the default /var/lib/headscale path is not writable in the container. However the sample setting is not following that like `private_key_path`
2022-10-11 22:55:54 +08:00
Kristoffer Dalby
03194e2d66 Merge branch 'main' into feature-random-suffix-on-collision 2022-10-11 08:24:21 +02:00
Kristoffer Dalby
f18e22224c Merge pull request #844 from kradalby/container-exist-fix
Run integration tests inside docker, dont depend on local platform
2022-10-08 12:25:59 +02:00
Kristoffer Dalby
8ee35c9c22 Stuff
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
d900f48d38 expose right porsts
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
a846e13c78 Expose and use ports consistently
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
ed2236aa24 Add buildtags to pls
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
a94ed0586e Run all integration tests fully in docker
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
22cabc16d7 No interactive tty
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
88931001fd Fail correctly if container exist
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
f3dbfc9045 Style change
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
85df2c80a8 Run oidc tests fully in docker
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
aca3a667c4 Fix declaration of pointer
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
a0ec3690b6 Fix error declaration
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
37a4d41d0e Make addr configurable
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
382a37f1e1 Test against last patch version
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
201f81ce00 Make sure mockoidc is up, has unique name and removed if exist
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
4904ccc3c3 Make sure mock container is removed before started
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:27 +02:00
Kristoffer Dalby
6b67584d47 Fix DERP name in integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-07 23:56:26 +02:00
Juan Font
d575dac73a Merge pull request #823 from kradalby/sanitise-machine-key-url
Protect against user injection for registration CLI page
2022-10-04 16:01:19 +02:00
Juan Font
5333df283a Merge branch 'main' into sanitise-machine-key-url 2022-10-04 14:31:28 +02:00
Kristoffer Dalby
d56ad2917d Fix nolint comment
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-03 12:29:13 +02:00
Kristoffer Dalby
df36bcfd39 Fix machine test from marger
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-10-03 12:29:13 +02:00
github-actions[bot]
a3d3ad2208 docs(README): update contributors 2022-10-03 12:29:13 +02:00
phpmalik
0b0fb0af22 Minor change
Spelling mistake
listning -> listening
2022-10-03 12:59:39 +05:30
=
2aebd2927d Random suffix only on collision.
0.16.0 introduced random suffixes to all machine given names
(DNS hostnames) regardless of collisions within a namespace.
This commit brings Headscale more inline with Tailscale by only
adding a suffix if the hostname will collide within the namespace.

The suffix generation differs from Tailscale.
See https://tailscale.com/kb/1098/machine-names/
2022-10-03 09:13:56 +02:00
Kristoffer Dalby
c00e5599b0 Merge pull request #840 from juanfont/update-contributors 2022-10-03 09:08:13 +02:00
github-actions[bot]
72e2fa46c7 docs(README): update contributors 2022-09-30 08:23:20 +00:00
Kristoffer Dalby
98f5b7f638 Merge pull request #837 from ShadowJonathan/patch-1 2022-09-30 10:22:38 +02:00
Jonathan de Jong
70ecda6fd1 Fix warning on success 2022-09-27 11:51:00 +02:00
Kristoffer Dalby
5fe6538c02 Merge pull request #831 from kradalby/fix-https-listen 2022-09-26 14:02:56 +02:00
Kristoffer Dalby
84c4b0336f Merge branch 'main' into fix-https-listen 2022-09-26 12:13:16 +02:00
Kristoffer Dalby
8fbba1ac94 Merge pull request #830 from kradalby/nix-overlay 2022-09-26 12:13:05 +02:00
Kristoffer Dalby
1a30bcba91 Merge branch 'main' into nix-overlay 2022-09-26 11:50:25 +02:00
Kristoffer Dalby
ed58b2e4e2 Merge branch 'main' into fix-https-listen 2022-09-26 11:50:20 +02:00
Kristoffer Dalby
5f975cbb50 Merge pull request #829 from kradalby/oidc-dependency 2022-09-26 11:49:53 +02:00
Kristoffer Dalby
81dd9b2386 format
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 11:34:04 +02:00
Kristoffer Dalby
9088521252 Move lets enc listener into go routine
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 11:33:48 +02:00
Kristoffer Dalby
fc6a1e15fc Revert overlay overlapping
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 11:13:42 +02:00
Kristoffer Dalby
94be5ca295 Nix update
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:50:41 +02:00
Kristoffer Dalby
804d9d8196 Format nix with alejandra
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:48:59 +02:00
Kristoffer Dalby
d0e945fdd7 _ unused variable
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:48:37 +02:00
Kristoffer Dalby
98e7842c26 Add nix overlay to flake
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:47:49 +02:00
Kristoffer Dalby
24629895c7 Add new config option to cli integration tests
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:14:46 +02:00
Kristoffer Dalby
256b6cb54d Add new option to config-example
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:01:15 +02:00
Kristoffer Dalby
6b4d53315b Update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 10:01:01 +02:00
Kristoffer Dalby
fb25a06a66 Preserve current behaviour with a config flag
Add a configuration flag (default true to preserve current behaviour) to
allow headscale to start without OIDC being able to initialise.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 09:57:28 +02:00
Kristoffer Dalby
dbe58e53e4 Allow headscale to start if oidc setup fails.
This commit makes headscale fall back to CLI authentication if oidc
fails to initialised and posts a warning to users.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 09:52:32 +02:00
Kristoffer Dalby
8dcc82ceb3 Use oidc if it initialised, not if it is configured
OIDC might be configured, but unable to be initialised, this only runs
the oidc cycle if it is actually successfully set up/initialised.

Prep for next commit

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-26 09:51:23 +02:00
Kristoffer Dalby
8be14ef6fe gofumpt
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-23 11:53:42 +02:00
Kristoffer Dalby
2bb34751d1 Validate the incoming nodekey with regex before attempting to parse
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-23 11:53:42 +02:00
Kristoffer Dalby
d06ba7b522 Merge branch 'main' into sanitise-machine-key-url 2022-09-23 11:09:23 +02:00
Kristoffer Dalby
a507a04650 Merge pull request #763 from tsujamin/autoapprovers 2022-09-23 11:07:53 +02:00
Benjamin George Roberts
7761a7b23e fix autoapprover test following tagged authkey change 2022-09-23 18:46:35 +10:00
Benjamin George Roberts
6d2cfd52c5 Merge branch 'main' into autoapprovers 2022-09-23 18:44:36 +10:00
Kristoffer Dalby
75a8fc8b3e Update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-23 10:44:29 +02:00
Kristoffer Dalby
8fa05c1e72 Merge pull request #767 from tsujamin/preauthkey-tags 2022-09-23 10:42:42 +02:00
Kristoffer Dalby
93082b8092 Protect against user injection for registration CLI page
This commit addresses a potential issue where we allowed unsanitised
content to be passed through a go template without validation.

We now try to unmarshall the incoming node key and fails to render the
template if it is not a valid node key.

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
2022-09-23 10:39:42 +02:00
Benjamin George Roberts
d764f52f24 Update changelog 2022-09-23 18:16:16 +10:00
Benjamin George Roberts
e5decbd0fa Update changelog 2022-09-23 18:13:48 +10:00
Kristoffer Dalby
8a1c0e0e9b Merge branch 'main' into preauthkey-tags 2022-09-23 18:11:27 +10:00
Benjamin Roberts
5b12ab9894 Merge branch 'main' into autoapprovers 2022-09-23 18:06:31 +10:00
Benjamin George Roberts
c52e3aafe6 remove unnecessary checks on slices 2022-09-23 18:04:30 +10:00
Juan Font
a46170e2a1 Merge pull request #793 from juanfont/remove-sponsorship
Remove sponsor buttons
2022-09-21 19:50:11 +02:00
Juan Font
aca1c1b156 Merge branch 'main' into remove-sponsorship 2022-09-21 18:13:00 +02:00
Juan Font
09863b540d Merge branch 'main' into preauthkey-tags 2022-09-21 18:03:35 +02:00
Juan Font
adb352e663 Merge branch 'main' into autoapprovers 2022-09-21 17:53:17 +02:00
Juan Font
c9b39da6b9 Merge pull request #790 from mike-lloyd03/reverse-proxy
Add reverse proxy documentation
2022-09-21 17:52:59 +02:00
Juan Font
6fe86dff00 Merge branch 'main' into remove-sponsorship 2022-09-21 17:47:43 +02:00
Juan Font
9b1dcb2f0c Merge branch 'main' into reverse-proxy 2022-09-21 17:47:13 +02:00
Juan Font
22c68fff13 Merge pull request #815 from juanfont/remove-gin-references
Removed gin from go.sum (Github security notice)
2022-09-21 17:45:12 +02:00
Juan Font
ddd92822b0 Merge branch 'main' into remove-gin-references 2022-09-21 17:26:23 +02:00
Juan Font
bd6282d1e3 Merge pull request #801 from juanfont/oidc-integration-testing
Add integration tests for OIDC authentication
2022-09-21 17:26:04 +02:00
Juan Font
7092a3ea47 Merge branch 'oidc-integration-testing' of https://github.com/juanfont/headscale into oidc-integration-testing 2022-09-21 15:02:13 +00:00
Juan Font
695359862e Return stderr too in ExecuteCommand 2022-09-21 15:01:26 +00:00
Juan Font
95948e03c9 Added indication of workaround for #814 2022-09-21 14:47:48 +00:00
Mike Lloyd
e286ba817b Format reverse-proxy.md 2022-09-20 20:12:45 -07:00
Juan Font
8aa0eefedd Merge branch 'main' into oidc-integration-testing 2022-09-20 23:43:45 +02:00
Juan Font
e6e5872b4b Merge branch 'main' into reverse-proxy 2022-09-20 23:34:44 +02:00
Juan Font
2c73f8ee62 Merge branch 'main' into remove-gin-references 2022-09-20 23:32:42 +02:00
Juan Font
cdc8bab7d9 Merge pull request #768 from kazauwa/feature/json-logs
toggle json logging via config
2022-09-20 23:32:10 +02:00
Juan Font
f2928d7dcb Removed gin from go.sum (Github security notice) 2022-09-20 21:26:11 +00:00
Juan Font
44be239723 Merge branch 'main' into reverse-proxy 2022-09-20 23:16:21 +02:00
Juan Font
397754753f Merge branch 'main' into feature/json-logs 2022-09-20 23:11:29 +02:00
Juan Font
e87b470996 Removed fmt.Println for linting 2022-09-20 21:06:43 +00:00
Juan Font
083d2a871c Linting fixes 2022-09-20 21:02:44 +00:00
Juan Font
7a171cf5ea Added sleep to workaround #814 2022-09-20 20:54:58 +00:00
Juan Font
1563d7555f Use Headscale container to run mockoidc 2022-09-20 20:42:50 +00:00
Juan Font
2e97119db8 Added derp config to OIDC etc 2022-09-20 20:42:12 +00:00
Juan Font
b3a53bf642 Do not load the config for CLI mockoidc (and version) 2022-09-20 19:59:22 +00:00
Juan Font
a3f18f248c Add internal mockoidc command 2022-09-20 19:58:36 +00:00
Juan Font
1c267f72e0 Capture listen error on mockoidc 2022-09-19 23:07:47 +00:00
Juan Font
becf918b78 Merge branch 'main' into remove-sponsorship 2022-09-18 23:58:42 +02:00
Juan Font
9c58395bb3 Removed unused param after routes fix 2022-09-18 21:40:52 +00:00
Juan Font
b117ca7720 Added missing TLS key for testing 2022-09-18 21:26:47 +00:00
Juan Font
d83a28bd1b Merge branch 'main' into oidc-integration-testing 2022-09-18 23:25:01 +02:00
Juan Font
42ef71bff9 Merge pull request #811 from kradalby/primary-routes
Fix subnet routers
2022-09-18 21:59:25 +02:00
Kristoffer Dalby
f2da1a1665 Add comment and update changelog
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 12:14:49 +02:00
Kristoffer Dalby
356b76fc56 Format
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 11:37:38 +02:00
Kristoffer Dalby
33ae56acfa Add primary routes to node
Signed-off-by: Kristoffer Dalby <kristoffer@dalby.cc>
2022-09-18 11:36:35 +02:00
Juan Font
9923adcb8b Merge branch 'main' into feature/json-logs 2022-09-15 00:22:18 +02:00
Juan Font Alonso
c21479cb9c Print docker network config 2022-09-15 00:06:17 +02:00
Juan Font Alonso
3abca99b0c Add logs for issues in Actions 2022-09-14 23:32:19 +02:00
Igor Perepilitsyn
874d6aaf6b Make styling fixes 2022-09-11 21:44:28 +02:00
Igor Perepilitsyn
ae4f2cc4b5 Update changelog 2022-09-11 21:37:38 +02:00
Igor Perepilitsyn
dd155dca97 Create a distinct log section in config 2022-09-11 21:37:23 +02:00
Juan Font Alonso
99307d1576 Update nix sum 2022-09-08 20:36:44 +02:00
Juan Font Alonso
b2f3ffbc5a Run integration tests in Actions 2022-09-08 19:49:37 +02:00
Juan Font Alonso
5774b32e55 Include OIDC in the full execution 2022-09-08 19:48:51 +02:00
Juan Font Alonso
41353a57c8 Added integration tests for OIDC on Makefile 2022-09-08 19:48:27 +02:00
Juan Font Alonso
9c0cf4595a OIDC integration tests working 2022-09-08 19:47:47 +02:00
Juan Font Alonso
71b712356f Minor change on the base config for OIDC 2022-09-08 19:47:29 +02:00
Juan Font Alonso
f33e3e3b81 Parse the OIDC login URL 2022-09-08 19:32:11 +02:00
Juan Font Alonso
5f384c6323 Removed old code and minor changes 2022-09-08 18:11:41 +02:00
Benjamin Roberts
e056b86c37 Merge branch 'main' into preauthkey-tags 2022-09-08 09:04:12 +10:00
Jamie Greeff
91e30397bd Remove --rm flag from Docker example
It appears to be causing confusion for users on Discord when copying/pasting from the example here, if Headscale crashes on launch then the container will be removed and logs can't be viewed with `docker logs`.
2022-09-08 09:03:42 +10:00
Benjamin George Roberts
8a8ec7476d fix linting issues in preauthkey tags 2022-09-08 09:03:38 +10:00
Juan Font Alonso
fca380587a Initial work on OIDC tests 2022-09-07 23:53:46 +02:00
Juan Font Alonso
cb70d7c705 Return the results on error 2022-09-07 23:53:31 +02:00
Juan Font Alonso
b27b789e28 Added base config file template 2022-09-07 18:40:02 +02:00
Juan Font
a9da953b55 Merge branch 'main' into autoapprovers 2022-09-07 18:21:21 +02:00
Juan Font
12d5b6a2d2 Merge branch 'main' into remove-sponsorship 2022-09-07 17:14:11 +02:00
Juan Font
a0a463494b Merge pull request #797 from madjam002/patch-1
Remove --rm flag from Docker example
2022-09-07 17:13:14 +02:00
Jamie Greeff
07dca79b20 Remove --rm flag from Docker example
It appears to be causing confusion for users on Discord when copying/pasting from the example here, if Headscale crashes on launch then the container will be removed and logs can't be viewed with `docker logs`.
2022-09-07 14:16:04 +01:00
Benjamin George Roberts
688cba7292 fix linting mistakes 2022-09-07 21:39:56 +10:00
Mike Lloyd
0fe3c21223 Move map block out of server block 2022-09-06 16:12:20 -07:00
Mike Lloyd
45df6e77ff Apply suggestions from code review
Thanks for the pointers!

Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2022-09-06 15:37:39 -07:00
Juan Font
548551c6ae Merge branch 'main' into autoapprovers 2022-09-07 00:31:48 +02:00
Juan Font
e3f1fd1ffc Merge branch 'main' into remove-sponsorship 2022-09-07 00:31:13 +02:00
Juan Font
470c49394c Merge branch 'main' into preauthkey-tags 2022-09-07 00:22:36 +02:00
Juan Font
31662bcd28 Merge branch 'main' into reverse-proxy 2022-09-07 00:19:57 +02:00
Juan Font
7247302f45 Merge branch 'main' into feature/json-logs 2022-09-07 00:05:38 +02:00
Juan Font
1a5a5b12b7 Merge pull request #795 from stefanvanburen/svanburen/buf-mod-update
Run `buf mod update` in protos/
2022-09-06 23:49:32 +02:00
Stefan VanBuren
0099dd1724 Run buf mod update 2022-09-06 14:52:09 -04:00
Juan Font
1f131c6729 Merge branch 'main' into feature/json-logs 2022-09-06 20:18:35 +02:00
Juan Font
fc4361b225 Delete FUNDING.yml 2022-09-06 20:09:01 +02:00
Juan Font
ce25a1e64e Remove sponsor buttons 2022-09-06 20:07:16 +02:00
Juan Font
449a135b94 Merge pull request #791 from kradalby/add-logo
Add logo files and add to readme
2022-09-05 15:34:00 +02:00
Kristoffer Dalby
002d484abe use logo in readme correcly 2022-09-05 15:31:03 +02:00
Kristoffer Dalby
9823ef2af5 use logo in readme 2022-09-05 15:30:19 +02:00
Kristoffer Dalby
641c6fd439 Add logo files 2022-09-05 15:29:30 +02:00
Mike Lloyd
3a042471b7 Add web sockets section 2022-09-04 17:39:51 -07:00
Mike Lloyd
dc18d64286 Add websockets config 2022-09-04 17:26:33 -07:00
Benjamin George Roberts
72a43007d8 fix broken preauth-key tag test 2022-09-05 09:44:55 +10:00
Benjamin George Roberts
842c28adff replace netaddr usage with netip 2022-09-05 09:33:53 +10:00
Juan Font
9810d84e2d Merge branch 'main' into autoapprovers 2022-09-04 22:40:08 +02:00
Juan Font
f6153a9b5d Merge branch 'main' into preauthkey-tags 2022-09-04 22:35:51 +02:00
Juan Font
302a88bfdb Merge branch 'main' into feature/json-logs 2022-09-04 22:32:58 +02:00
Mike Lloyd
f6e83413e5 Add PR link to changelog 2022-09-04 09:49:34 -07:00
Mike Lloyd
02ab3a2cb6 Update changelog 2022-09-04 09:46:11 -07:00
Mike Lloyd
90e840c3c9 Add reverse proxy documentation 2022-09-04 09:42:23 -07:00
Juan Font
af60ffb7fa Merge pull request #788 from juanfont/warn-websockets-requirement
Warn when Headscale is running behind an improperly configured proxy
2022-09-04 16:44:54 +02:00
Juan Font Alonso
c28e559da4 Updated changelog 2022-09-04 16:23:46 +02:00
Juan Font Alonso
5c59255b41 Also warn in DERP server if Websockets are not properly working 2022-09-04 16:13:48 +02:00
Juan Font Alonso
a377ee14b4 Minor message change 2022-09-04 16:13:30 +02:00
Juan Font Alonso
2262188d8a Warn when Headscale is running behind a wrongly configured proxy 2022-09-04 16:05:21 +02:00
Juan Font
7c49c752a9 Merge pull request #786 from juanfont/update-deps-20220904
Update dependencies 20220904
2022-09-04 16:01:52 +02:00
Juan Font Alonso
e29726cc50 Updated nix sum 2022-09-04 15:45:35 +02:00
Juan Font Alonso
3c73cbe92b Merge branch 'main' into update-deps-20220904 2022-09-04 15:37:25 +02:00
Juan Font Alonso
cc357062be Missing go.sum 2022-09-04 15:36:59 +02:00
Juan Font Alonso
17c06f7167 Upgrade direct dependencies 2022-09-04 15:36:00 +02:00
Juan Font
d12e0156c3 Merge pull request #784 from juanfont/fix-go-1.19-lint
Fix linting broken after Go 1.19
2022-09-04 15:31:41 +02:00
Juan Font Alonso
204dedaa49 Only pass the context in pollmap, no req needed 2022-09-04 15:14:12 +02:00
Juan Font Alonso
52073ce7c9 Pass context in OIDC helpers 2022-09-04 15:02:18 +02:00
Juan Font Alonso
434747e007 Use timeout in lets encrypt http server 2022-09-04 11:47:05 +02:00
Juan Font Alonso
7a78314d9d Remove nolint directives 2022-09-04 11:44:24 +02:00
Juan Font Alonso
f23e9dc235 Pass the req context when pinging the DB 2022-09-04 11:43:09 +02:00
Juan Font Alonso
4527801d48 More unused parameters removed in protocol functions 2022-09-04 11:41:31 +02:00
Juan Font Alonso
e0857f0226 Removed unused parameters in protocol functions 2022-09-04 11:40:14 +02:00
Juan Font Alonso
0d074b1da6 setLastStateChangeToNow was always receiving nil 2022-09-04 11:37:49 +02:00
Juan Font Alonso
f2fda4f906 Return error on marshaling issues 2022-09-04 11:36:03 +02:00
Juan Font Alonso
c1c36036ae Add timeouts for the Noise server 2022-09-04 11:35:39 +02:00
Juan Font Alonso
9a1438d2e3 Use inherited context 2022-09-04 11:35:13 +02:00
Juan Font Alonso
582122851d Go do not like underscores in packages 2022-09-04 11:34:23 +02:00
Juan Font Alonso
f4d197485c Use library const for HTTP verbs 2022-09-04 11:33:00 +02:00
Juan Font Alonso
68305df9b2 Applied gofumpt 2022-09-04 11:32:29 +02:00
Juan Font Alonso
ca0be81833 Target the latest version for golint 2022-09-04 11:31:06 +02:00
Juan Font
380fbfe438 Merge pull request #780 from juanfont/bump-ts1.30
Target Tailscale version to v1.30.0 (and replace inet.af/netaddr with net/netip)
2022-09-04 09:24:42 +02:00
Juan Font Alonso
32d68a40d5 Update flake sha 2022-09-04 00:00:23 +02:00
Juan Font Alonso
198e92c08f Remove dependency on netaddr 2022-09-03 23:46:14 +02:00
Juan Font Alonso
38b26f5285 Merge branch 'main' into bump-ts1.30 2022-09-03 23:33:09 +02:00
Juan Font
096a009685 Merge pull request #781 from juanfont/switch-to-nix-unstable
Switch to Nix unstable for Go 1.19
2022-09-03 23:32:51 +02:00
Juan Font Alonso
30c0fdb38d Update changelog 2022-09-03 23:19:07 +02:00
Juan Font Alonso
663dbf7395 Use go 1.19 in Nix 2022-09-03 23:06:21 +02:00
Juan Font Alonso
373db0dc5e Switch to nix unstable 2022-09-03 23:05:34 +02:00
Juan Font Alonso
2733fb30cc Minor change in go.mod 2022-09-03 16:23:36 +02:00
Juan Font Alonso
d29411408b Merge branch 'main' into bump-tailscale-v1.30 2022-09-03 16:20:25 +02:00
Juan Font
24bafdf2bb Merge pull request #778 from juanfont/bump-go-1.19
Target Go 1.19 for Headscale
2022-09-03 13:16:48 +02:00
Juan Font
a9ede6a2bc Merge branch 'main' into feature/json-logs 2022-09-03 12:39:04 +02:00
Juan Font Alonso
2c5bf6982c Updated changelog 2022-09-03 12:24:22 +02:00
Juan Font Alonso
dd3ec84000 Minor doc change 2022-09-03 12:22:03 +02:00
Juan Font Alonso
84044e236d Release using go 1.19 2022-09-03 12:21:54 +02:00
Juan Font Alonso
2ddf7ab515 Use Go 1.19 in Dockerfiles 2022-09-03 12:21:35 +02:00
Juan Font Alonso
f519c513c2 Target go 1.19 in go.mod 2022-09-03 12:21:04 +02:00
Juan Font Alonso
d5cc5b2bc8 Move integration tests to net/netip 2022-09-02 09:22:34 +02:00
Juan Font Alonso
51abf90db6 Use net/netip in derp server 2022-09-02 09:16:19 +02:00
Juan Font Alonso
71410cb6da Port dns to net/netip 2022-09-02 09:15:05 +02:00
Juan Font Alonso
efb12f208c Move db to net/netip 2022-09-02 09:13:50 +02:00
Juan Font Alonso
64ede5dbef Move namespaces unit tests to net/netip 2022-09-02 09:13:07 +02:00
Juan Font Alonso
7af78152a4 Migrate routes to net/netip 2022-09-02 00:06:19 +02:00
Juan Font Alonso
290ec8bb19 Migrate ACLs to net/netip 2022-09-02 00:05:43 +02:00
Juan Font Alonso
cdf48b1216 Migrate utils to net/netip 2022-09-02 00:05:18 +02:00
Juan Font Alonso
a24710a961 Migrate machine to net/netip 2022-09-02 00:04:31 +02:00
Juan Font Alonso
197da8afcb Migrate config.go to net/netip 2022-09-02 00:04:04 +02:00
Juan Font Alonso
12385d4357 Target Tailscale v1.30.0 2022-09-01 20:50:56 +02:00
Juan Font
e7f8bb866f Merge pull request #772 from juanfont/enable-1.30-in-tests
Add Tailscale v1.30.0 to the integration test roaster
2022-09-01 00:03:21 +02:00
Juan Font Alonso
1ad19a3bd8 Add 1.30.0 to the version roaster 2022-08-31 22:17:13 +02:00
Igor Perepilitsyn
bb6b07dedc FIXES #768 add new config entry to the old itegration tests 2022-08-26 13:43:25 +02:00
Igor Perepilitsyn
2403c0e198 toggle json logging via config 2022-08-26 13:10:51 +02:00
Benjamin George Roberts
ac18723dd4 Set tags as part of handleAuthKeyCommon 2022-08-25 22:26:04 +10:00
Benjamin George Roberts
6faa1d2e4a Fix tests broken by preauthkey tags 2022-08-25 22:23:52 +10:00
Benjamin George Roberts
791272e408 Adds grpc/cli support for preauthkey tags 2022-08-25 22:23:46 +10:00
Benjamin George Roberts
e27a4db281 add acl_tags to PreAuthKey proto 2022-08-25 22:15:43 +10:00
Benjamin George Roberts
60cc9ddb3b Add test for autoApprovers feature 2022-08-25 22:15:19 +10:00
Benjamin George Roberts
7653ad40d6 Split GetRouteApprovers from EnableAutoApprovedRoutes 2022-08-25 22:12:30 +10:00
Benjamin George Roberts
004ebcaba1 initial implementation of autoApprovers support 2022-08-25 22:00:04 +10:00
Kristoffer Dalby
cc0bec15ef Merge pull request #760 from juanfont/update-contributors 2022-08-23 21:21:50 +02:00
github-actions[bot]
20970b580a docs(README): update contributors 2022-08-22 12:47:42 +00:00
Juan Font
53857d418a Merge pull request #756 from huskyii/env_config
Env config
2022-08-22 14:47:01 +02:00
Jiang Zhu
a81a4d274f Update CHANGELOG.md 2022-08-22 20:20:20 +08:00
Jiang Zhu
ce4a1cf447 1. add noise key to config file
2. lower node check interval
2022-08-22 00:35:08 +08:00
Jiang Zhu
35dd9209b9 update CHANGELOG.md 2022-08-21 23:51:04 +08:00
Jiang Zhu
81f91f03b4 add env var to specify config location 2022-08-21 23:51:04 +08:00
Juan Font
84a5edf345 Merge pull request #738 from juanfont/hs2021-v2
Implement TS2021 protocol in headscale
2022-08-21 16:02:28 +02:00
Juan Font Alonso
4aafe6c9d1 Added line in CHANGELOG 2022-08-21 12:32:01 +02:00
Juan Font
3ab1487641 Merge branch 'main' into hs2021-v2 2022-08-21 11:57:33 +02:00
Kristoffer Dalby
0c7f1eac82 Merge pull request #757 from juanfont/changelog-0.16.4 2022-08-21 11:15:30 +02:00
Juan Font Alonso
6fe895fd22 Updated changelog for 0.16.4 2022-08-21 10:51:58 +02:00
Juan Font Alonso
71d22dc994 Added missing files 2022-08-21 10:47:45 +02:00
Juan Font Alonso
4424a9abc0 Noise private key now a nested field in config 2022-08-21 10:42:23 +02:00
Juan Font Alonso
e20e818a42 Integrate expiration fixes (#754) in TS2021 branch 2022-08-20 11:46:44 +02:00
Juan Font
061e2fe4b4 Merge pull request #754 from Aluxima/expired-machine-registration
Fix cli registration of expired machines
2022-08-20 11:41:15 +02:00
Juan Font Alonso
f0a8a2857b Clarified why we have a different key 2022-08-20 00:23:33 +02:00
Juan Font Alonso
175dfa1ede Update flake.nix sum 2022-08-20 00:15:46 +02:00
Juan Font Alonso
04e4fa785b Updated dependencies 2022-08-20 00:11:07 +02:00
Juan Font Alonso
6aec520889 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-20 00:06:58 +02:00
Juan Font Alonso
e9906b522f Use upstream AcceptHTTP for the Noise upgrade 2022-08-20 00:06:26 +02:00
Juan Font
2f554133c5 Move comment up
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-19 23:49:06 +02:00
Juan Font Alonso
922b8b5365 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-19 16:32:18 +02:00
Juan Font Alonso
c894db3dd4 Use common core for noise registration 2022-08-19 16:29:04 +02:00
Laurent Marchaud
e85562268d Switch to using nodeKey instead of machineKey for expired machines registration
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 15:48:35 +02:00
Laurent Marchaud
fca33aacbe Fix rebased errors scope in machine.go
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 15:07:01 +02:00
Juan Font
e43713a866 Merge branch 'main' into hs2021-v2 2022-08-19 15:02:01 +02:00
Juan Font Alonso
b6e3cd81c6 Fixed minor linting things 2022-08-19 14:27:40 +02:00
Juan Font Alonso
43ad0d4416 Removed unused method 2022-08-19 14:24:43 +02:00
Juan Font Alonso
a33b5a5c00 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-19 14:20:55 +02:00
Juan Font Alonso
e2bffd4f5a Make legacy protocol use common methods for client registration 2022-08-19 14:20:24 +02:00
Juan Font Alonso
a87a9636e3 Expanded response marshal methods to support legacy and Noise 2022-08-19 14:19:29 +02:00
Laurent Marchaud
a31432ee7b Fix changelog
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:14:30 +02:00
Laurent Marchaud
0c66590108 Update changelog
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:11:19 +02:00
Laurent Marchaud
c6ea9b4b80 Fix cli registration of expired machines
Signed-off-by: Laurent Marchaud <laurent@marchaud.com>
2022-08-19 14:11:13 +02:00
Juan Font
19455399f4 Merge pull request #752 from juanfont/add-code-of-conduct
Create CODE_OF_CONDUCT.md
2022-08-19 00:38:01 +02:00
Juan Font
43ba1fb176 Prettier 2022-08-18 22:32:53 +00:00
Juan Font
a6f56b4285 Create CODE_OF_CONDUCT.md 2022-08-18 22:08:33 +02:00
Juan Font
9d430d3c72 Update noise.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-18 21:33:56 +02:00
Juan Font Alonso
f9a2a2b57a Add docker DNS IP to the remaining files 2022-08-18 18:07:15 +02:00
Juan Font Alonso
e4d961cfad Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-18 17:57:06 +02:00
Juan Font
67ffebc30a Merge branch 'main' into hs2021-v2 2022-08-18 17:56:56 +02:00
Juan Font Alonso
cf731fafab Catch retry error in taildrop send 2022-08-18 17:56:01 +02:00
Juan Font Alonso
f43a83aad7 Find out IPv4 for taildrop 2022-08-18 17:53:36 +02:00
Juan Font Alonso
7185f8dfea Only use released versions in public integration tests 2022-08-18 17:53:25 +02:00
Juan Font Alonso
8a707de5f1 Add local Docker DNS server (makes resolving http://headscale more reliable) 2022-08-18 17:53:04 +02:00
Juan Font
61bb6292b7 Merge pull request #746 from gozssky/patch-1
Fix charset typo in swagger.go
2022-08-18 12:23:45 +02:00
Juan Font
40e0ae99da Merge branch 'main' into patch-1 2022-08-18 11:49:15 +02:00
Juan Font
2dd615a4ef Merge pull request #745 from 617a7a/main
feat: add support for TLS with Postgres
2022-08-18 11:48:33 +02:00
Azz
7e06abdca2 chore: azz forgot how to write code 2022-08-17 20:12:45 +01:00
Azz
c316f53e23 fix: ci happy now? 2022-08-17 19:32:20 +01:00
Azz
b6d324be69 Merge branch 'main' of https://github.com/juanfont/headscale
# Conflicts:
#	CHANGELOG.md
2022-08-17 19:31:26 +01:00
Juan Font
f7380312d3 Merge pull request #747 from juanfont/fix-oidc
Fix error decoding OIDC claims (#744)
2022-08-17 18:30:50 +02:00
Juan Font
287309b65c Update changelog 2022-08-17 15:08:29 +00:00
Juan Font
cc3de7e723 Fix error decoding claims (#744) 2022-08-17 15:03:10 +00:00
Yujie Xia
e03b3029e3 Fix charset typo in swagger.go 2022-08-17 12:27:58 +08:00
Juan Font Alonso
ba07bac46a Use IPv4 in the tests 2022-08-16 18:42:22 +02:00
Juan Font Alonso
b71a881d0e Retry magicdns tests 2022-08-16 18:19:04 +02:00
Juan Font Alonso
ce53bb0eee Minor changes to HEAD Dockerfile 2022-08-16 17:52:59 +02:00
Juan Font Alonso
c0fe1abf4d Use node_key to find peers 2022-08-16 17:51:43 +02:00
Juan Font Alonso
0db7fc5ab7 Mark all namespaces to lastChange now 2022-08-16 13:39:15 +02:00
azz
701ad3e017 chore: update CHANGELOG.md 2022-08-16 09:09:28 +01:00
azz
0cc14d0aca feat: added db_ssl to config-example.yaml 2022-08-16 09:02:51 +01:00
Azz
3f5ea7998f Merge branch 'main' into main 2022-08-16 08:56:36 +01:00
azz
4c7f54020b feat: add support for TLS with Postgres 2022-08-16 08:50:30 +01:00
Juan Font Alonso
eb461d0713 Enable HEAD and unstable in integration tests 2022-08-16 00:18:02 +02:00
Juan Font Alonso
128ec6717c Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-15 23:35:24 +02:00
Juan Font Alonso
b3cf5289f8 Use CapVer to offer Noise only to supported clients 2022-08-15 23:35:06 +02:00
Juan Font
c701f9e817 Merge branch 'main' into hs2021-v2 2022-08-15 22:56:39 +02:00
Juan Font
e1a95e2057 Merge pull request #734 from vtrf/postgres-connection-string
Add ability to connect to PostgreSQL via unix socket
2022-08-15 19:20:01 +02:00
Victor Freire
0a5db52855 Add ability to connect to PostgreSQL via unix socket 2022-08-15 11:55:38 -03:00
Juan Font
7197ade4b4 Merge branch 'main' into postgres-connection-string 2022-08-15 13:37:09 +02:00
Juan Font Alonso
865f1ffb3c Fix issues with DERP integration tests due to tailscale/tailscale#4323 2022-08-15 11:25:47 +02:00
Juan Font Alonso
8db7629edf Fix config file in integration tests for Noise 2022-08-15 10:53:06 +02:00
Juan Font Alonso
b8980b9ed3 More minor logging stuff 2022-08-15 10:44:22 +02:00
Juan Font Alonso
5cf9eedf42 Minor logging corrections 2022-08-15 10:43:39 +02:00
Juan Font
193b4213b3 Merge pull request #739 from juanfont/updated-changelog-0.16.2
Added changelog entries for 0.16.x
2022-08-14 23:27:27 +02:00
Juan Font Alonso
8557bcedae Added changelog entries for 0.16.x 2022-08-14 23:22:41 +02:00
Juan Font Alonso
f599bea216 Fixed issue when not using compression 2022-08-14 23:15:41 +02:00
Juan Font Alonso
704a19b0a5 Removed legacy method to generate MapResponse 2022-08-14 23:13:07 +02:00
Juan Font Alonso
e29b344e0f Move Noise poll to new file, and use common poll 2022-08-14 23:12:18 +02:00
Juan Font Alonso
7cc227d01e Added Noise field to logging 2022-08-14 23:11:33 +02:00
Juan Font Alonso
df8ecdb603 Working on common codebase for poll, starting with legacy 2022-08-14 22:57:03 +02:00
Juan Font Alonso
f4bab6b290 Created common methods for keep and map poll responses 2022-08-14 22:50:39 +02:00
Juan Font Alonso
35f3dee1d0 Move Noise API to new file 2022-08-14 21:19:52 +02:00
Juan Font Alonso
db89fdea23 Added file for legacy protocol 2022-08-14 21:16:29 +02:00
Juan Font Alonso
d0898ecabc Move common parts of the protocol to dedicated file 2022-08-14 21:15:58 +02:00
Juan Font Alonso
e640c6df05 Fixes in Noise poll (clients should work now) 2022-08-14 21:10:08 +02:00
Juan Font Alonso
ab18c721bb Support for Noise machines in getPeers 2022-08-14 21:07:29 +02:00
Juan Font Alonso
aaa33cf093 Minor change in router 2022-08-14 21:07:05 +02:00
Juan Font Alonso
0f09e19e38 Updated go.mod checksum 2022-08-14 17:09:14 +02:00
Juan Font Alonso
b301405f24 Merge branch 'hs2021-v2' of https://github.com/juanfont/headscale into hs2021-v2 2022-08-14 17:06:03 +02:00
Juan Font Alonso
1f3032ad21 Merge branch 'main' into hs2021-v2 2022-08-14 17:05:51 +02:00
Juan Font Alonso
c10142f767 Added noise poll handler 2022-08-14 17:05:04 +02:00
Juan Font Alonso
0d0042b7e6 Added zstd constant for linting 2022-08-14 17:04:07 +02:00
Juan Font Alonso
78a179c971 Minor update in docs 2022-08-14 16:53:54 +02:00
Juan Font Alonso
cab828c9d4 Fixed unit tests to load config 2022-08-14 16:52:57 +02:00
Juan Font Alonso
ff46f3ff49 Move reusable method to common api file 2022-08-14 16:13:17 +02:00
Juan Font
b67cff50f5 Merge branch 'main' into hs2021-v2 2022-08-14 13:44:12 +02:00
Juan Font
e29ac8a4ab Merge pull request #737 from juanfont/fix-machinekey-oidc
Fixed another recurrence of MachineKey
2022-08-14 13:44:01 +02:00
Juan Font Alonso
20d2615081 Check json encoder errors 2022-08-14 12:47:04 +02:00
Juan Font
7fb2f83540 Merge branch 'main' into fix-machinekey-oidc 2022-08-14 12:44:30 +02:00
Juan Font Alonso
eb8d8f142c And more linting stuff 2022-08-14 12:44:07 +02:00
Juan Font Alonso
3bea20850a Some linting fixes 2022-08-14 12:40:22 +02:00
Juan Font Alonso
ade1b73779 Output an error when a user runs headscale without noise_private_key_path defined 2022-08-14 12:35:14 +02:00
Juan Font Alonso
281ae59b5a Update integration tests to work with Noise protocol 2022-08-14 12:18:33 +02:00
Juan Font Alonso
90bb6ea907 Minor formatting changes 2022-08-14 12:10:20 +02:00
Juan Font Alonso
5b14cafddd Fixed another recurrence of MachineKey 2022-08-14 12:04:31 +02:00
Juan Font Alonso
9994fce9d5 Fixed some linting errors 2022-08-14 12:00:43 +02:00
Kristoffer Dalby
c19e1a481e Merge pull request #736 from juanfont/update-contributors 2022-08-14 08:16:58 +02:00
Juan Font Alonso
39b85b02bb Move getMapResponse into reusable function by TS2019 and TS2021 2022-08-14 03:20:53 +02:00
Juan Font Alonso
7a91c82cda Merge branch 'main' into hs2021-v2 2022-08-14 03:07:43 +02:00
Juan Font Alonso
c7cea9ef16 updated paths 2022-08-14 03:07:28 +02:00
github-actions[bot]
d56b409cb9 docs(README): update contributors 2022-08-13 20:44:38 +00:00
Juan Font
ee8f38111e Merge pull request #735 from juanfont/fix-expired-url
Fix expired node registration URL
2022-08-13 22:44:04 +02:00
Juan Font Alonso
8c13f64d3c Changed missing path 2022-08-13 21:55:44 +02:00
Juan Font Alonso
a7efc22045 Fix expired node registration URL 2022-08-13 21:17:05 +02:00
Juan Font Alonso
1880035f6f Add registration handler over Noise protocol 2022-08-13 21:12:19 +02:00
Juan Font Alonso
fdd0c50402 Added helper method to fetch machines by any nodekey + tests 2022-08-13 21:03:02 +02:00
Juan Font Alonso
be24bacb79 Add noise mux and Noise path to base router 2022-08-13 20:55:37 +02:00
Juan Font Alonso
b261d19cfe Added Noise upgrade handler and Noise mux 2022-08-13 20:52:11 +02:00
Victor Freire
ec5acf7be2 Add ability to connect to PostgreSQL via unix socket 2022-08-13 11:34:12 -03:00
Juan Font Alonso
014e7abc68 Make private key errors constants 2022-08-13 14:46:23 +02:00
Juan Font Alonso
3e8f0e9984 Added support for Noise clients in /key handler 2022-08-13 11:24:05 +02:00
Juan Font Alonso
6e8e2bf508 Generate and read the Noise private key 2022-08-13 11:14:38 +02:00
Juan Font
09cd7ba304 Merge pull request #725 from juanfont/switch-to-db-d
Improve registration protocol implementation and switch to NodeKey as main identifier
2022-08-12 09:56:17 +02:00
Juan Font Alonso
77bf1e81ec Added missing dot in comment 2022-08-12 09:36:17 +02:00
Juan Font Alonso
a9b9a2942d Update changelog 2022-08-12 09:31:11 +02:00
Juan Font
a261e27113 Update api.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-12 09:03:32 +02:00
Juan Font
f01a33491b Update api.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-12 09:03:11 +02:00
Juan Font
739e11e1ee Update api.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-08-12 09:02:58 +02:00
Juan Font
393aae01df Merge branch 'main' into switch-to-db-d 2022-08-11 15:02:08 +02:00
Juan Font
73cd428ed2 Merge pull request #729 from juanfont/fix-reuse-of-ns
Minor fix to linting issue introduced when fixing excludeCorrectlyTaggedNodes (#707)
2022-08-11 15:01:53 +02:00
Juan Font Alonso
1e7b57e513 Minor fix to linting issue introduced in #707 2022-08-11 14:12:45 +02:00
Juan Font Alonso
e1e3feb6a8 Add a sleep to reduce the impact of #727 2022-08-11 13:37:25 +02:00
Juan Font
8e56d8b425 Merge branch 'main' into switch-to-db-d 2022-08-11 13:11:38 +02:00
Juan Font
6c8445988c Merge pull request #707 from restanrm/fix-bug-in-excludecorrectlytaggednodes
Fix bug in excludeCorrectlyTaggedNodes
2022-08-11 13:08:43 +02:00
Adrien Raffin-Caboisse
110b01befa Merge remote-tracking branch 'origin/main' into fix-bug-in-excludecorrectlytaggednodes 2022-08-11 12:49:26 +02:00
Juan Font Alonso
d586b9d285 Added comment clarifying registration API 2022-08-11 12:16:50 +02:00
Juan Font Alonso
804d70386d Switch to nodekey in urls 2022-08-11 12:15:16 +02:00
Juan Font Alonso
fb3b2e6bc8 Improve protocol implementation for client registration (fixes #706) 2022-08-11 12:11:02 +02:00
Juan Font Alonso
030d7264e6 Fixed comment for linting 2022-08-10 16:03:33 +02:00
Juan Font Alonso
e91c378bd4 Replace machine key with node key in preparation for Noise in auth related stuff 2022-08-10 15:35:26 +02:00
Juan Font Alonso
e950b3be29 Add method to fetch by nodekey 2022-08-10 13:15:31 +02:00
Juan Font
dbf0e206b8 Merge pull request #722 from juanfont/bump-versions-20220810
Update dependencies versions
2022-08-10 11:25:03 +02:00
Juan Font Alonso
84f66090fd Updated CHangelog and flake 2022-08-10 11:04:42 +02:00
Juan Font Alonso
f8958d4e22 Update xsync library (helps in #704) 2022-08-10 10:55:45 +02:00
Juan Font Alonso
70807e40f6 Updated base dependencies 2022-08-10 10:54:23 +02:00
Juan Font Alonso
9a01e3d192 Bump tailscale to 1.28.0 2022-08-10 10:47:49 +02:00
Juan Font
a03a99569d Merge pull request #720 from juanfont/replace-ioutil
Replaced legacy ioutil usage
2022-08-10 07:41:50 +02:00
Juan Font Alonso
2d887046de Replaced legacy ioutil usage 2022-08-09 23:21:19 +02:00
Juan Font
3a091896fb Merge pull request #685 from GrigoriyMikhalkin/oidc-refactoring
Decompose OIDCCallback method
2022-08-09 20:52:32 +02:00
Juan Font
8a9fe1da4b Merge branch 'main' into oidc-refactoring 2022-08-09 20:29:02 +02:00
Juan Font
abf478c9e6 Merge pull request #703 from nnsee/android-readme
Update the readme and documentation with details on the Android app
2022-08-09 15:39:45 +02:00
Juan Font
913a94d2ab Merge branch 'main' into android-readme 2022-08-09 15:37:20 +02:00
Juan Font
01e5be3b57 Merge pull request #711 from sophware/typofix
typo fixed from advertised to advertise
2022-08-09 15:11:23 +02:00
Juan Font
e93529e9f3 Merge branch 'main' into typofix 2022-08-09 15:05:53 +02:00
Juan Font
ade4e23e14 Merge pull request #698 from GrigoriyMikhalkin/export-errors
Export API errors
2022-08-09 15:04:51 +02:00
Juan Font
fc65ded2d5 Merge branch 'main' into oidc-refactoring 2022-08-09 14:37:58 +02:00
Juan Font
aa2b92703f Merge branch 'main' into export-errors 2022-08-09 14:33:10 +02:00
Juan Font
2c9dbe158d Merge pull request #713 from juanfont/update-buf-lint
Update buf setup action for proto-lint
2022-08-09 14:32:52 +02:00
Juan Font Alonso
d6fa5c96ae Update setup action for proto lint 2022-08-09 14:21:45 +02:00
Juan Font
0506e68a96 Merge branch 'main' into export-errors 2022-08-09 14:16:24 +02:00
Juan Font
b32f986105 Merge pull request #710 from juanfont/cosmetic-changes-integration
Improvements in integration tests
2022-08-09 14:16:10 +02:00
Juan Font Alonso
577eedef11 Restore the number of containers 2022-08-09 13:53:25 +02:00
Juan Font Alonso
27855880b2 Updated versions for taiscale 2022-08-09 13:53:02 +02:00
Juan Font Alonso
b01d392f9e Run integrtation tests in different steps in Github Actions 2022-08-09 12:26:58 +02:00
Juan Font Alonso
d548f5de3f Splitted integration tests in Makefile 2022-08-09 12:26:29 +02:00
Juan Font Alonso
f8986132d4 Use tags to split the integration tests 2022-08-09 12:26:12 +02:00
Juan Font Alonso
e7148b8080 Temporarily disable unstable branch 2022-08-09 09:58:45 +02:00
Juan Font Alonso
0a29492fc5 Increase swap size in integration tests 2022-08-08 23:20:29 +02:00
Grigoriy Mikhalkin
a1e7e771ce refactor OIDC callback aux functions 2022-08-07 13:57:07 +02:00
Grigoriy Mikhalkin
00d2a447f4 decompose OIDCCallback method 2022-08-07 13:27:29 +02:00
Steve Malloy
2254ac2102 typo fixed from advertised to advertise 2022-08-05 15:44:11 -04:00
Juan Font Alonso
21ae31e77d Reduce number of containers in integration tests (for testing) 2022-08-05 18:57:08 +02:00
Juan Font Alonso
a6113066ff Improved logs in integration tests 2022-08-05 17:35:28 +02:00
Adrien Raffin-Caboisse
0bb205d31f Merge remote-tracking branch 'origin/main' into fix-bug-in-excludecorrectlytaggednodes 2022-08-05 11:56:33 +02:00
Juan Font
d7e8db7adc Merge branch 'main' into export-errors 2022-08-05 10:14:35 +02:00
Juan Font
0eb3b23f16 Merge pull request #708 from juanfont/revert-680-topic/speedup-build
Revert BuildKit (docker buildx) support
2022-08-05 10:14:19 +02:00
Juan Font
54e381cecb Revert "Topic/speedup build" 2022-08-05 00:31:39 +02:00
Grigoriy Mikhalkin
cc1343d31d fixed typo in ErrCannotDecryptResponse name 2022-08-05 00:00:36 +02:00
Adrien Raffin-Caboisse
bce59345e4 docs: add entry in changelog 2022-08-04 10:51:06 +02:00
Adrien Raffin-Caboisse
79688e6187 chore(all): apply formater 2022-08-04 10:47:00 +02:00
Adrien Raffin-Caboisse
babf9470c2 fix(acl): fix issue with groups in excludeCorretlyTaggedNodes
This commit fix issue #563
2022-08-04 10:42:47 +02:00
Rasmus Moorats
10d566c946 add details on how to use the android app 2022-08-02 09:49:28 +03:00
Grigoriy Mikhalkin
911e6ba6de exported API errors 2022-07-29 17:35:21 +02:00
Juan Font
f9c4d577e2 Merge pull request #680 from ohdearaugustin/topic/speedup-build
Topic/speedup build
2022-07-28 23:07:32 +02:00
Juan Font
9826b518bd Merge branch 'main' into topic/speedup-build 2022-07-28 22:58:07 +02:00
Juan Font
32a8f06486 Merge pull request #689 from restanrm/fix-duplicate-tags-returned-by-api
Remove duplicate tags if sent by the client
2022-07-28 22:52:35 +02:00
Juan Font
2ab2b8656b Merge branch 'main' into fix-duplicate-tags-returned-by-api 2022-07-27 00:37:07 +02:00
Juan Font
d9ab98e47f Merge branch 'main' into topic/speedup-build 2022-07-26 18:45:41 +02:00
Juan Font
9d584bb0d3 Merge pull request #692 from juanfont/update-runc-dependencies
Update runc dependencies to fix security notification
2022-07-26 17:20:51 +02:00
Juan Font
4f725ba9e1 Merge branch 'main' into update-runc-dependencies 2022-07-26 13:59:35 +02:00
Juan Font
b75a113c91 Merge pull request #688 from juanfont/prepare-cl-0.17.0
Prepare changelog structure for 0.17.0
2022-07-26 13:59:24 +02:00
Juan Font Alonso
75af83bb81 Update checksum for nix 2022-07-26 12:11:15 +02:00
Juan Font
0f6f0c3b6b Merge branch 'main' into prepare-cl-0.17.0 2022-07-26 12:05:28 +02:00
Juan Font Alonso
b344524a6d Update runc dependencies to fix security notification 2022-07-26 12:02:58 +02:00
Juan Font Alonso
6f4d5a532e fixed linting errors 2022-07-26 11:25:20 +02:00
Juan Font
2d83c70173 Merge pull request #670 from iSchluff/feature/db-health-check
ping db in health check
2022-07-26 00:40:23 +02:00
Adrien Raffin-Caboisse
c90e862460 fix(grpc): add more checks for tag validation 2022-07-25 14:01:41 +02:00
Adrien Raffin-Caboisse
c46a34e6b8 fix(machine): remove duplicate in forcedTags 2022-07-25 11:04:30 +02:00
Juan Font Alonso
693f59ba2f Prepare changelog structure for 0.17.0 2022-07-25 10:35:21 +02:00
Juan Font
abae078855 Merge branch 'main' into feature/db-health-check 2022-07-24 22:10:16 +02:00
Juan Font
0212db3fad Merge pull request #687 from huskyii/node_ls
more intuitive output of node ls
2022-07-24 12:06:41 +02:00
Jiang Zhu
49354f678e update CHANGELOG 2022-07-23 04:47:37 +08:00
Jiang Zhu
dc94570c4a more intuitive output of node ls 2022-07-23 01:33:11 +08:00
Kristoffer Dalby
51b1027aec Merge pull request #686 from juanfont/update-contributors 2022-07-22 18:56:49 +02:00
github-actions[bot]
936adb7d2c docs(README): update contributors 2022-07-22 07:36:16 +00:00
Juan Font
581d1f3bfa Merge pull request #668 from GrigoriyMikhalkin/graceful-shutdown
graceful shutdown fix
2022-07-22 09:35:40 +02:00
Juan Font
7c87ef6c86 Merge branch 'main' into graceful-shutdown 2022-07-22 09:06:46 +02:00
Juan Font
1a9a9b718d Merge pull request #684 from juanfont/fix-api-mux
Fix API router
2022-07-22 09:06:06 +02:00
Juan Font Alonso
6c9f3420e2 Updated changelog 2022-07-21 23:59:44 +02:00
Juan Font Alonso
a4d0efbe8d Fix API router 2022-07-21 23:57:07 +02:00
Grigoriy Mikhalkin
56858a56db Revert "decompose OIDCCallback method"
This reverts commit 395caaad42.
2022-07-21 23:54:35 +02:00
Grigoriy Mikhalkin
395caaad42 decompose OIDCCallback method 2022-07-21 23:47:20 +02:00
Grigoriy Mikhalkin
3f0639c87d graceful shutdown lint fixes 2022-07-21 23:47:20 +02:00
Grigoriy Mikhalkin
889eff265f graceful shutdown fix 2022-07-21 23:47:20 +02:00
Kristoffer Dalby
c6eb7be7fb Merge pull request #683 from juanfont/update-contributors 2022-07-20 10:57:38 +02:00
github-actions[bot]
02c7a46b97 docs(README): update contributors 2022-07-20 07:21:19 +00:00
Kristoffer Dalby
ea7b3baa8b Merge pull request #677 from huskyii/remove_gin 2022-07-20 09:20:24 +02:00
Jiang Zhu
5724f4607c fix nix build 2022-07-19 20:45:32 +08:00
Jiang Zhu
b755d47652 update CHANGELOG 2022-07-19 20:45:23 +08:00
ohdearaugustin
96221cc4f7 docs: add bulding container docs 2022-07-17 21:18:04 +02:00
ohdearaugustin
34d261179e Speedup docker container build 2022-07-17 21:18:04 +02:00
ohdearaugustin
091b05f155 Change build os 2022-07-17 21:18:04 +02:00
Jiang Zhu
aca5646032 remove gin completely, ~2MB reduction on final binary 2022-07-16 02:03:46 +08:00
Kristoffer Dalby
7e9abbeaec Merge pull request #676 from juanfont/update-contributors 2022-07-15 09:15:18 +01:00
Anton Schubert
c6aaa37f2d ping db in health check 2022-07-12 22:56:53 +02:00
github-actions[bot]
b8c3387892 docs(README): update contributors 2022-07-12 11:35:28 +00:00
Juan Font
c50d3aa9bd Merge pull request #675 from juanfont/configurable-update-interval
Make tailnet updates check interval configurable
2022-07-12 13:34:49 +02:00
Juan Font Alonso
4ccff8bf28 Added the new parameter to the integration test params 2022-07-12 13:13:04 +02:00
Juan Font Alonso
5b5298b025 Renamed config param for node update check internal 2022-07-12 12:52:03 +02:00
Juan Font Alonso
8e0939f403 Updated changelog 2022-07-12 12:33:42 +02:00
Juan Font Alonso
cf3fc85196 Make tailnet updates check configurable 2022-07-12 12:27:28 +02:00
Juan Font
e0b15c18ce Merge pull request #667 from kradalby/rerun-docker
Make integration tests retry on failure.
2022-06-27 17:04:39 +02:00
Kristoffer Dalby
566b8c3df3 Fix issue were dockertest fails to start because of container mismatch 2022-06-27 12:07:30 +00:00
Kristoffer Dalby
32a6151df9 Rerun integration tests 5 times if error 2022-06-27 12:02:29 +00:00
Kristoffer Dalby
3777de7133 Use failnow for cli tests aswell 2022-06-27 12:00:21 +00:00
Kristoffer Dalby
8cae4f80d7 Fail tests instead of fatal
Currently we exit the program if the setup does not work, this can cause
is to leave containers and other resources behind since we dont run
TearDown. This change will just fail the test if we cant set up, which
should mean that the TearDown runs aswell.
2022-06-27 11:58:16 +00:00
Kristoffer Dalby
911c5bddce Make saving logs from tests an option (default false)
We currently have a bit of flaky logic which prevents the docker plugin
from cleaning up the containers if the tests or setup fatals or crashes,
this is due to a limitation in the save / passed stats handling.

This change makes it an environment variable which by default ditches
the logs and makes the containers clean up "correctly" in the teardown
method.
2022-06-27 11:56:37 +00:00
Juan Font
4a200c308b Merge pull request #656 from juanfont/abandon-gin
Drop Gin as web framework for TS2019 API
2022-06-26 15:54:41 +02:00
Juan Font Alonso
625e45b1cb Merge branch 'abandon-gin' of https://github.com/juanfont/headscale into abandon-gin 2022-06-26 14:25:05 +02:00
Juan Font Alonso
8551b0dde0 Fixed issue when in linting rampage 2022-06-26 14:24:57 +02:00
Juan Font
050782aff3 Merge branch 'main' into abandon-gin 2022-06-26 12:36:49 +02:00
Juan Font Alonso
00885dffe1 Fix implicit memory aliasing in for loop (lint 8/n) 2022-06-26 12:35:18 +02:00
Juan Font Alonso
ffcc72876c Lint fixes 7/n 2022-06-26 12:30:52 +02:00
Juan Font Alonso
fa91ece5b4 Lint fixes 6/n 2022-06-26 12:25:26 +02:00
Juan Font Alonso
c810b24eb9 Lint fixes 5/n 2022-06-26 12:21:35 +02:00
Juan Font Alonso
03ced0ecfe Lint fixes 4/n 2022-06-26 12:06:25 +02:00
Juan Font Alonso
c859bea0cf Lint fixes 3/n 2022-06-26 12:01:04 +02:00
Juan Font Alonso
a913d1b521 Lint fixes 2/n 2022-06-26 11:55:37 +02:00
Kristoffer Dalby
2464c92572 Merge pull request #665 from juanfont/update-contributors 2022-06-26 11:48:11 +02:00
Juan Font Alonso
10cd87e5a2 Lint fixes 1/n 2022-06-26 11:43:17 +02:00
Juan Font Alonso
58c336e7f4 updated nix flake go.sum 2022-06-26 11:31:31 +02:00
Juan Font
bb4a9583a7 Merge branch 'main' into abandon-gin 2022-06-26 11:08:48 +02:00
github-actions[bot]
7ae38346e5 docs(README): update contributors 2022-06-26 08:22:05 +00:00
Kristoffer Dalby
7604c0f691 Merge pull request #658 from juanfont/fix-segfault-when-not-runner 2022-06-26 10:21:27 +02:00
Kristoffer Dalby
f2f4c3f684 Merge branch 'main' into fix-segfault-when-not-runner 2022-06-26 09:52:15 +02:00
Kristoffer Dalby
34f489b1f4 Update cmd/headscale/cli/utils.go 2022-06-26 09:52:11 +02:00
Kristoffer Dalby
72d1d2630e Update cmd/headscale/cli/utils.go 2022-06-26 09:52:04 +02:00
Kristoffer Dalby
d559e23bc6 Merge pull request #651 from iSchluff/fix/db-shutdown 2022-06-26 09:51:45 +02:00
Kristoffer Dalby
4637400d29 Update CHANGELOG.md 2022-06-26 09:30:16 +02:00
Kristoffer Dalby
0fa943e4b7 Update CHANGELOG.md 2022-06-26 09:29:33 +02:00
Kristoffer Dalby
9707b1f540 Merge branch 'main' into fix/db-shutdown 2022-06-26 08:28:50 +01:00
Juan Font Alonso
657fb208d6 Flush buffered data on polling 2022-06-25 20:47:42 +02:00
Juan Font
647972c7cf Merge branch 'main' into fix-segfault-when-not-runner 2022-06-23 22:17:33 +02:00
Juan Font Alonso
39b58f7d4c Use a signal to close the longpolls on shutdown 2022-06-23 19:40:07 +02:00
Juan Font Alonso
c8378e8b7d Quick fix to segfault on CLI when Headscale is not running (fix #652) 2022-06-22 14:40:40 +02:00
Juan Font Alonso
d404ba102d Use request context to close when client disconnects 2022-06-20 21:47:02 +02:00
Juan Font Alonso
5e9004c407 Fix issues in the poll loop 2022-06-20 21:40:28 +02:00
Juan Font Alonso
8e63b53b0c Merge branch 'abandon-gin' of https://github.com/juanfont/headscale into abandon-gin 2022-06-20 21:38:03 +02:00
Juan Font Alonso
116bef25a7 Fixed wrong copy paste in Header 2022-06-20 21:19:49 +02:00
Juan Font
294975ba87 Merge branch 'main' into abandon-gin 2022-06-20 21:16:11 +02:00
Juan Font Alonso
51b8c659f1 Updated changelog 2022-06-20 21:13:12 +02:00
Juan Font Alonso
082fbead66 Added mux dependency 2022-06-20 21:12:23 +02:00
Juan Font Alonso
73c16ffc65 Fixed issue with the method used to send data 2022-06-20 20:30:08 +02:00
Juan Font Alonso
dec51348e6 Minor status change 2022-06-20 20:29:42 +02:00
Juan Font Alonso
b0b919efb0 Added more logging to derp server 2022-06-20 12:32:13 +02:00
Juan Font Alonso
396c3ecdf7 Remove Gin from the OIDC handlers 2022-06-20 12:31:19 +02:00
Juan Font Alonso
53e5c05b0a Remove gin from the poll handlers 2022-06-20 12:30:51 +02:00
Juan Font Alonso
dedeb4c181 Remove Gin from the Registration handler 2022-06-20 12:30:41 +02:00
Juan Font Alonso
e611063669 Migrate platform config out of Gin 2022-06-20 12:29:59 +02:00
Juan Font Alonso
6c9c9a401f Remove gin from DERP server 2022-06-18 19:51:37 +02:00
Juan Font
6da4396faa Merge pull request #654 from ChibangLW/main
Add version info to binary in Docker container
2022-06-18 18:48:35 +02:00
Juan Font Alonso
d89fb68a7a Switch to use gorilla's mux as muxer 2022-06-18 18:41:42 +02:00
Leon Lenzen
8d9462147c chore: use docker-meta version 2022-06-18 12:00:02 +02:00
Leon Lenzen
89b7fa6b06 chore: fix lint 2022-06-18 11:39:27 +02:00
Leon Lenzen
d4a550bb4c chore: add version to binary in containers 2022-06-18 11:36:09 +02:00
Juan Font Alonso
d5e331a2fb Remove Gin from OIDC callback 2022-06-17 17:42:17 +02:00
Juan Font Alonso
367da0fcc2 Remove Gin from simple endpoints for TS2019 2022-06-17 16:48:04 +02:00
Anton Schubert
8111b0aa83 update changelog 2022-06-17 11:07:35 +02:00
Anton Schubert
735440d1a3 add timeout for http shutdown, add db disconnect 2022-06-17 11:07:25 +02:00
Juan Font
3ae340527f Merge pull request #648 from juanfont/show-nodes-online
Send Online field of tailcfg.Node based on LastSeen
2022-06-16 19:26:13 +02:00
Juan Font
bfa9ed814d Merge branch 'main' into show-nodes-online 2022-06-16 18:53:25 +02:00
Juan Font Alonso
1e4678c02f Updated changelog 2022-06-16 18:48:32 +02:00
Juan Font Alonso
66fffd69ce Send Online field of tailcfg.Node based on LastSeen 2022-06-16 18:43:50 +02:00
Kristoffer Dalby
e3f99d670e Merge pull request #646 from juanfont/update-contributors 2022-06-16 16:08:31 +01:00
github-actions[bot]
360488abb4 docs(README): update contributors 2022-06-16 13:08:07 +00:00
Kristoffer Dalby
8dda44105e Merge pull request #643 from iSchluff/fix/dns-name-panic 2022-06-16 14:07:21 +01:00
Kristoffer Dalby
2215e17223 Merge branch 'main' into fix/dns-name-panic 2022-06-16 11:04:31 +01:00
Kristoffer Dalby
157db307f9 Merge pull request #642 from kradalby/ignore-integtest-dump 2022-06-16 08:35:47 +01:00
Juan Font
0bd39b2c5e Merge branch 'main' into ignore-integtest-dump 2022-06-16 00:25:45 +02:00
Anton Schubert
8f31ed51e1 fix occasional panic on registration
GenerateRandomStringDNSSafe will panic occasionally if the random base64
string contains too many - and _ due to the replacement. Fix by looping.
2022-06-15 12:22:57 +02:00
Kristoffer Dalby
d2d1f92836 Merge pull request #641 from juanfont/update-contributors 2022-06-12 22:57:32 +01:00
Kristoffer Dalby
c02819ab9f Ignore new dump file 2022-06-12 17:26:44 +00:00
github-actions[bot]
28a3a5bd61 docs(README): update contributors 2022-06-12 17:00:23 +00:00
Kristoffer Dalby
891815634b Merge pull request #639 from kradalby/ephemeral-error-msg 2022-06-12 17:59:48 +01:00
Kristoffer Dalby
8650328922 Remove debug output, it runs before we disable it 2022-06-12 16:40:43 +00:00
Kristoffer Dalby
7bd07e3b9b Merge branch 'main' into ephemeral-error-msg 2022-06-12 14:33:49 +01:00
Kristoffer Dalby
76195bb3ac Add warn if configuration could not be found 2022-06-12 13:32:16 +00:00
Juan Font
6afd492095 Merge pull request #638 from kradalby/update-nodes-derp
Simplify DERP maps update function
2022-06-12 15:26:20 +02:00
Kristoffer Dalby
c95bce4aea Update changelog 2022-06-12 13:18:49 +00:00
Kristoffer Dalby
fd3a1c13e3 Add a default to ephemeral_node_inactivity_timeout 2022-06-12 13:12:53 +00:00
Kristoffer Dalby
95824ac2ec MOve ephemeral inactivity config check to all the other config check 2022-06-12 13:12:43 +00:00
Kristoffer Dalby
a050158d11 Use new update state logic for derp maps 2022-06-12 12:27:37 +00:00
Kristoffer Dalby
e0ef601123 Merge pull request #636 from huskyii/fix_issue635 2022-06-12 12:53:19 +01:00
Jiang Zhu
9c5d485fdd fix issue 635 2022-06-12 17:01:17 +08:00
Juan Font
cb88b16207 Merge pull request #630 from kradalby/test-126
Add 1.26 to tests
2022-06-11 18:14:38 +02:00
Kristoffer Dalby
257c025975 Update build system 2022-06-11 15:42:06 +00:00
Kristoffer Dalby
50bdf9d3b9 Update vendor sha 2022-06-11 15:39:37 +00:00
Kristoffer Dalby
8d58894daa Tailscale 1.26 uses dnstype pointer 2022-06-11 15:34:02 +00:00
Kristoffer Dalby
43fa7f9fd5 Upgrade tailscale lib to 1.26 2022-06-11 15:34:02 +00:00
Kristoffer Dalby
f2a8bfeb9f Merge branch 'main' into test-126 2022-06-11 16:04:35 +01:00
Kristoffer Dalby
06bbeea37f Merge pull request #632 from juanfont/update-contributors 2022-06-11 16:04:21 +01:00
github-actions[bot]
e5f26f819a docs(README): update contributors 2022-06-11 14:35:56 +00:00
Kristoffer Dalby
a058f17946 Merge branch 'main' into test-126 2022-06-11 15:35:36 +01:00
Kristoffer Dalby
a4b4fc8b6c Merge pull request #624 from iSchluff/feature/configure-randomize-port 2022-06-11 15:35:24 +01:00
Kristoffer Dalby
ab35baaa29 Merge branch 'main' into feature/configure-randomize-port 2022-06-11 15:07:47 +01:00
Kristoffer Dalby
883bb92991 Merge pull request #618 from juanfont/acl-syntax-fixes 2022-06-11 15:07:29 +01:00
Kristoffer Dalby
bfb58de7b8 Add 1.26 to tests 2022-06-11 13:45:32 +00:00
Kristoffer Dalby
6faf2d63d0 Update integration dump tests 2022-06-11 13:31:30 +00:00
Kristoffer Dalby
569f3caab9 Use constants in tests 2022-06-11 13:17:07 +00:00
Kristoffer Dalby
7cd0f5e8a4 Merge branch 'main' into acl-syntax-fixes 2022-06-11 14:14:21 +01:00
Kristoffer Dalby
02cc6bcc05 Merge branch 'main' into feature/configure-randomize-port 2022-06-11 13:49:32 +01:00
Kristoffer Dalby
9ff09b73ad Update Changelog 2022-06-11 13:49:17 +01:00
Kristoffer Dalby
f93cf4b980 Merge pull request #628 from kradalby/acl-update-nodes 2022-06-11 13:32:57 +01:00
Juan Font Alonso
3d7be5b287 Minor rename 2022-06-11 14:12:53 +02:00
Juan Font Alonso
cdf41bd500 Merge branch 'acl-syntax-fixes' of https://github.com/juanfont/headscale into acl-syntax-fixes 2022-06-11 14:12:39 +02:00
Juan Font Alonso
735a6aaa39 Use const for IANA protcol numbers 2022-06-11 14:09:08 +02:00
Kristoffer Dalby
0c2648c188 Update the nodes after we have reloaded the ACL policy with sighup 2022-06-11 12:54:44 +01:00
Kristoffer Dalby
7e6291c21c Change Set state change function to filter instead of single namespace
This commit makes the setLastStateChangeToNow function take a list of
namespaces instead of a single namespace. If no namespaces is passed,
all namespaces will be updated. This means that the argument acts like a
filter.
2022-06-11 12:53:02 +01:00
Kristoffer Dalby
3f7749c6d4 Merge branch 'main' into feature/configure-randomize-port 2022-06-11 10:55:05 +01:00
Kristoffer Dalby
586c5411f1 Merge pull request #611 from huskyii/doc_openbsd 2022-06-11 10:54:28 +01:00
Jiang Zhu
2be16b581c 1) fix typo 2) another hard coded version 2022-06-11 17:23:01 +08:00
Jiang Zhu
06e22bf878 Merge branch 'juanfont:main' into doc_openbsd 2022-06-11 16:54:01 +08:00
Jiang Zhu
0b4b530809 remove the hardcoded version(suggested by @kradalby) 2022-06-11 16:41:52 +08:00
Kristoffer Dalby
efca3daa5c Merge pull request #612 from huskyii/enhance_cli_config 2022-06-10 20:38:11 +01:00
Kristoffer Dalby
fdefe46c40 Merge branch 'main' into enhance_cli_config 2022-06-10 20:18:33 +01:00
Anton Schubert
34be10840c add ability to set randomizeClientPort 2022-06-09 21:26:40 +02:00
Juan Font
80ad1db228 Merge branch 'main' into acl-syntax-fixes 2022-06-09 14:09:26 +02:00
Juan Font
e918ea89a3 Merge pull request #619 from majst01/simplify-split
Use strings.Cut to simplify logic
2022-06-09 14:08:49 +02:00
Juan Font Alonso
19b968849f Added missing file 2022-06-08 18:21:35 +02:00
Juan Font Alonso
5bc11891f5 Update internal docs with protocol usage 2022-06-08 18:15:38 +02:00
Juan Font Alonso
818d26b5f9 Updated changelog 2022-06-08 18:12:56 +02:00
Juan Font Alonso
c47354bdc3 Update internal docs to the new syntax 2022-06-08 18:12:47 +02:00
Stefan Majer
86ce0e0c66 Use strings.Cut to simplify logic 2022-06-08 18:09:11 +02:00
Juan Font Alonso
39f03b86c8 Added ACL test file 2022-06-08 18:06:25 +02:00
Juan Font Alonso
8287ba24b9 Do not lint the protocol magic numbers
I happily use https://pkg.go.dev/golang.org/x/net/internal/iana, but it is internal
2022-06-08 17:55:32 +02:00
Juan Font Alonso
ab1aac9f3e Improve ACLs by adding protocol parsing support 2022-06-08 17:43:59 +02:00
Juan Font Alonso
3e353004b8 Migrate ACLs syntax to new Tailscale format
Implements #617.

Tailscale has changed the format of their ACLs to use a more firewall-y terms ("users" & "ports" -> "src" & "dst"). They have also started using all-lowercase tags. This PR applies these changes.
2022-06-08 13:40:15 +02:00
Jiang Zhu
bcb04d38a5 Merge branch 'main' into enhance_cli_config
Extract LoadConfig from GetHeadscaleConfig, as they are conceptually
different operation, e.g.,
1) you can reload config through LoadConfig and do not get config
2) you can get config without reload config
2022-06-07 22:51:47 +08:00
Jiang Zhu
de0e2bf828 Merge branch 'main' into doc_openbsd 2022-06-07 22:31:32 +08:00
Kristoffer Dalby
8fed47a2be Merge pull request #616 from juanfont/update-contributors 2022-06-07 15:58:53 +02:00
github-actions[bot]
17d4968425 docs(README): update contributors 2022-06-07 06:16:00 +00:00
Kristoffer Dalby
54acee6880 Merge pull request #615 from demiflat/fix_typo 2022-06-07 08:15:17 +02:00
Darrell Kundel
a4e05d4db3 fix typo for GGO->CGO 2022-06-07 11:36:13 +08:00
Kristoffer Dalby
b0acbed329 Merge pull request #608 from kradalby/config-rework 2022-06-05 20:18:20 +02:00
Kristoffer Dalby
1b2967320b Merge branch 'main' into config-rework 2022-06-05 17:57:15 +02:00
Kristoffer Dalby
90f6be0c98 Rename one char var 2022-06-05 17:52:28 +02:00
Kristoffer Dalby
78ed610b50 Switch config to pointer 2022-06-05 17:47:26 +02:00
Kristoffer Dalby
af891808f6 Make get config load the config, use config in main method 2022-06-05 17:47:12 +02:00
Jiang Zhu
0c5a402206 add changelog 2022-06-05 23:15:21 +08:00
Jiang Zhu
8744eeeb19 ExecuteCommand set HEADSCALE_LOG_LEVEL to disabled 2022-06-05 23:14:49 +08:00
Jiang Zhu
ce13596077 add integration test for headscale -c 2022-06-05 23:13:58 +08:00
Jiang Zhu
402a29e50c impl heascale -c to specify config file 2022-06-05 18:25:09 +08:00
Jiang Zhu
0363e58467 cli.LoadConfig accepts config file now 2022-06-05 17:55:27 +08:00
Jiang Zhu
c8a14ccabb fix prettier 2022-06-05 16:01:53 +08:00
Jiang Zhu
1de29fd4e6 fix rcd link 2022-06-05 15:49:24 +08:00
Jiang Zhu
75a0155f73 add openbsd doc 2022-06-05 15:45:38 +08:00
Kristoffer Dalby
adb55bcfe9 Merge pull request #610 from huskyii/fix_pie_build 2022-06-04 12:37:02 +02:00
Jiang Zhu
2201ec8905 some GOOS do not support pie build, detect in makefile and fall back to non-pie build 2022-06-04 18:11:10 +08:00
Kristoffer Dalby
39f6fdef1a Merge pull request #609 from kradalby/add-arm64-darwin-drop-32 2022-06-04 11:42:51 +02:00
Kristoffer Dalby
699aa5cf38 Merge branch 'main' into add-arm64-darwin-drop-32 2022-06-03 19:36:01 +02:00
Kristoffer Dalby
1486adb25a Update changelog 2022-06-03 17:35:47 +00:00
Kristoffer Dalby
2653c2f5e8 Drop arm32 (armhf) for linux and add Darwin arm64 2022-06-03 17:34:06 +00:00
Kristoffer Dalby
7b7244dac2 Merge pull request #607 from juanfont/update-contributors 2022-06-03 11:01:07 +02:00
github-actions[bot]
571ce2b0b9 docs(README): update contributors 2022-06-03 08:49:19 +00:00
Kristoffer Dalby
c3db5ed749 Merge remote-tracking branch 'upstream/main' into config-rework 2022-06-03 10:49:08 +02:00
Kristoffer Dalby
0797148076 Merge pull request #601 from kradalby/signals-reload-acl 2022-06-03 10:48:43 +02:00
Kristoffer Dalby
24c9530eee Add loglevel and disable update to config struct 2022-06-03 10:37:45 +02:00
Kristoffer Dalby
679cf7c0d7 Merge branch 'main' into signals-reload-acl 2022-06-03 10:23:53 +02:00
Kristoffer Dalby
19b6405332 Merge pull request #597 from kradalby/db-error-handling 2022-06-03 10:23:21 +02:00
Kristoffer Dalby
aee8aa1c61 Move TLS config into its own struct 2022-06-03 10:14:14 +02:00
Kristoffer Dalby
5514a862dc Update headscale read config tests 2022-06-03 09:26:46 +02:00
Kristoffer Dalby
1ea8bb782c Move all read config logic to config.go 2022-06-03 09:26:36 +02:00
Kristoffer Dalby
35722cd5aa Move FilePerm function from cli to headscale 2022-06-03 09:24:36 +02:00
Kristoffer Dalby
533ecee252 Move config struct to its own file 2022-06-03 09:05:41 +02:00
Kristoffer Dalby
f1db2d0c8e Merge branch 'main' into signals-reload-acl 2022-06-02 11:12:15 +02:00
Kristoffer Dalby
6f6fb4dcd6 Merge branch 'main' into db-error-handling 2022-06-02 11:11:58 +02:00
Kristoffer Dalby
b1ba7ba685 Merge pull request #602 from iSchluff/fix/forced-tags-with-tagOwner 2022-06-02 11:11:43 +02:00
Anton Schubert
6dccfee862 Fix forced Tags with legitimate tagOwners
Also replace loops contains
2022-06-01 15:43:32 +02:00
Kristoffer Dalby
6f32b80b2b Update changelog 2022-05-31 14:30:11 +02:00
Kristoffer Dalby
2feed18b28 Support reloading ACLs with SIGHUP
Also continously listen for signals, not just once.
2022-05-31 14:28:23 +02:00
Kristoffer Dalby
36dca3516a Move Abspath function to headscale utils 2022-05-31 14:28:23 +02:00
Kristoffer Dalby
06129277ed Rename abspath function to describe what it does 2022-05-31 14:28:23 +02:00
Kristoffer Dalby
6b1482daee Use config object instead of viper for policy path 2022-05-31 14:28:23 +02:00
Kristoffer Dalby
24e4787a64 Make ACL policy part of the config struct 2022-05-31 14:28:23 +02:00
Kristoffer Dalby
5bfae22c8f Make config get function global 2022-05-31 14:28:23 +02:00
Kristoffer Dalby
3e078f0494 Fix logtail config function name 2022-05-31 14:28:01 +02:00
Kristoffer Dalby
0b4f59b82b Improve signal handling
This commit starts to wire up better signal handling, it starts with
handling shutdown a bit better, using the graceful shutdown for all the
listeners we use.

It also adds the initial switch case for handling config and acl reload,
which is to be implemented.
2022-05-31 14:28:00 +02:00
Kristoffer Dalby
a19af04582 Fix errors introduced by merge 2022-05-31 11:03:08 +02:00
Kristoffer Dalby
0676aa11a9 Merge branch 'main' into db-error-handling 2022-05-31 10:18:13 +02:00
Kristoffer Dalby
be25bbce92 Merge pull request #560 from kradalby/rename-fixess 2022-05-31 10:14:48 +02:00
Kristoffer Dalby
5ecfbbaf5d Fix pointer in machine save call 2022-05-31 10:05:00 +02:00
Kristoffer Dalby
7f7cd737dc Merge branch 'main' into rename-fixess 2022-05-31 09:45:49 +02:00
Kristoffer Dalby
b472e5a689 Merge pull request #599 from kradalby/parse-duration-improv 2022-05-31 09:45:36 +02:00
Kristoffer Dalby
25c674ed32 Merge branch 'main' into parse-duration-improv 2022-05-31 09:43:18 +02:00
Kristoffer Dalby
3d93cf9e2d Update changelog 2022-05-31 09:42:50 +02:00
Kristoffer Dalby
f7edea5f40 Merge branch 'main' into rename-fixess 2022-05-31 09:42:20 +02:00
Kristoffer Dalby
d26e220fb9 Merge pull request #598 from kradalby/parse-duration-improv 2022-05-31 09:42:00 +02:00
Kristoffer Dalby
d860270733 Use Prometheus duration parser (support days and weeks) 2022-05-30 16:10:39 +02:00
Kristoffer Dalby
a09633e859 Check errors of more database calls 2022-05-30 15:39:24 +02:00
Kristoffer Dalby
a1837a4d69 Merge branch 'main' into db-error-handling 2022-05-30 15:31:56 +02:00
Kristoffer Dalby
52cc3bc8eb Check all errors for db.Save 2022-05-30 15:31:06 +02:00
Kristoffer Dalby
9175aca094 Merge branch 'main' into rename-fixess 2022-05-30 15:29:50 +02:00
Kristoffer Dalby
848727a21d Merge pull request #596 from kradalby/disable-logcatcher 2022-05-30 15:29:35 +02:00
Kristoffer Dalby
df7d5fa2b9 Fix lint 2022-05-30 14:58:40 +02:00
Kristoffer Dalby
86dfc91dd5 update readme 2022-05-30 14:57:49 +02:00
Kristoffer Dalby
7f66d9184b Add config test 2022-05-30 14:57:43 +02:00
Kristoffer Dalby
ff5f31b87e Disable logtail for clients 2022-05-30 14:52:50 +02:00
Kristoffer Dalby
a0c465c2eb Wire up setting to enable/disable logtail 2022-05-30 14:47:41 +02:00
Kristoffer Dalby
d11279e615 Merge branch 'main' into rename-fixess 2022-05-30 13:38:28 +02:00
Kristoffer Dalby
266aac9e61 Update CHANGELOG 2022-05-30 13:35:40 +02:00
Kristoffer Dalby
4ffd3eacb0 Override golangci-lint to use go 1.17 2022-05-30 13:35:40 +02:00
Kristoffer Dalby
a443255b3e Validate isOutdated against all namespaces
This commit makes isOutdated validate a nodes necessity to update
against all namespaces, and not just the nodes own namespace (which made
more sense before).

getLastStateChange is now uses the passed namespaces as a filter,
meaning that not requesting any namespace will give you the total last
updated state.

In addition, the sync.Map is exchanged for a variant that uses generics
which allows us to remove some casting logic.
2022-05-30 13:35:40 +02:00
Kristoffer Dalby
a992840c9b Give UpdateMachine a more meaningful name 2022-05-30 13:35:40 +02:00
Kristoffer Dalby
dbc1d981c9 Revert golines 2022-05-30 13:35:28 +02:00
github-actions[bot]
9993f51b5e docs(README): update contributors 2022-05-30 13:35:28 +02:00
Kristoffer Dalby
3a3fc0a4be Update headscale checksum 2022-05-30 12:03:16 +02:00
Kristoffer Dalby
5316dd9c27 Use new nix stable (22.05) 2022-05-30 11:59:22 +02:00
Kristoffer Dalby
59a1a85a2b Change to a go generics set implementation, no more casting 🎉 2022-05-30 11:49:35 +02:00
Kristoffer Dalby
fc502e1e79 Update golines and fix go mod checksum 2022-05-30 11:13:31 +02:00
Kristoffer Dalby
405de9e0f8 Merge pull request #595 from juanfont/update-contributors 2022-05-29 18:13:40 +01:00
Kristoffer Dalby
6eac5046c6 Merge branch 'main' into rename-fixess 2022-05-29 17:21:51 +01:00
github-actions[bot]
f7f722af52 docs(README): update contributors 2022-05-29 14:49:25 +00:00
Kristoffer Dalby
583f6eeedd Merge pull request #594 from juanfont/update-contributors 2022-05-29 15:48:41 +01:00
github-actions[bot]
bec35b4965 docs(README): update contributors 2022-05-29 11:03:33 +00:00
Kristoffer Dalby
e596d8287c Merge pull request #593 from juanfont/update-contributors 2022-05-29 12:02:49 +01:00
github-actions[bot]
6c903d2d93 docs(README): update contributors 2022-05-29 10:23:00 +00:00
Kristoffer Dalby
914431b94a Merge pull request #591 from pvinis/patch-2 2022-05-29 11:22:26 +01:00
Kristoffer Dalby
11da7436c7 Merge branch 'main' into patch-2 2022-05-29 11:19:29 +01:00
Kristoffer Dalby
0f532aa5c1 Merge pull request #590 from pvinis/patch-1 2022-05-29 11:19:14 +01:00
Pavlos Vinieratos
835828fe92 link fix 2022-05-28 20:48:59 +03:00
Pavlos Vinieratos
fff1011ed8 typo 2022-05-28 20:46:01 +03:00
Kristoffer Dalby
ef497caa1b Merge pull request #2 from juanfont/fix-rename-integration-tests 2022-05-28 17:14:41 +01:00
Juan Font Alonso
4f3f0542d4 Fix some issues in testing with new hostname handling 2022-05-28 12:54:57 +02:00
Kristoffer Dalby
5fa987519d move populate to after when given_name exist 2022-05-23 17:33:07 +01:00
Kristoffer Dalby
77ceeaf5fd Test magic dns with the correct urls 2022-05-18 21:18:04 +02:00
Kristoffer Dalby
4a9d3bedf9 Use new names to resolve magic dns 2022-05-18 20:14:18 +02:00
Kristoffer Dalby
802eb931d1 Make sure givenname is set for preauthkeys 2022-05-17 22:02:18 +02:00
Kristoffer Dalby
9ebeb3d7e4 Retreive hostnames from headscale, now that they are random 2022-05-17 22:11:51 +02:00
Kristoffer Dalby
e631c6f7e0 Merge master 2022-05-16 21:41:46 +02:00
Kristoffer Dalby
163e5c29e4 fix trace log message 2022-05-16 20:35:35 +02:00
Kristoffer Dalby
4aae917f74 Require GivenName to be unique 2022-05-16 20:33:47 +02:00
Kristoffer Dalby
9b393eb861 Add integration cli tests for rename command 2022-05-16 20:32:56 +02:00
Kristoffer Dalby
5fa3016703 Generate unique givennames for hosts joining (and debug added) 2022-05-16 20:32:38 +02:00
Kristoffer Dalby
03cccd60a6 Reword FQDN normalize errors to not _only_ cover namespaces 2022-05-16 20:31:32 +02:00
Kristoffer Dalby
177c21b294 Add helper function to create a unique givenname 2022-05-16 20:30:43 +02:00
Kristoffer Dalby
f4873d9387 Fix rename cli error 2022-05-16 20:29:31 +02:00
Kristoffer Dalby
747d64cdae Merge pull request #558 from restanrm/feat-list-tags-of-machines 2022-05-16 19:03:48 +01:00
Kristoffer Dalby
c9efd5c132 Merge branch 'main' into feat-list-tags-of-machines 2022-05-16 16:35:52 +01:00
Kristoffer Dalby
546ddd2a84 Merge pull request #510 from reynico/acls-doc 2022-05-16 16:32:51 +01:00
Kristoffer Dalby
2edb5428f9 Merge branch 'main' into acls-doc 2022-05-16 16:32:18 +01:00
Adrien Raffin-Caboisse
9f082125fa fix: remove version pinning for golangci-lint it does not work 2022-05-16 16:48:04 +02:00
Adrien Raffin-Caboisse
11582105ab fix: flake.nex update sha256 2022-05-16 15:26:00 +02:00
Adrien Raffin-Caboisse
c4e69fe2c3 fix: ignore exhaust linter 2022-05-16 15:13:16 +02:00
Adrien Raffin-Caboisse
4435a4f19d chore: apply lint recommendations 2022-05-16 14:59:46 +02:00
Adrien Raffin-Caboisse
02ae7a0563 fix: pin version of golangci-lint to match dev config 2022-05-16 14:40:45 +02:00
Adrien Raffin-Caboisse
852dc0f4de feat: add golangci-lint in nix develop 2022-05-16 14:40:05 +02:00
Adrien Raffin-Caboisse
844ad15109 fix: revert previous commit and add exclusion of linter 2022-05-16 14:29:05 +02:00
Adrien Raffin-Caboisse
522e892099 fix: remove unknown linters:
When running in CI, I obtained the following error:

```
  Running [/home/runner/golangci-lint-1.41.0-linux-amd64/golangci-lint run --out-format=github-actions --new-from-patch=/tmp/tmp-1795-28vaWZek2jfM/pull.patch --new=false --new-from-rev=] in [] ...
  level=error msg="Running error: unknown linters: 'ireturn,maintidx', run 'golangci-lint linters' to see the list of supported linters"
```
2022-05-16 14:17:31 +02:00
Adrien Raffin-Caboisse
0445f404ec fix: pin version of golangci-lint in GA 2022-05-16 14:12:49 +02:00
Adrien Raffin-Caboisse
bc1909fa22 Merge branch 'feat-list-tags-of-machines' of github.com:restanrm/headscale into feat-list-tags-of-machines 2022-05-16 11:17:36 +02:00
Adrien Raffin-Caboisse
ca71830963 docs: add small documentation on getTags func 2022-05-16 11:16:07 +02:00
Kristoffer Dalby
a28eebfca3 Merge branch 'main' into feat-list-tags-of-machines 2022-05-15 12:11:28 +01:00
Kristoffer Dalby
0d31ea08c3 Merge pull request #578 from samson4649/main 2022-05-15 11:58:40 +01:00
Samuel Lock
614c003704 updated changelog 2022-05-14 22:36:04 +10:00
Adrien Raffin-Caboisse
b511295349 fix: integration tests result
Execute command doesn't fail, the result is passed in json content.
2022-05-13 13:02:40 +02:00
Adrien Raffin-Caboisse
fcdc292647 fix: update tag in db if acl is enabled 2022-05-13 13:00:32 +02:00
Adrien Raffin-Caboisse
09836cd150 chore: update vendorSha after update of go.mod and go.sum 2022-05-13 12:19:32 +02:00
Adrien Raffin-Caboisse
49ec9943b9 fix: loop over result machines instead of startup machines 2022-05-13 12:14:11 +02:00
Adrien Raffin-Caboisse
72c1edaaa4 Merge remote-tracking branch 'origin/main' into feat-list-tags-of-machines 2022-05-13 11:56:06 +02:00
Adrien Raffin-Caboisse
294ed7a751 docs: update changelog 2022-05-13 11:51:31 +02:00
Adrien Raffin-Caboisse
31c0062d5e feat: add integration tests for tag support 2022-05-13 11:47:22 +02:00
Adrien Raffin-Caboisse
63d920510d feat: improve nodes list with inputs from @deonthomasgy
cf: https://github.com/juanfont/headscale/compare/main...deonthomasgy:dev/thomas/show-tags
2022-05-13 11:46:28 +02:00
Adrien Raffin-Caboisse
16f9691e80 fix: ignore emptyPolicy errors for db insertion 2022-05-13 11:20:40 +02:00
Adrien Raffin-Caboisse
209d003832 feat: handle insert into database error 2022-05-13 11:09:28 +02:00
Adrien Raffin-Caboisse
62cfd60e38 feat: add validation of tags 2022-05-13 10:18:01 +02:00
Adrien Raffin-Caboisse
fdbc9657bc feat: return error if validation is failed 2022-05-13 10:14:38 +02:00
Adrien Raffin-Caboisse
ad4401aa40 fix: remove debug code 2022-05-13 10:14:36 +02:00
Samuel Lock
c26280c331 modified code to satisfy golangci-lint and added integration test 2022-05-11 09:31:24 +10:00
Kristoffer Dalby
b028a7dfc9 Merge branch 'main' into main 2022-05-10 22:33:33 +01:00
Kristoffer Dalby
41cd0d30eb Merge pull request #576 from juanfont/update-contributors 2022-05-10 18:26:14 +01:00
Samuel Lock
8be9e9655c fixed issue #360 2022-05-10 20:51:14 +10:00
github-actions[bot]
31bdba7456 docs(README): update contributors 2022-05-08 21:57:04 +00:00
Kristoffer Dalby
d6e1d10b12 Merge pull request #573 from deonthomasgy/patch-1 2022-05-08 22:56:32 +01:00
Kristoffer Dalby
21268f7abe Merge branch 'main' into patch-1 2022-05-08 22:55:19 +01:00
Juan Font
91b95ff707 Merge pull request #574 from deonthomasgy/main
show ipv4 address first in node list
2022-05-08 23:32:25 +02:00
Deon Thomas
6ed79b7bb8 order Ip Address, IPv4 first, cleanup 2022-05-08 15:21:10 -04:00
Deon Thomas
b4f5ed6618 order ip address output, IPv4 first 2022-05-08 15:06:12 -04:00
Deon Thomas
ed46491a3d fixed typo
not => note
2022-05-07 16:23:26 -04:00
Adrien Raffin-Caboisse
dc8c20e002 fix: handle empty aclPolicy for integration tests 2022-05-04 22:56:55 +02:00
Adrien Raffin-Caboisse
68417cc888 fix(go): add missing updated files 2022-05-03 20:37:06 +02:00
Adrien Raffin-Caboisse
a2fb5b2b9d Merge remote-tracking branch 'origin/main' into feat-list-tags-of-machines 2022-05-03 20:35:28 +02:00
Kristoffer Dalby
3fbfc5a649 Merge pull request #570 from juanfont/update-contributors 2022-05-02 22:23:30 +01:00
github-actions[bot]
00535a2016 docs(README): update contributors 2022-05-02 21:12:01 +00:00
Kristoffer Dalby
fd452d52ca Merge pull request #565 from apognu/dev/oidc-custom-config 2022-05-02 22:11:14 +01:00
Antoine POPINEAU
7cc58af932 Allow more configuration over the OIDC flow.
Adds knobs to configure three aspects of the OpenID Connect flow:

 * Custom scopes to override the default "openid profile email".
 * Custom parameters to be added to the Authorize Endpoint request.
 * Domain allowlisting for authenticated principals.
 * User allowlisting for authenticated principals.
2022-05-02 17:11:07 +02:00
Kristoffer Dalby
ddb87af5ce Merge pull request #569 from Kazauwa/362-add-move-command 2022-05-02 12:38:00 +01:00
Igor Perepilitsyn
b9ea83fed8 check that new command does not break nodes list output 2022-05-02 15:04:24 +04:00
Igor Perepilitsyn
e279224484 add integrations tests 2022-05-02 14:00:09 +04:00
Igor Perepilitsyn
12d8f0f4b0 remove redundant lines of code, fix response when output is not plain text 2022-05-02 14:00:00 +04:00
Igor Perepilitsyn
6ba68d150c correctly update machine namespace 2022-05-02 13:58:28 +04:00
Igor Perepilitsyn
1b3a7bbf03 apply styling fixes 2022-05-02 08:32:33 +04:00
Igor Perepilitsyn
4e686f8b77 add unit test 2022-05-01 21:40:18 +04:00
Igor Perepilitsyn
62c780a448 update changelog 2022-05-01 17:56:02 +04:00
Igor Perepilitsyn
bc055edf12 add command for moving node between namespaces 2022-05-01 17:55:34 +04:00
Igor Perepilitsyn
47c72a4e2e add rpc method for moving node 2022-05-01 17:55:34 +04:00
Juan Font
02a78e5a45 Merge pull request #568 from juanfont/reduce-containers-int-tests
Reduce the number of containers in integration tests
2022-05-01 08:07:29 +02:00
Juan Font Alonso
01d9a2f589 Fixed linting issues 2022-04-30 23:48:28 +02:00
Juan Font
5403f215bc Reduce the number of containers in integration tests 2022-04-30 21:19:54 +00:00
Kristoffer Dalby
96e2955ba7 Merge pull request #566 from juanfont/fix-spurious-updates 2022-04-30 22:12:57 +01:00
Juan Font
03659c4175 Updated changelog 2022-04-30 14:50:55 +00:00
Juan Font
843e2bd9b6 Do not setLastStateChangeToNow every 5 seconds 2022-04-30 14:47:16 +00:00
Kristoffer Dalby
28efd92fca Merge pull request #559 from kradalby/update-deps 2022-04-28 10:17:54 +01:00
Kristoffer Dalby
7bb87a7300 Update vendor sha 2022-04-26 19:57:49 +00:00
Adrien Raffin-Caboisse
fec8cda16a fix: fix linting issue on my computer 2022-04-25 22:33:53 +02:00
Adrien Raffin-Caboisse
2c448d4a5c chore: apply linting 2022-04-25 22:27:44 +02:00
Adrien Raffin-Caboisse
3d302441b6 fix: order error in the tests 2022-04-25 22:17:23 +02:00
Adrien Raffin-Caboisse
8061abe279 refact: use generics for contains functions 2022-04-25 22:17:23 +02:00
Adrien Raffin-Caboisse
ea9aaa6022 feat: update functions to use set command 2022-04-25 22:17:23 +02:00
Adrien Raffin-Caboisse
cc9eeda889 feat: updating cli to match the set command 2022-04-25 22:17:23 +02:00
Adrien Raffin-Caboisse
25f1dcf724 feat: update generated files 2022-04-25 22:17:23 +02:00
Adrien Raffin-Caboisse
31debf7055 feat: rewrite proto to only update tags of machine 2022-04-25 22:17:23 +02:00
Kristoffer Dalby
db8db0299e Resolve merge 2022-04-25 16:58:06 +00:00
Juan Font
e80954b6c8 Merge pull request #482 from kradalby/flake-build-env
Add Nix reproducible build system
2022-04-25 18:38:44 +02:00
Kristoffer Dalby
8504d0d8ba Move todo to correct file 2022-04-24 21:12:45 +01:00
Kristoffer Dalby
7ef8cd881c Fix comment 2022-04-24 21:10:50 +01:00
Kristoffer Dalby
79704dc9b0 Update command with new fields 2022-04-24 20:57:15 +01:00
Kristoffer Dalby
06c928bc52 Migrate name and nickname fields 2022-04-24 20:56:42 +01:00
Kristoffer Dalby
62808cbd86 Bubble error up to user for rename 2022-04-24 20:56:28 +01:00
Kristoffer Dalby
14994cb6cc Use new logic and fields for dns 2022-04-24 20:55:54 +01:00
Kristoffer Dalby
6b79679cb4 Generate from proto 2022-04-24 20:55:20 +01:00
Kristoffer Dalby
caf79f6910 Change nickname to givenname in proto 2022-04-24 20:55:11 +01:00
Kristoffer Dalby
6e2768097a Rename name -> hostname, nickname -> givenname 2022-04-24 20:54:38 +01:00
Kristoffer Dalby
8845938881 Merge branch 'main' into main 2022-04-24 09:48:00 +02:00
Kristoffer Dalby
a23035aee7 update rest of deps 2022-04-24 07:21:50 +00:00
Kristoffer Dalby
e51e6f487f Resolve merge conflict 2022-04-23 08:31:57 +00:00
Kristoffer Dalby
f78deaebb6 Add new tailscale to integration tests 2022-04-23 08:30:13 +00:00
Kristoffer Dalby
4d2949bda9 Upgrade tailscale dep 2022-04-23 08:29:26 +00:00
Kristoffer Dalby
cb0899b534 Update vendor shar 2022-04-23 00:02:57 +02:00
Kristoffer Dalby
ecf5259693 resolve merge conflict 2022-04-22 22:34:20 +02:00
Kristoffer Dalby
3a90079ab8 Merge branch 'main' into feat-list-tags-of-machines 2022-04-22 22:27:36 +02:00
Kristoffer Dalby
970dea5d68 Merge pull request #557 from mpldr/remove-buf-installation 2022-04-22 08:16:03 +01:00
Kristoffer Dalby
cd9807a1d3 Merge branch 'main' into flake-build-env 2022-04-22 07:52:29 +01:00
Kristoffer Dalby
613dc61339 Merge branch 'main' into remove-buf-installation 2022-04-22 07:51:40 +01:00
Adrien Raffin-Caboisse
b9fee36f6e fix: linting 2022-04-21 23:56:58 +02:00
Adrien Raffin-Caboisse
17d6624bb9 chore: fix lint 2022-04-21 23:49:21 +02:00
Adrien Raffin-Caboisse
f53bb63b2d fix: move tag command to subcommand of nodes 2022-04-21 23:43:20 +02:00
Adrien Raffin-Caboisse
ea7bcfffbb Merge branch 'main' into feat-list-tags-of-machines 2022-04-21 20:38:51 +02:00
Moritz Poldrack
3023323528 remove necessary buf installation
This commit adds buf as a regular dependency and go running it instead
of requiring installing buf to one's own GOBIN.
2022-04-21 20:23:21 +02:00
Kristoffer Dalby
2dfd8a9098 Merge pull request #556 from juanfont/update-contributors 2022-04-21 12:53:14 +01:00
github-actions[bot]
c8ed1f0f43 docs(README): update contributors 2022-04-21 11:51:30 +00:00
Kristoffer Dalby
f9e2ce2c8c Merge pull request #551 from mpldr/patch-1 2022-04-21 12:50:50 +01:00
Moritz Poldrack
886e95c00d Merge branch 'main' into patch-1 2022-04-21 11:00:33 +02:00
Moritz Poldrack
6dd9e93346 expanded arguments in useradd to be easier to understand for beginners 2022-04-21 11:00:17 +02:00
Kristoffer Dalby
2dacf839dc Upgrade tailscale dep 2022-04-21 08:35:08 +00:00
Kristoffer Dalby
8f6952acee Merge branch 'main' into flake-build-env 2022-04-21 09:23:23 +01:00
Kristoffer Dalby
235a90276f Merge pull request #531 from juanfont/suggest-english 2022-04-21 09:21:37 +01:00
Kristoffer Dalby
5c285afda5 Merge branch 'main' into flake-build-env 2022-04-21 09:21:10 +01:00
Kristoffer Dalby
db930af50e Merge branch 'main' into patch-1 2022-04-21 09:13:34 +01:00
Kristoffer Dalby
ffa570e877 Merge branch 'main' into suggest-english 2022-04-21 09:13:08 +01:00
Kristoffer Dalby
96ae78f422 Merge pull request #553 from kradalby/fix-discord-link 2022-04-21 09:12:49 +01:00
Kristoffer Dalby
580c72bf16 Update discord link so it does not grant temp memberships 2022-04-21 09:06:30 +01:00
Kristoffer Dalby
9254afff2d Add direnv and nix output to gitignore 2022-04-21 09:06:13 +01:00
Moritz Poldrack
7ce0bd053c removed leading whitespace 2022-04-16 23:58:05 +02:00
Moritz Poldrack
41a8c14acb add information on how to create a headscale user 2022-04-16 23:56:57 +02:00
Moritz Poldrack
be2487f4c0 Clarified systemd friendly path
Suggested-by: Rendezvous
2022-04-16 23:52:06 +02:00
Adrien Raffin-Caboisse
4651c44dde feat: print tags in nodes list 2022-04-16 13:32:00 +02:00
Adrien Raffin-Caboisse
4fcc5e253c chore: fmt for grpc file 2022-04-16 13:15:18 +02:00
Adrien Raffin-Caboisse
89a1a56328 feat: add unit tests and fmt 2022-04-16 13:15:04 +02:00
Adrien Raffin-Caboisse
db1528bc73 feat: add invalid and valid tags to grpc response 2022-04-16 12:27:54 +02:00
Adrien Raffin-Caboisse
587bdc75de feat: add valid and invalid fields
Also change ID in proto for ForcedTags since the previous ID's should be reserved for commented fields
2022-04-16 11:30:51 +02:00
Adrien Raffin-Caboisse
98f54c9f7f chore: apply format and lint 2022-04-15 18:27:57 +02:00
Adrien Raffin-Caboisse
cd1d10761f feat(acls): add support for forced tags 2022-04-15 18:01:13 +02:00
Adrien Raffin-Caboisse
9de9bc23f8 feat(cli): add tag subcommand to add and remove tags 2022-04-15 16:12:35 +02:00
Adrien Raffin-Caboisse
02f68ebac8 feat: add forcedTags field and update proto 2022-04-15 16:00:08 +02:00
Kristoffer Dalby
dd3f24b83f Merge branch 'main' into suggest-english 2022-04-12 18:16:39 +01:00
Kristoffer Dalby
bc63c577a9 Merge pull request #537 from reynico/exit-if-acl-wrong 2022-04-12 18:15:15 +01:00
Kristoffer Dalby
57c81e4153 Merge branch 'main' into exit-if-acl-wrong 2022-04-12 17:01:40 +01:00
Kristoffer Dalby
556ca5fec7 Merge pull request #544 from mpldr/makefile-improvements 2022-04-12 17:00:58 +01:00
Kristoffer Dalby
93682ab708 Merge branch 'main' into makefile-improvements 2022-04-12 17:00:30 +01:00
Kristoffer Dalby
6eeee8e5c7 Merge pull request #545 from mpldr/fix-discord-invite 2022-04-12 16:59:40 +01:00
Kristoffer Dalby
d195847d8f Merge branch 'main' into fix-discord-invite 2022-04-12 16:59:04 +01:00
Moritz Poldrack
3d8dc9d2bf fix discord invite
Fixes: https://github.com/juanfont/headscale/issues/533
2022-04-11 19:08:07 +02:00
Moritz Poldrack
8601dd1f42 fixed CGO disabling 2022-04-11 14:57:12 +02:00
Kristoffer Dalby
3abdc870d8 Merge branch 'main' into makefile-improvements 2022-04-11 11:49:55 +01:00
Kristoffer Dalby
367f8489db Merge pull request #542 from mpldr/issue-342-send-on-closed-channel 2022-04-11 11:48:53 +01:00
Moritz Poldrack
c312f8bf4a set up Makefile for reproducible builds 2022-04-11 08:56:40 +02:00
Moritz Poldrack
1f43c39f93 replaced version-at-commit script with git-describe call 2022-04-11 08:54:12 +02:00
Moritz Poldrack
9f03a012fb updated changelog 2022-04-10 22:47:52 +02:00
Moritz Poldrack
22dd61d849 fixed the issue of sending on closed channel
This commit fixes the issue of headscale crashing after sending on a
closed channel by moving the channel close to the sender side, instead
of the creator. closeChanWithLog is also implemented with generics now.

Fixes: https://github.com/juanfont/headscale/issues/342
Signed-off-by: Moritz Poldrack <git@moritz.sh>
2022-04-10 17:31:09 +02:00
Kristoffer Dalby
a92f6abc6e Merge pull request #541 from juanfont/update-contributors 2022-04-10 10:04:44 +01:00
github-actions[bot]
9cdaa9730b docs(README): update contributors 2022-04-10 09:03:16 +00:00
Kristoffer Dalby
5d67ed0ce1 Merge pull request #540 from yangchuansheng/dev 2022-04-10 10:02:42 +01:00
Carson Yang
62d774b6ee Fix key name about derp port 2022-04-10 09:53:27 +08:00
Kristoffer Dalby
a14f50eeca Merge pull request #538 from artemklevtsov/patch-1 2022-04-09 18:49:47 +01:00
Artem Klevtsov
98e98a8adb Fix wrong metrics port in docs
It should be 9090.
2022-04-09 16:24:57 +07:00
Nico Rey
fa7ef3df2f make linter happy 2022-04-07 15:21:26 -03:00
Nico Rey
c3324371d6 Update changelog 2022-04-06 18:41:13 -03:00
Nico Rey
6e08241712 Exit Headscale if ACL policy file cannot be parsed 2022-04-06 11:05:08 -03:00
Kristoffer Dalby
c07dd3f14f Merge pull request #534 from nning/main 2022-04-06 08:35:39 +01:00
henning mueller
b2ae9b6cac fix: Remove days from expiry option value examples 2022-04-05 18:45:29 +02:00
Juan Font
57536b020e Merge branch 'main' into suggest-english 2022-04-02 11:47:11 +02:00
Juan Font Alonso
0003e30084 Suggest English as lingua franca 2022-04-02 11:45:18 +02:00
Juan Font
23be13b113 Merge pull request #528 from juanfont/update-contributors
docs(README): update contributors
2022-03-29 23:12:16 +02:00
github-actions[bot]
5e44266292 docs(README): update contributors 2022-03-28 15:06:39 +00:00
Kristoffer Dalby
32522cb482 Merge pull request #521 from Niek/patch-1 2022-03-28 16:06:00 +01:00
Niek van der Maas
6d296a195d Update docs/running-headscale-container.md
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-28 08:58:33 +02:00
Niek van der Maas
3272febfb3 Change publish interface 2022-03-26 13:33:31 +01:00
Niek van der Maas
7dae780be1 Update docs/running-headscale-container.md
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-25 23:44:55 +01:00
Niek van der Maas
73f1c06f65 Fix long line 2022-03-25 07:46:01 +00:00
Niek van der Maas
b60727b205 Merge branch 'main' into patch-1 2022-03-25 08:44:16 +01:00
Niek van der Maas
8cee31d8d7 Fix prettier 2022-03-25 07:43:15 +00:00
Juan Font
b5aace6d3a Merge pull request #519 from hdhoang/pak-counter
Fix labels cardinality error when registering unknown pre-auth key
2022-03-25 00:25:19 +01:00
Niek van der Maas
7e286c570e Docker docs enhancements
While configuring a Docker setup I noticed that the docs could use some enhancements.
2022-03-22 13:45:30 +01:00
hdhoang
52fd13bfc4 Fix labels cardinality error when registering unknown pre-auth key 2022-03-21 15:49:14 +07:00
Kristoffer Dalby
b8e4aeede8 Upgrade golines 2022-03-20 22:39:43 +00:00
Kristoffer Dalby
9a632c17d1 Merge pull request #518 from juanfont/update-contributors 2022-03-20 14:42:41 +00:00
github-actions[bot]
8758ee1c4d docs(README): update contributors 2022-03-20 14:18:39 +00:00
Kristoffer Dalby
150ae1846a Merge pull request #517 from juanfont/changelog-prep-0.15
Prepare CHANGELOG for v0.15.0
2022-03-20 14:18:01 +00:00
Juan Font
452286552c Update CHANGELOG.md to include future 0.16.0
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-20 15:07:22 +01:00
Juan Font Alonso
631cf58ff0 Added date for 0.15.0 in changelog 2022-03-20 13:36:25 +01:00
Juan Font
8a2c0e88f4 Merge pull request #513 from juanfont/unstable-integration-tests
Add Tailscale unstable channel and repo HEAD to integration tests
2022-03-20 13:35:23 +01:00
Juan Font Alonso
af6a47fdd3 Changelog updated 2022-03-20 12:36:30 +01:00
Juan Font
94d910557f Merge branch 'main' into unstable-integration-tests 2022-03-20 12:34:04 +01:00
Juan Font Alonso
a8a683d3cc Added default values in Dockerfile.tailscale 2022-03-20 12:33:41 +01:00
Juan Font Alonso
a1caa5b45c Minor improvements on logging 2022-03-20 12:31:18 +01:00
Juan Font Alonso
f42868f67f Docker requires lowercase for the container names 2022-03-20 12:30:56 +01:00
Juan Font Alonso
a6455653c0 Added missing package 2022-03-20 12:30:08 +01:00
Kristoffer Dalby
c8aa653275 Merge branch 'main' into main 2022-03-19 09:36:36 +00:00
Kristoffer Dalby
91e5cbd793 Add direnv flake support 2022-03-19 09:23:03 +00:00
Kristoffer Dalby
79fc74c7a4 Merge branch 'main' into acls-doc 2022-03-18 22:43:17 +00:00
Kristoffer Dalby
c8503075e0 Merge pull request #514 from aofei/main 2022-03-18 21:18:22 +00:00
Kristoffer Dalby
4068a7b00b Merge branch 'main' into main 2022-03-18 21:02:05 +00:00
Kristoffer Dalby
daae2fe549 Merge pull request #512 from restanrm/feat-add-debug-log 2022-03-18 21:01:16 +00:00
Kristoffer Dalby
47bbb85a20 Merge branch 'main' into acls-doc 2022-03-18 20:44:44 +00:00
Kristoffer Dalby
739653fa71 Merge branch 'main' into feat-add-debug-log 2022-03-18 20:44:21 +00:00
Kristoffer Dalby
304109a6c5 Merge pull request #511 from restanrm/fix-machine-registration-expired 2022-03-18 20:44:05 +00:00
Kristoffer Dalby
c29af96a19 Merge branch 'main' into main 2022-03-18 20:42:44 +00:00
Kristoffer Dalby
d21e9d29d1 Merge branch 'main' into feat-add-debug-log 2022-03-18 19:41:32 +00:00
Kristoffer Dalby
b65bd5baa8 Merge branch 'main' into fix-machine-registration-expired 2022-03-18 19:41:26 +00:00
Juan Font Alonso
0165b89941 Fixed paths 2022-03-18 19:35:09 +01:00
Kristoffer Dalby
53b62f3f39 Merge pull request #499 from juanfont/mandatory-stun 2022-03-18 18:28:37 +00:00
Kristoffer Dalby
cd2914ab3b Merge branch 'main' into mandatory-stun 2022-03-18 17:44:12 +00:00
Kristoffer Dalby
e85b97143c Merge pull request #509 from kradalby/go118 2022-03-18 17:43:41 +00:00
Aofei Sheng
1eafe960b8 fix: possible panic in Headscale.scheduledDERPMapUpdateWorker
There is a possible nil pointer dereference panic in the
`Headscale.scheduledDERPMapUpdateWorker`. Such as when the embedded
DERP server is disabled.
2022-03-19 01:20:25 +08:00
Juan Font Alonso
749c92954c Add Tailscale unstable channel and repo HEAD to integration tests
In preparation for the implementation of the new TS2021 protocol (Tailscale control protocol v2) we are expanding the test infrastructure
2022-03-18 17:05:28 +01:00
Juan Font Alonso
db9ba17920 Added missing file 2022-03-18 13:10:35 +01:00
Juan Font Alonso
d5ce7d7523 Prettier 2022-03-18 13:09:57 +01:00
Juan Font Alonso
2e6687209b Make STUN server mandatory if DERP embedded is enabled 2022-03-18 12:58:00 +01:00
Adrien Raffin-Caboisse
2e04abf4bb feat(oidc): add debug log 2022-03-18 09:40:12 +01:00
Adrien Raffin-Caboisse
882c0c34c1 chore(changelog): update changelog 2022-03-18 09:34:18 +01:00
Adrien Raffin-Caboisse
61ebb713f2 fix(oidc): Reset expiry for reauthentication
The previous code resetted the expiry time to be expired.  So the machine was never reauthenticated
2022-03-18 09:32:07 +01:00
Kristoffer Dalby
ac5ad42474 Fix integration nix 2022-03-18 08:24:21 +00:00
Nico Rey
d68d7d5a6f Docs/ACLs: Add a network diagram to help explain ACLs 2022-03-17 19:58:56 -03:00
Nico Rey
bff9036f14 Docs/ACLs: Add router examples with subnets 2022-03-17 19:58:34 -03:00
Nico Rey
8b08c2a918 Docs/ACLs: Namespaces are created automatically 2022-03-17 19:24:39 -03:00
Nico Rey
b9f0fabb5c Docs/ACLs: Wording, add intermediary router example 2022-03-17 19:23:37 -03:00
Kristoffer Dalby
9d4822b8c7 Actually set up nix 2022-03-17 18:20:01 +00:00
Kristoffer Dalby
466d03d574 Nixify integration test 2022-03-17 18:18:51 +00:00
Kristoffer Dalby
d43fec7f96 Merge branch 'main' into flake-build-env 2022-03-17 18:11:21 +00:00
Kristoffer Dalby
62f4c205f5 Run binary build with nix 2022-03-17 18:11:04 +00:00
Kristoffer Dalby
003c19004d Run tests with nix 2022-03-17 18:10:50 +00:00
Kristoffer Dalby
70274d528c Add nix to runn on lint and integration 2022-03-17 18:08:49 +00:00
Kristoffer Dalby
6d41279781 Upgrade to go 1.18 2022-03-17 18:07:26 +00:00
Kristoffer Dalby
b781446e86 Upgrade to go 1.18 2022-03-17 17:43:11 +00:00
Kristoffer Dalby
1c9b1c0579 Merge pull request #507 from juanfont/update-contributors 2022-03-17 07:28:42 +00:00
github-actions[bot]
ade9552736 docs(README): update contributors 2022-03-17 06:38:00 +00:00
Kristoffer Dalby
68403cb76e Merge pull request #505 from y0ngb1n/fix-docs-metrics-endpoint 2022-03-17 06:37:27 +00:00
Yang Bin
537ecb8db0 docs: fixed /metrics endpoint 8080 → 9090, reference config-example.yaml 2022-03-17 09:25:42 +08:00
Juan Font Alonso
8f5875efe4 Reorg errors 2022-03-16 19:46:59 +01:00
Juan Font
98ac88d5ef Changed comment position
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-16 18:45:34 +01:00
Kristoffer Dalby
d13338a9fb Merge branch 'main' into mandatory-stun 2022-03-16 07:18:18 +00:00
Juan Font
1579ffb66a Merge pull request #500 from bravechamp/patch-1
Fix API access
2022-03-15 14:53:23 +01:00
bravechamp
0bfa5302a7 Fix API access
By allowing API keys to be validated
2022-03-15 16:05:56 +03:00
Juan Font Alonso
b8aad5451d Make STUN run by default when embedded DERP is enabled
This commit also allows to set an external STUN server, while running the embedded DERP server (without embedded STUN)
2022-03-15 13:22:25 +01:00
bravechamp
60ee04674d Normalize nickname before saving to database 2022-03-13 21:55:36 +00:00
bravechamp
9901d6b2e7 Ability to clear nickname 2022-03-13 21:10:41 +00:00
bravechamp
663e8384a3 Nickname support 2022-03-13 21:03:20 +00:00
Kristoffer Dalby
61440c42d3 Merge pull request #496 from juanfont/update-contributors
docs(README): update contributors
2022-03-10 19:58:24 +00:00
github-actions[bot]
18ee6274e1 docs(README): update contributors 2022-03-10 19:50:59 +00:00
Kristoffer Dalby
0abfbdc18a Merge pull request #495 from appbricks/appbricks/main-bug-fix
Regression bug fix when re-authenticating machine with auth-key
2022-03-10 19:50:23 +00:00
Mevan Samaratunga
082a852c5e fixed linting recommendation 2022-03-10 10:40:20 -05:00
Mevan Samaratunga
af081e9fd3 fixed lint errors 2022-03-10 10:22:21 -05:00
Mevan Samaratunga
8b5e8b7dfc Refresh expired machine on re-auth - closes #489 2022-03-10 08:59:28 -05:00
Kristoffer Dalby
1e7d7e510e Update go sha for flake 2022-03-08 17:17:02 +00:00
Kristoffer Dalby
a806694d23 fix gosum merge 2022-03-08 17:11:46 +00:00
Kristoffer Dalby
62d7fae056 Merge pull request #311 from restanrm/docs-acl-modifications
Issues with current ACL implementation and solution proposal
2022-03-08 17:08:17 +00:00
Kristoffer Dalby
06d85688fd set version based on git rev 2022-03-08 17:07:25 +00:00
Kristoffer Dalby
dd219d0ff6 Merge branch 'main' into docs-acl-modifications 2022-03-08 17:05:59 +00:00
Kristoffer Dalby
6087e1cf6f Merge pull request #488 from juanfont/update-contributors 2022-03-08 17:02:47 +00:00
github-actions[bot]
c47fb1ae54 docs(README): update contributors 2022-03-08 16:50:11 +00:00
Kristoffer Dalby
48cec3cd90 Merge pull request #486 from e-zk/main 2022-03-08 16:49:32 +00:00
Kristoffer Dalby
e54c508c10 Merge branch 'main' into main 2022-03-08 16:05:41 +00:00
Kristoffer Dalby
941e9d9b0f Merge pull request #388 from juanfont/embedded-derp 2022-03-08 16:05:30 +00:00
Kristoffer Dalby
11ccae8e52 Merge branch 'main' into flake-build-env 2022-03-08 16:04:52 +00:00
Juan Font Alonso
b803240dc1 Added new line for prettier 2022-03-08 12:21:08 +01:00
Juan Font Alonso
bdbf620ece Merge branch 'embedded-derp' of https://github.com/juanfont/headscale into embedded-derp 2022-03-08 12:16:43 +01:00
Juan Font
e5d22b8a70 Merge branch 'main' into embedded-derp 2022-03-08 12:16:34 +01:00
Juan Font Alonso
05c5e2280b Updated CHANGELOG and README 2022-03-08 12:15:05 +01:00
Juan Font Alonso
b41d89946a Merge branch 'embedded-derp' of https://github.com/juanfont/headscale into embedded-derp 2022-03-08 12:11:59 +01:00
Juan Font Alonso
cc0c88a63a Added small integration test for stun 2022-03-08 12:11:51 +01:00
e-zk
c06689dec1 fix: make register html/template consistent with other html
- makes the html/template for /register follow the same formatting
  as /apple and /windows
- adds a <title> element
- minor change for consistency's sake
2022-03-08 18:34:46 +10:00
Kristoffer Dalby
b85dd7abbd Merge pull request #484 from juanfont/prtemplate-fix
Fix checkboxes in PR template
2022-03-08 07:29:30 +00:00
Kristoffer Dalby
6aeaff43aa Fix checkboxes in PR template 2022-03-08 07:21:04 +00:00
Kristoffer Dalby
dd26cbd193 Merge branch 'main' into embedded-derp 2022-03-08 07:18:51 +00:00
Kristoffer Dalby
9a60eeaf86 Merge branch 'main' into flake-build-env 2022-03-08 07:18:21 +00:00
Kristoffer Dalby
b0ae3240fd Merge pull request #387 from restanrm/fix-magic-dns-and-uppercase-letters
Fix magic dns and uppercase letters
2022-03-08 07:17:45 +00:00
Adrien Raffin-Caboisse
41efe98953 fix: apply fmt and fix missing name changes 2022-03-07 23:20:30 +01:00
Adrien Raffin-Caboisse
2b68c90778 chore: update changelog 2022-03-07 23:14:39 +01:00
Adrien Raffin-Caboisse
f19c048569 fix: change normalization function name 2022-03-07 22:55:54 +01:00
Adrien Raffin-Caboisse
6cc8bbc24f feat(api): add normalisation at machine register step 2022-03-07 22:46:29 +01:00
Kristoffer Dalby
c24de595f6 Add example commands for docker 2022-03-07 17:41:16 +00:00
Kristoffer Dalby
63641a7b17 Correct pkgs call 2022-03-07 17:37:53 +00:00
Kristoffer Dalby
a6570d33a6 Add option to build docker image
This commit adds dockerbuild to flakes.nix:

```
nix build .#headscale-docker
```

This uses the Nix infra to build and _does not_ use Dockerfile.

It currently works on Linux (no macOS)
2022-03-07 17:18:41 +00:00
Kristoffer Dalby
124d8a3424 Update readme with nix notes 2022-03-07 16:58:07 +00:00
Kristoffer Dalby
5de9de14a9 Add flake build file
This commit adds a flake.nix build file, it can be used for three
things:

Build `headscale` from local or straight from git:

nix build
or
nix build github:juanfont/headscale

Run and Build `headscale` from local or straight from git:

nix run
or
nix run github:juanfont/headscale

Set up a development environment including all our tools,
- linters
- protobuf tooling
- compilers

nix develop
2022-03-07 16:51:38 +00:00
Kristoffer Dalby
15f8cb5034 Remove hacky go tool install 2022-03-07 07:40:56 +00:00
Juan Font Alonso
03452a8dca Prettied 2022-03-07 00:29:40 +01:00
Juan Font Alonso
15ed71315c Merge branch 'embedded-derp' of https://github.com/juanfont/headscale into embedded-derp 2022-03-06 23:47:31 +01:00
Juan Font Alonso
05df8e947a Added missing file 2022-03-06 23:47:14 +01:00
Juan Font Alonso
b3fa66dbd2 Check for DERP in test 2022-03-06 23:46:16 +01:00
Juan Font Alonso
a27b386123 Clarified expiration dates 2022-03-06 23:45:01 +01:00
Juan Font Alonso
580db9b58f Mention that STUN is UDP 2022-03-06 23:19:21 +01:00
Adrien Raffin-Caboisse
1114449601 change: update name of method to check and normalize Domain name 2022-03-06 20:46:17 +01:00
Juan Font
b47de07eea Update Dockerfile.tailscale
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-06 20:42:27 +01:00
Juan Font
e1fcf0da26 Added more version
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-03-06 20:40:55 +01:00
Juan Font
dcf3ea567c Merge branch 'main' into fix-magic-dns-and-uppercase-letters 2022-03-06 17:37:48 +01:00
Juan Font Alonso
de2ea83b3b Linting here and there 2022-03-06 17:35:54 +01:00
Juan Font Alonso
eb06054a7b Make DERP Region configurable 2022-03-06 17:25:21 +01:00
Juan Font Alonso
eb500155e8 Make STUN server configurable 2022-03-06 17:00:56 +01:00
Juan Font Alonso
dc909ba6d7 Improved logging on startup 2022-03-06 16:54:19 +01:00
Juan Font Alonso
70910c4595 Working /bootstrap-dns DERP helper 2022-03-06 01:23:35 +01:00
Juan Font Alonso
54c3e00a1f Merge local DERP server region with other configured DERP sources 2022-03-05 20:04:31 +01:00
Juan Font Alonso
e78c002f5a Fix minor issue 2022-03-05 19:48:30 +01:00
Juan Font Alonso
237f7f1027 Merge branch 'main' into embedded-derp 2022-03-05 19:42:29 +01:00
Juan Font Alonso
992efbd84a Added missing private TLS key 2022-03-05 19:35:15 +01:00
Juan Font Alonso
e9eb90fa76 Added integration tests for the embedded DERP server 2022-03-05 19:34:06 +01:00
Juan Font Alonso
88378c22fb Rename the file to derp_server.go for coherence 2022-03-05 19:31:50 +01:00
Juan Font Alonso
b742379627 Do not use the term embedded 2022-03-05 19:30:30 +01:00
Juan Font Alonso
df37d1a639 Do not offer the option to be DERP insecure
Websockets, in which DERP is based, requires a TLS certificate. At the same time,
if we use a certificate it must be valid... otherwise Tailscale wont connect (does not
have an Insecure option). So there is no option to expose insecure here
2022-03-05 19:19:21 +01:00
Juan Font Alonso
758b1ba1cb Renamed configuration items of the DERP server 2022-03-05 16:22:02 +01:00
Kristoffer Dalby
435ee36d78 Merge pull request #394 from juanfont/renovateaction/dockerfiles 2022-03-05 00:41:22 +00:00
Renovate Bot
35efd8f95a chore(deps): update dependency docker.io/golang to v1.17.8 2022-03-05 00:09:36 +00:00
Juan Font Alonso
09d78c7a05 Even more stuff moved to common 2022-03-04 13:54:59 +01:00
Kristoffer Dalby
60655c5242 Merge pull request #393 from juanfont/update-contributors 2022-03-04 12:30:20 +00:00
Juan Font Alonso
22d2443281 Move more stuff to common 2022-03-04 13:26:45 +01:00
github-actions[bot]
a70669fca7 docs(README): update contributors 2022-03-04 11:04:12 +00:00
Kristoffer Dalby
0720473033 Merge pull request #392 from e-zk/windows-endpoint 2022-03-04 11:03:33 +00:00
Kristoffer Dalby
e799307e74 Merge branch 'main' into windows-endpoint 2022-03-04 10:47:52 +00:00
e-zk
575f33d183 docs: fix comments to comply with golangci-lint 2022-03-04 20:35:09 +10:00
Juan Font Alonso
607c1eb316 Be consistent with uppercase DERP 2022-03-04 11:31:41 +01:00
e-zk
d69dada8ff feat(windows): rename apple_mobileconfig.go => platform_config.go
rename apple_mobileconfig.go to platform_config.go since the file
includes configuration info for multiple platforms now.
2022-03-04 20:03:49 +10:00
e-zk
f9e0c13890 docs: update CHANGELOG 2022-03-04 19:53:57 +10:00
e-zk
12a50ac8ac feat(windows): add /windows endpoint for Windows configuration
- registry file /windows/tailscale.reg is generated, filling in the
  associated control server URL
- also includes CLI instructions
- fix /apple incorrect template: 'Url' is supposed to be '.URL'
2022-03-04 19:53:44 +10:00
e-zk
b342cf0240 feat(windows): cleanup /apple endpoint
- rename the gin function to AppleConfigMessage
- use <pre> + <code> for code blocks
- add headscale heading
- reword some sections
2022-03-04 19:53:29 +10:00
Kristoffer Dalby
e3ff87b7ef Merge pull request #389 from e-zk/main 2022-03-04 07:26:35 +00:00
zakaria
745696b310 docs: fix mistake in ACME challenge type comment 2022-03-04 12:11:43 +10:00
Juan Font Alonso
23cde8445f Merge branch 'main' into embedded-derp 2022-03-04 00:04:59 +01:00
Juan Font Alonso
9d43f589ae Added missing deps 2022-03-04 00:04:28 +01:00
Juan Font Alonso
897d480f4d Add an embedded DERP server to Headscale
This series of commit will be adding an embedded DERP server (and STUN) to Headscale,
thus making it completely self-contained and not dependant in other infrastructure.
2022-03-04 00:01:31 +01:00
Adrien Raffin-Caboisse
6f172a6e4c fix(acls): remove dead error code 2022-03-03 23:53:08 +01:00
Adrien Raffin-Caboisse
44a5372c53 fix(poll): Normalize hostname
This function is called often. Normalization of the hostname will be written in database.
2022-03-03 23:52:25 +01:00
Kristoffer Dalby
f2ea6fb30f Merge pull request #384 from restanrm/fix-issue-with-empty-namespace-and-acl-evaluation 2022-03-03 08:43:37 +00:00
Adrien Raffin-Caboisse
4a4952899b feat(acls): add some logs and skip error
logs looks like the following
```
2022-03-02T20:43:08Z DBG Expanding alias=app-test
2022-03-02T20:43:08Z DBG Expanding alias=kube-test
2022-03-02T20:43:08Z DBG Expanding alias=test
2022-03-02T20:43:08Z WRN No IPs found with the alias test
2022-03-02T20:43:08Z DBG Expanding alias=prod
2022-03-02T20:43:08Z WRN No IPs found with the alias prod
2022-03-02T20:43:08Z DBG Expanding alias=prod
2022-03-02T20:43:08Z WRN No IPs found with the alias prod
```
2022-03-02 21:54:43 +01:00
Kristoffer Dalby
b72a8aa7d1 Merge pull request #381 from juanfont/update-contributors 2022-03-02 14:18:09 +00:00
github-actions[bot]
e301d0d1df docs(README): update contributors 2022-03-02 13:44:26 +00:00
Kristoffer Dalby
75ca91b0f7 Merge pull request #380 from juanfont/update-contributors
docs(README): update contributors
2022-03-02 13:43:53 +00:00
github-actions[bot]
e208ccc982 docs(README): update contributors 2022-03-02 13:42:25 +00:00
Kristoffer Dalby
71a62697aa Merge pull request #379 from juanfont/kradalby-patch-1
Second contributor attempt
2022-03-02 13:41:50 +00:00
Kristoffer Dalby
f9c0597875 Second contributor attempt 2022-03-02 13:40:37 +00:00
Kristoffer Dalby
aa3eb5171a Merge pull request #344 from reynico/metrics-listen 2022-03-02 13:06:29 +00:00
Nico Rey
dcc46af8de Changelog: add breaking change 2022-03-02 09:22:29 -03:00
Kristoffer Dalby
b61500670c Merge branch 'main' into metrics-listen 2022-03-02 11:35:33 +00:00
Kristoffer Dalby
ccec534e19 Merge pull request #377 from juanfont/smarter-contribute-pipeline 2022-03-02 11:17:02 +00:00
Kristoffer Dalby
9b10457209 Merge branch 'main' into smarter-contribute-pipeline 2022-03-02 11:14:50 +00:00
Kristoffer Dalby
9a8f605cba Merge pull request #371 from kradalby/use-specific-database-typess 2022-03-02 11:14:04 +00:00
Kristoffer Dalby
1246267ead Merge branch 'main' into smarter-contribute-pipeline 2022-03-02 10:43:07 +00:00
Kristoffer Dalby
a0a56d43f8 Merge branch 'main' into use-specific-database-typess 2022-03-02 10:29:34 +00:00
Kristoffer Dalby
63d87110f6 Merge pull request #376 from e-zk/feat/command-aliases 2022-03-02 10:28:18 +00:00
Kristoffer Dalby
7c99d963e2 Merge branch 'main' into feat/command-aliases 2022-03-02 10:06:38 +00:00
zakaria
a614f158be docs: update changelog 2022-03-02 19:53:07 +10:00
Kristoffer Dalby
2b6a5173da Allow upstream delete continue on failure 2022-03-02 09:12:00 +00:00
Kristoffer Dalby
32ac690494 Update contributors.yml 2022-03-02 09:08:30 +00:00
Kristoffer Dalby
0835bffc3c Update changelog 2022-03-02 08:15:21 +00:00
Kristoffer Dalby
c80e364f02 Remove always nil error 2022-03-02 08:15:14 +00:00
Kristoffer Dalby
5b169010be Resolve merge conflict 2022-03-02 08:11:50 +00:00
Kristoffer Dalby
eeded85d9c Merge pull request #366 from kradalby/registration-simplification 2022-03-02 08:02:26 +00:00
Kristoffer Dalby
e4d81bbb16 Merge branch 'main' into registration-simplification 2022-03-02 07:31:02 +00:00
Kristoffer Dalby
1f8c7f427b Add comment 2022-03-02 07:29:56 +00:00
Kristoffer Dalby
ef422e6988 Protect against expiry nil 2022-03-02 07:29:56 +00:00
Kristoffer Dalby
ec4dc68524 Use correct machinekey format for oidc reg 2022-03-02 07:29:56 +00:00
Kristoffer Dalby
86ade72c19 Remove err check 2022-03-02 07:29:56 +00:00
Kristoffer Dalby
0c0653df8b Merge pull request #375 from restanrm/fix-limitations-in-source-acls-rules
Fix limitations in source acls rules
2022-03-02 07:02:29 +00:00
zakaria
12b3b5f8f1 feat(aliases): add aliases for preauthkeys command
- `preauthkey`, `authkey`, `pre` are aliases for `preauthkey` command
- `ls`, `show` are aliases for `list` subcommand
- `c`, `new` are aliases for `create` subcommand
- `revoke`, `exp`, `e` are aliases for `expire` subcommand
2022-03-02 15:42:12 +10:00
zakaria
052dbfe440 feat(aliases): add aliases for apikeys command
- `apikey`, `api` are aliases for `apikeys` command
- `ls`, `show` are aliases for `list` subcommand
- `c`, `new` are aliases for `create` subcommand
- `revoke`, `exp`, `e` are aliases for the `expire` subcommand
2022-03-02 15:32:35 +10:00
zakaria
5310f8692b feat(aliases): add aliases for namespaces command
- `namespace`, `ns`, `user`, `users` are aliases for `namespaces`
   command
- `c`, `new` are aliases for the `create` subcommand
- `delete` is an alias for the `destroy` subcommand
- `mv` is an alias for the `rename` subcommand
- `ls`, `show` are aliases for the `list` subcommand
2022-03-02 14:35:20 +10:00
zakaria
aff6b84250 feat(aliases): add 'gen' alias for 'generate' command 2022-03-02 14:29:33 +10:00
zakaria
21eee912a3 feat(aliases): add aliases for nodes command
- `node`, `machine`, `machines` are aliases for `nodes` command
- `ls`, `show` aliases for `list` subcommand
- `logout`, `exp`, `e` are aliases for `expire` subcommand
- `del` is an alias for `delete` subcommand
2022-03-02 14:28:03 +10:00
zakaria
dbb2af0238 feat(aliases): add aliases for route command
- `r` is alias for `route` command
- `ls`, or `show` is alias for `list` subcommand
2022-03-02 14:27:56 +10:00
Adrien Raffin-Caboisse
77fe0b01f7 docs: update changelog 2022-03-01 22:50:22 +01:00
Adrien Raffin-Caboisse
361b4f7f4f fix(machine): allow to use * in ACL sources 2022-03-01 22:48:21 +01:00
Kristoffer Dalby
dec4ee5f73 Merge pull request #373 from restanrm/feat-email-in-acls 2022-03-01 21:18:13 +00:00
Adrien Raffin-Caboisse
b2dca80e7a docs: update changelog 2022-03-01 21:16:33 +01:00
Adrien Raffin-Caboisse
a455a874ad feat(acls): normalize the group name 2022-03-01 21:10:52 +01:00
Kristoffer Dalby
49cd761bf6 Use new machine types in tests 2022-03-01 16:34:35 +00:00
Kristoffer Dalby
6477e6a583 Use new machine types 2022-03-01 16:34:24 +00:00
Kristoffer Dalby
8a95fe517a Use specific types for all fields on machine (no datatypes.json)
This commit removes the need for datatypes.JSON and makes the code a bit
cleaner by allowing us to use proper types throughout the code when it
comes to hostinfo and other datatypes on the machine object.

This allows us to remove alot of unmarshal/marshal operations and remove
a lot of obsolete error checks.

This following commits will clean away a lot of untyped data and
uneccessary error checks.
2022-03-01 16:31:25 +00:00
Kristoffer Dalby
a9d4fa89dc Merge branch 'main' into registration-simplification 2022-03-01 15:53:06 +01:00
Kristoffer Dalby
94c5474212 Merge pull request #369 from kradalby/update-dependencies
Update dependencies
2022-03-01 15:52:27 +01:00
Kristoffer Dalby
d34d617935 Merge branch 'main' into registration-simplification 2022-03-01 15:18:24 +01:00
Kristoffer Dalby
573008757d Merge branch 'main' into update-dependencies 2022-03-01 15:16:56 +01:00
Kristoffer Dalby
4c74043f72 Merge pull request #359 from kradalby/yaml-acls
Add YAML support to ACLs
2022-03-01 15:16:37 +01:00
Kristoffer Dalby
0551b34de5 Merge branch 'main' into update-dependencies 2022-03-01 14:51:57 +01:00
Kristoffer Dalby
105812421e Merge branch 'main' into yaml-acls 2022-03-01 14:49:37 +01:00
Kristoffer Dalby
4a9fd3a680 Merge pull request #368 from kradalby/apple-profile-fix
Fix apple profile issue being generated with escaped characters
2022-03-01 14:49:07 +01:00
Kristoffer Dalby
1cb39d914c Update dependencies 2022-03-01 07:35:17 +00:00
Kristoffer Dalby
5157f356cb Fix apple profile issue being generated with escaped characters 2022-03-01 07:30:35 +00:00
Kristoffer Dalby
7c63412df5 Remove todo 2022-02-28 23:02:41 +00:00
Kristoffer Dalby
82cb6b9ddc Cleanup some unreachable code 2022-02-28 23:00:41 +00:00
Kristoffer Dalby
379017602c Reformat and add db backup note 2022-02-28 22:50:35 +00:00
Kristoffer Dalby
8bef04d8df Remove sorted todo 2022-02-28 22:45:42 +00:00
Kristoffer Dalby
5e92ddad43 Remove redundant caches
This commit removes the two extra caches (oidc, requested time) and uses
the new central registration cache instead. The requested time is
unified into the main machine object and the oidc key is just added to
the same cache, as a string with the state as a key instead of machine
key.
2022-02-28 22:42:30 +00:00
Kristoffer Dalby
e64bee778f Regenerate proto 2022-02-28 22:21:14 +00:00
Kristoffer Dalby
5e1b12948e Remove registered field from proto 2022-02-28 22:21:06 +00:00
Kristoffer Dalby
eea8e7ba6f Update changelog 2022-02-28 22:11:31 +00:00
Kristoffer Dalby
78251ce8ec Remove registrated field
This commit removes the field from the database and does a DB migration
**removing** all unregistered machines from headscale.

This means that from this version, all machines in the database is
considered registered.
2022-02-28 18:05:03 +00:00
Kristoffer Dalby
a8649d83c4 Remove all references to Machine.Registered from tests 2022-02-28 17:42:03 +00:00
Kristoffer Dalby
16b21e8158 Remove all references to Machine.Registered 2022-02-28 16:55:57 +00:00
Kristoffer Dalby
35616eb861 Fix oidc error were namespace isnt created #365 2022-02-28 16:41:28 +00:00
Kristoffer Dalby
e7bef56718 Remove reference to registered in integration test 2022-02-28 16:36:29 +00:00
Kristoffer Dalby
c6b87de959 Remove poorly aged test 2022-02-28 16:36:16 +00:00
Kristoffer Dalby
50053e616a Ignore complexity linter 2022-02-28 16:35:08 +00:00
Kristoffer Dalby
54cc3c067f Implement new machine register parameter 2022-02-28 16:34:50 +00:00
Kristoffer Dalby
402a76070f Reuse machine structure for parameters, named parameters 2022-02-28 16:34:28 +00:00
Nico Rey
9a61725e9f Metrics: Disable toggle. Set default port to 9090 2022-02-28 10:40:02 -03:00
Kristoffer Dalby
6126d6d9b5 Merge branch 'main' into metrics-listen 2022-02-28 14:24:25 +01:00
Kristoffer Dalby
469551bc5d Register new machines needing callback in memory
This commit stores temporary registration data in cache, instead of
memory allowing us to only have actually registered machines in the
database.
2022-02-28 08:06:39 +00:00
Kristoffer Dalby
1caa6f5d69 Add todo for JSON datatype 2022-02-27 18:48:25 +01:00
Kristoffer Dalby
ecc26432fd Fix excessive replace 2022-02-27 18:48:12 +01:00
Kristoffer Dalby
caffbd8956 Update cli registration with new method 2022-02-27 18:42:43 +01:00
Kristoffer Dalby
fd1e4a1dcd Generalise registration for openid 2022-02-27 18:42:24 +01:00
Kristoffer Dalby
acb945841c Generalise registration for pre auth keys 2022-02-27 18:42:15 +01:00
Kristoffer Dalby
c58ce6f60c Generalise the registration method to DRY stuff up 2022-02-27 18:40:10 +01:00
Kristoffer Dalby
d6f6939c54 Update changelog 2022-02-27 09:08:29 +01:00
Kristoffer Dalby
e0b9a317f4 Add note to config example 2022-02-27 09:05:08 +01:00
Kristoffer Dalby
c159eb7541 Add basic test of yaml parsing 2022-02-27 09:04:59 +01:00
Kristoffer Dalby
8a3a0b6403 Add YAML support to ACLs 2022-02-27 09:04:48 +01:00
Kristoffer Dalby
67d6c8f946 Remove oversensitive tracing output 2022-02-27 09:04:27 +01:00
Nico Rey
06e6c29a5b metrics: make metrics endpoint toggleable 2022-02-25 18:36:03 -03:00
Nico Rey
a9122c3de3 prometheus: replace default port by a port between the recommended prometheus range 2022-02-25 18:21:20 -03:00
Kristoffer Dalby
b1bd17f316 Merge pull request #350 from restanrm/feat-oidc-login-as-namespace 2022-02-25 13:22:26 +01:00
Adrien Raffin-Caboisse
b39faa124a Merge remote-tracking branch 'origin/main' into feat-oidc-login-as-namespace 2022-02-25 11:28:17 +01:00
Kristoffer Dalby
8689a39c96 Merge pull request #357 from kradalby/make-namespace-to-users
Remove boundaries between Namespaces
2022-02-25 11:01:41 +01:00
Kristoffer Dalby
bae8ed3e70 Merge branch 'main' into make-namespace-to-users 2022-02-25 10:39:12 +01:00
Kristoffer Dalby
08c7076667 Merge pull request #346 from kradalby/integration-test-concurrent-join
Fix ip allocation bug, make integration tests faster
2022-02-25 10:37:35 +01:00
Kristoffer Dalby
91b50550ee Update readme and glossary to reflect features and goals 2022-02-25 10:34:35 +01:00
Kristoffer Dalby
2c7064462a Update changelog 2022-02-25 10:30:58 +01:00
Kristoffer Dalby
d9e7f37280 Uncomment previous test and update them for no boundries 2022-02-25 10:27:27 +01:00
Kristoffer Dalby
e03b3d558f Remove boundries between namespaces 2022-02-25 10:26:34 +01:00
Kristoffer Dalby
2fd36dd254 Resolve merge 2022-02-25 09:08:15 +00:00
Kristoffer Dalby
381598663d Merge pull request #347 from kradalby/remove-shared
Remove the concept of "shared nodes"
2022-02-25 10:06:58 +01:00
Kristoffer Dalby
6d699d3c29 Update changelog 2022-02-25 08:44:16 +00:00
Kristoffer Dalby
ebe59a5a27 Fix utils tests, use ipset datastructure 2022-02-25 08:28:22 +00:00
Nico
d55c79e75b Merge branch 'main' into metrics-listen 2022-02-24 10:41:07 -03:00
Kristoffer Dalby
eda0a9f88a Lock allocation of IP address
current logic is not safe as it will allow an IP that isnt persisted to
the DB to be given out multiple times if machines joins in quick
succession.

This adds a lock around the "get ip" and machine registration and save
to DB so we ensure thiis isnt happning.

Currently this had to be done three places, which is silly, and outlined
in #294.
2022-02-24 13:18:18 +00:00
Adrien Raffin-Caboisse
47e8442d91 Update CHANGELOG.md
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-02-24 13:34:48 +01:00
Adrien Raffin-Caboisse
f9ce32fe1a Update CHANGELOG.md
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-02-24 13:34:36 +01:00
Kristoffer Dalby
189e883f91 Resolve merge 2022-02-24 11:41:54 +00:00
Kristoffer Dalby
aa506503e2 Merge branch 'main' into feat-oidc-login-as-namespace 2022-02-24 11:40:34 +00:00
Kristoffer Dalby
9c2c09fce7 Merge branch 'main' into remove-shared 2022-02-24 11:39:44 +00:00
Kristoffer Dalby
5596a0acef Merge pull request #297 from arch4ngel/configurable-mtls
Configurable mtls
2022-02-24 11:32:02 +00:00
Kristoffer Dalby
9687e6768d Remove retry from integration tests 2022-02-24 11:29:53 +00:00
Kristoffer Dalby
fb85c78e8a Fail integration tests fast 2022-02-24 11:28:34 +00:00
Kristoffer Dalby
d27f2bc538 Merge branch 'main' into metrics-listen 2022-02-24 11:16:57 +00:00
Kristoffer Dalby
8c33907655 Sort lint 2022-02-24 11:10:40 +00:00
Kristoffer Dalby
afb67b6e75 Merge branch 'main' into configurable-mtls 2022-02-24 11:09:05 +00:00
Kristoffer Dalby
69f220fe5c Merge branch 'main' into feat-oidc-login-as-namespace 2022-02-24 11:01:32 +00:00
Kristoffer Dalby
c46dfd761c Merge pull request #349 from kradalby/remove-cgo 2022-02-24 10:11:15 +00:00
Adrien Raffin-Caboisse
95453cba75 Merge branch 'main' into feat-oidc-login-as-namespace 2022-02-23 17:56:45 +01:00
Kristoffer Dalby
ed2175706c Merge branch 'remove-cgo' of github.com:kradalby/headscale into remove-cgo 2022-02-23 16:23:53 +00:00
Kristoffer Dalby
686e45cf27 Set all anti-cgo options and add comment 2022-02-23 16:15:20 +00:00
Adrien Raffin-Caboisse
ae6a20e4d9 fix: add valid test identified by linter 2022-02-23 14:28:25 +01:00
Adrien Raffin-Caboisse
046116656b chore: update formatting 2022-02-23 14:22:21 +01:00
Adrien Raffin-Caboisse
972bef1194 feat: add length error if hostname too long 2022-02-23 14:21:46 +01:00
Adrien Raffin-Caboisse
4f1f235a2e feat: add strip_email_domain to normalization of namespace 2022-02-23 14:03:07 +01:00
Adrien Raffin-Caboisse
7e4709c13f fix(namespace): remove name validation for destroy and get 2022-02-23 13:35:57 +01:00
Adrien Raffin-Caboisse
cef0a2b0b3 fix(namespaces_test): fix missing namespace name 2022-02-23 11:40:48 +01:00
Adrien Raffin-Caboisse
fcdbe7c510 fix(utils_test): fix namespace name 2022-02-23 11:38:20 +01:00
Adrien Raffin-Caboisse
995731a29c fix(namespace): checknamespace name before actions
I keep the check server side because it's better from a security point of view.
2022-02-23 11:32:16 +01:00
Adrien Raffin-Caboisse
45727dbb21 feat(namespace): add check function for namespace 2022-02-23 11:32:14 +01:00
Kristoffer Dalby
f0a73632e0 Merge branch 'main' into remove-cgo 2022-02-23 09:01:38 +00:00
Kristoffer Dalby
823cc493f0 Merge branch 'main' into configurable-mtls 2022-02-23 07:29:31 +00:00
Juan Font
a86b33f1ff Merge pull request #345 from juanfont/update-contributors
docs(README): update contributors
2022-02-22 23:46:54 +01:00
Juan Font
28c2bbeb27 Merge branch 'main' into update-contributors 2022-02-22 23:35:34 +01:00
github-actions[bot]
d4761da27c docs(README): update contributors 2022-02-22 22:34:27 +00:00
Juan Font
b0c7ebeb7d Merge pull request #351 from pernila/patch-1
Added FreeBSD to the supported clients
2022-02-22 23:33:58 +01:00
Juan Font
5f375d69b5 Merge branch 'main' into update-contributors 2022-02-22 23:32:02 +01:00
Juan Font
9eb705a4fb Merge branch 'main' into patch-1 2022-02-22 23:31:29 +01:00
Juan Font
1b87396a8c Merge pull request #333 from ohdearaugustin/topic/renovatebot
Topic/renovatebot
2022-02-22 23:30:46 +01:00
Juan Font
bb14bcd4d2 Merge branch 'main' into topic/renovatebot 2022-02-22 23:29:08 +01:00
pernila
48c866b058 Added FreeBSD to the supported clients
Added FreeBSD to the supported clients
Now in ports: https://www.freshports.org/security/headscale/
2022-02-22 23:06:35 +02:00
Adrien Raffin-Caboisse
fe0b43eaaf chore: update changelog 2022-02-22 21:20:59 +01:00
Adrien Raffin-Caboisse
afd4a3706e chore: update formating 2022-02-22 21:05:39 +01:00
Adrien Raffin-Caboisse
717250adb3 feat: removing matchmap from headscale 2022-02-22 20:58:08 +01:00
Kristoffer Dalby
67f5c32b49 Only allow one connection to sqlite 2022-02-22 19:04:52 +00:00
Adrien Raffin-Caboisse
0191ea93ff feat(oidc): bind email to namespace 2022-02-22 19:59:15 +01:00
Adrien Raffin-Caboisse
92ffac625e feat(namespace): add normalization function for namespace 2022-02-22 19:59:12 +01:00
Kristoffer Dalby
bfbcea35a0 Remove dependency on CGO
This commit changes the SQLite dependency to one that does not depend on
CGO. It uses a C-to-Go translated sqlite library that is Pure go.
2022-02-22 16:51:54 +00:00
Kristoffer Dalby
638a84adb9 Merge branch 'main' into integration-test-concurrent-join 2022-02-22 16:49:32 +00:00
Kristoffer Dalby
ec58979ce0 Merge branch 'main' into remove-shared 2022-02-22 16:48:14 +00:00
Kristoffer Dalby
7e6e093f17 Merge branch 'integration-test-concurrent-join' of github.com:kradalby/headscale into integration-test-concurrent-join 2022-02-22 16:19:28 +00:00
Kristoffer Dalby
4962335860 Remove dependency on CGO
This commit changes the SQLite dependency to one that does not depend on
CGO. It uses a C-to-Go translated sqlite library that is Pure go.
2022-02-22 16:18:25 +00:00
Kristoffer Dalby
a37339fa54 Merge pull request #348 from restanrm/remove-comment
fix(machine): remove comment
2022-02-22 14:06:12 +00:00
Kristoffer Dalby
f7eeb979fb Add timeout 2022-02-22 13:46:59 +00:00
Adrien Raffin-Caboisse
f2f8d834e8 fix(machine): remove comment
After some more tests in tailscale I couldn't replicate the behavior
described in there.

When adding a rule, allowing A to talk to B the reverse connection was
instantly added to B to allow communication to B.

The previous assumption was probably wrong.
2022-02-22 11:26:21 +01:00
Kristoffer Dalby
fe2f75d13d Allow integration test to retry 2022-02-22 07:40:56 +00:00
Kristoffer Dalby
52db6188df Merge branch 'main' into update-contributors 2022-02-21 23:38:56 +00:00
Kristoffer Dalby
8dca40535f Test if we can join headscale in parallell to speed up 2022-02-21 23:16:39 +00:00
Kristoffer Dalby
f4c302f1fb Uncomment tests that will failed in transition period 2022-02-21 23:10:20 +00:00
Kristoffer Dalby
4ca8181dcb Remove sharing from integration tests 2022-02-21 23:04:10 +00:00
Kristoffer Dalby
24a8e198a1 Remove sharing references across the code 2022-02-21 23:01:35 +00:00
Kristoffer Dalby
9411ec47c3 Remove sharing class and tests 2022-02-21 22:53:30 +00:00
Kristoffer Dalby
1e8f4dbdff Drop shared node table 2022-02-21 22:52:55 +00:00
Kristoffer Dalby
9399754489 Remove protobuf share/unshare generated go 2022-02-21 22:48:27 +00:00
Kristoffer Dalby
9d1752acbc Remove protobuf share/unshare 2022-02-21 22:48:14 +00:00
Kristoffer Dalby
6da2a19d10 Remove grpc share/unshare functions 2022-02-21 22:45:04 +00:00
Kristoffer Dalby
9ceac5c0fc Remove CLI and tests for Shared node 2022-02-21 22:44:08 +00:00
Kristoffer Dalby
f562ad579a Merge branch 'main' into configurable-mtls 2022-02-21 21:44:49 +00:00
github-actions[bot]
bbadeb567a docs(README): update contributors 2022-02-21 21:41:48 +00:00
Kristoffer Dalby
69cdfbb56f Merge pull request #320 from restanrm/feat-improve-acls-usage
Improvements on the ACLs and bug fixing
2022-02-21 21:41:15 +00:00
Adrien Raffin-Caboisse
d971f0f0e6 fix(acls_test): fix comment in go code 2022-02-21 21:48:05 +01:00
Adrien Raffin-Caboisse
650108c7c7 chore(fmt): apply fmt 2022-02-21 21:46:40 +01:00
Adrien Raffin-Caboisse
baae266db0 Update acls_test.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-02-21 20:25:41 +01:00
Adrien Raffin-Caboisse
50af44bc2f fix: add error checking in acl and poll
If aclPolicy is not defined, in updateAclPolicy, return an error.
2022-02-21 20:06:31 +01:00
Nico Rey
e3bcc88880 Linter: make linter happy 2022-02-21 15:22:36 -03:00
Nico Rey
14e49885fb metrics/kustomize: update Kustomize examples 2022-02-21 12:51:25 -03:00
Nico Rey
fbc1843889 metrics/tests: update tests 2022-02-21 12:51:05 -03:00
Nico Rey
45d5ab30ff metrics/cfg: add a new entry for the Prometheus listen address 2022-02-21 12:50:44 -03:00
Nico Rey
d5fd7a5c00 metrics: add a new router and listener for Prometheus' metrics endpoint 2022-02-21 12:50:15 -03:00
Justin Angel
b5a59d4e7a updating changelog and docs 2022-02-21 10:20:11 -05:00
Adrien Raffin-Caboisse
211fe4034a chore(linter): ignore tt var as it's generated code (vscode) 2022-02-21 16:10:20 +01:00
Justin Angel
daa75da277 Linting and updating tests 2022-02-21 10:09:23 -05:00
Adrien Raffin-Caboisse
25550f8866 chore(format): run prettier on repo 2022-02-21 16:06:20 +01:00
Adrien Raffin-Caboisse
4bbe0051f6 chore(machines): apply lint 2022-02-21 10:02:59 +01:00
Adrien Raffin-Caboisse
5ab62378ae tests(machines): test all combinations of peer filtering 2022-02-21 09:58:19 +01:00
Adrien Raffin-Caboisse
f006860136 feat(machines): untie dependency with class for filter func
The dependency to the `headscale` struct makes tests harder to do.

This change allow to easily add some tests for this quite sensible function.
2022-02-21 09:58:19 +01:00
Adrien Raffin-Caboisse
9c6ce02554 fix(machines): use ListAllMachines function
added a simple filter to remove the current node
2022-02-21 09:58:19 +01:00
Adrien Raffin-Caboisse
960412a335 fix(machines): simplify complex if check
This should fix the performance issue with computation of `dst` variable. It's also easier to read now.
2022-02-21 09:58:19 +01:00
Kristoffer Dalby
ecb3ee6bfa Merge branch 'main' into feat-improve-acls-usage 2022-02-21 08:51:21 +00:00
Adrien Raffin-Caboisse
5242025ab3 fix(machines): renaming following review comments 2022-02-20 23:50:08 +01:00
Adrien Raffin-Caboisse
b3d0fb7a93 fix(machine): revert modifications
Using h.ListAllMachines also listed the current machine in the result. It's unnecessary (I don't know if it's harmful).

Breaking the check with the `matchSourceAndDestinationWithRule` broke the tests. We have a specificity with the '*' destination that isn't symetrical.
I need to think of a better way to do this. It too hard to read.
2022-02-20 23:47:04 +01:00
Adrien Raffin-Caboisse
5e167cc00a fix(tests): fix naming issues related to code review 2022-02-20 23:00:31 +01:00
Adrien Raffin-Caboisse
d00251c63e fix(acls,machines): apply code review suggestions 2022-02-20 21:26:20 +01:00
Adrien Raffin-Caboisse
4f9ece14c5 Apply suggestions from code review on changelog
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2022-02-20 20:47:12 +01:00
Kristoffer Dalby
7bf2a91dd0 Merge branch 'main' into configurable-mtls 2022-02-20 14:33:23 +00:00
Justin Angel
385dd9cc34 refactoring 2022-02-20 09:06:14 -05:00
Kristoffer Dalby
602291df61 Merge pull request #338 from juanfont/update-contributors 2022-02-19 23:13:08 +00:00
github-actions[bot]
5245f1accc docs(README): update contributors 2022-02-19 22:48:26 +00:00
Kristoffer Dalby
91babb5130 Merge pull request #336 from ohdearaugustin/topic/fix-contributors-action 2022-02-19 21:08:53 +00:00
ohdearaugustin
8798efd353 contributor: set specific version 2022-02-19 21:36:08 +01:00
Kristoffer Dalby
66a12004e7 Merge branch 'main' into topic/renovatebot 2022-02-19 19:34:30 +00:00
Kristoffer Dalby
74621e2750 Merge pull request #332 from e-zk/main
Fix spelling error
2022-02-19 19:34:24 +00:00
Kristoffer Dalby
74c3c6bb60 Merge branch 'main' into main 2022-02-19 19:32:34 +00:00
Kristoffer Dalby
84b98e716a Merge pull request #334 from ohdearaugustin/topic/renovatebot-codeowner
CODEOWNER: add renovate config ohdearaugustin
2022-02-19 19:32:21 +00:00
ohdearaugustin
e9f13b6031 CODEOWNER: add renovate config ohdearaugustin 2022-02-19 20:28:08 +01:00
ohdearaugustin
fe6d47030f renovatebot: configure 2022-02-19 20:15:53 +01:00
ohdearaugustin
a19550adbf prettier: renovatebot.yml 2022-02-19 19:49:12 +01:00
ohdearaugustin
3db88d27de github/workflows: init renovatebot 2022-02-19 19:49:12 +01:00
e-zk
a6b7bc5939 Fix spelling error 2022-02-20 03:14:51 +10:00
Kristoffer Dalby
397b6fc4bf Merge branch 'main' into docs-acl-modifications 2022-02-18 20:13:10 +00:00
Kristoffer Dalby
7d5e6d3f0f Merge pull request #330 from kradalby/codeowners
Add ohdearaugustin to CODEOWNERS for config and docs
2022-02-18 20:12:29 +00:00
Kristoffer Dalby
7a90c2fba1 Merge branch 'main' into codeowners 2022-02-18 20:11:33 +00:00
Kristoffer Dalby
5cf215a44b Merge pull request #325 from juanfont/kradalby-patch-4
Update changelog for 0.13.0
2022-02-18 20:11:03 +00:00
Kristoffer Dalby
7916fa8b45 Add ohdearaugustin to CODEOWNERS for config and docs 2022-02-18 19:57:03 +00:00
Kristoffer Dalby
5fbef07627 Update changelog for 0.13.0 2022-02-18 18:54:27 +00:00
Kristoffer Dalby
21df798f07 Merge branch 'main' into feat-improve-acls-usage 2022-02-18 17:19:19 +00:00
Kristoffer Dalby
67bb1fc9dd Merge pull request #324 from m-tanner-dev0/patch-1 2022-02-18 07:18:22 +00:00
Tanner
61bfa79be2 Update README.md
change flippant language
2022-02-17 17:55:40 -08:00
Adrien Raffin-Caboisse
f073d8f43c chore(lint): ignore linting on test_expandalias
This is a false positive on the way the function is built.
Small tests cases are all inside this functions, making it big.
2022-02-17 09:32:55 +01:00
Adrien Raffin-Caboisse
5f642eef76 chore(lint): more lint fixing 2022-02-17 09:32:54 +01:00
Adrien Raffin-Caboisse
d8c4c3163b chore(fmt): apply make fmt command 2022-02-17 09:32:54 +01:00
Adrien Raffin-Caboisse
9cedbbafd4 chore(all): update some files for linter 2022-02-17 09:32:51 +01:00
Adrien Raffin-Caboisse
aceaba60f1 docs(changelog): bump changelog 2022-02-17 09:30:09 +01:00
Adrien Raffin-Caboisse
7b5ba9f781 docs(acl): add configuration example to explain acls 2022-02-17 09:30:09 +01:00
Adrien Raffin
de59946447 feat(acls): rewrite functions to be testable
Rewrite some function to get rid of the dependency on Headscale object. This allows us
to write succinct test that are more easy to review and implement.

The improvements of the tests allowed to write the removal of the tagged hosts
from the namespace as specified here: https://tailscale.com/kb/1068/acl-tags/
2022-02-17 09:30:09 +01:00
Adrien Raffin
97eac3b938 feat(acl): update frequently the aclRules
This call should be done quite at each modification of a server resources like RequestTags.
When a server changes it's tag we should rebuild the ACL rules.

When a server is added to headscale we also should update the ACLRules.
2022-02-17 09:30:08 +01:00
Adrien Raffin
fb45138fc1 feat(acls): check acl owners and add bunch of tests 2022-02-17 09:30:08 +01:00
Adrien Raffin
e9949b4c70 feat(acls): simplify updating rules 2022-02-17 09:30:08 +01:00
Adrien Raffin
e482dfeed4 feat(machine): add ACLFilter if ACL's are enabled.
This commit change the default behaviour and remove the notion of namespaces between the hosts. It allows all namespaces to be only filtered by the ACLs. This behavior is closer to tailsnet.
2022-02-17 09:30:05 +01:00
Jamie Greeff
9b7d657cbe Return all peers instead of peers in same namespace 2022-02-17 09:27:59 +01:00
Adrien Raffin-Caboisse
55d746d3f5 docs(acls-proposal): wording comment
A hidden thing was implied in this document is that each person should have his own namespace.
Hidden information in spicification isn't good.
Thank's @kradalby for pointing it out.
2022-02-16 09:16:25 +01:00
Kristoffer Dalby
73497382b7 Merge pull request #306 from kradalby/apiwork
Introduce API keys and enable remote control API
2022-02-15 22:23:32 +00:00
Adrien Raffin-Caboisse
c364c2a382 chore(acl-proposals): apply prettier 2022-02-15 09:53:22 +01:00
Adrien Raffin-Caboisse
e540679dbd docs(acl-proposals): integrate comments 2022-02-15 09:52:05 +01:00
Adrien Raffin-Caboisse
86b329d8bf chore(docs): create proposals directory 2022-02-15 09:27:33 +01:00
Kristoffer Dalby
b2b2954545 Merge branch 'main' into apiwork 2022-02-14 22:29:20 +00:00
Kristoffer Dalby
a3360b082f Merge pull request #321 from ohdearaugustin/topic/specific-go-version 2022-02-14 22:03:17 +00:00
Kristoffer Dalby
b721502147 Merge branch 'main' into topic/specific-go-version 2022-02-14 20:51:33 +00:00
Kristoffer Dalby
1869bff4ba Merge pull request #316 from kradalby/kv-worker-cleanup 2022-02-14 20:51:00 +00:00
ohdearaugustin
0b9dd19ec7 Dockerfiles: update go version to 1.17.7 2022-02-14 21:32:20 +01:00
ohdearaugustin
b2889bc355 github/workflows: set specific go version 2022-02-14 21:31:49 +01:00
Kristoffer Dalby
28c824acaf Merge branch 'main' into apiwork 2022-02-14 16:17:34 +00:00
Kristoffer Dalby
57f1da6dca Merge branch 'main' into kv-worker-cleanup 2022-02-14 11:35:15 +00:00
Kristoffer Dalby
c9640b2f3e Merge pull request #317 from kradalby/sponsor 2022-02-14 11:34:56 +00:00
Kristoffer Dalby
546b1e8a05 Merge branch 'main' into kv-worker-cleanup 2022-02-14 10:17:03 +00:00
Kristoffer Dalby
3b54a68f5c Merge branch 'main' into sponsor 2022-02-14 10:16:58 +00:00
Kristoffer Dalby
1b1aac18d2 Merge pull request #315 from kradalby/windows-client-docs2 2022-02-14 10:16:25 +00:00
Kristoffer Dalby
f30ee3d2df Add note about support in readme 2022-02-13 11:07:45 +00:00
Kristoffer Dalby
9f80349471 Add sponsorship button
This commit adds a sponsor/funding section to headscale.

@juanfont and I have discussed this and this arrangement is agreed upon
and hopefully this can bring us to a place in the future were even more
features and prioritization can be put upon the project.
2022-02-13 11:02:31 +00:00
Kristoffer Dalby
14b23544e4 Add note about running grpc behind a proxy and combining ports 2022-02-13 09:48:33 +00:00
Kristoffer Dalby
4e54796384 Allow gRPC server to run insecure 2022-02-13 09:08:46 +00:00
Kristoffer Dalby
c3b68adfed Fix lint 2022-02-13 08:46:35 +00:00
Kristoffer Dalby
0018a78d5a Add insecure option
Add option to not _validate_ if the certificate served from headscale is
trusted.
2022-02-13 08:41:49 +00:00
Kristoffer Dalby
50f0270543 Merge branch 'main' into windows-client-docs2 2022-02-12 22:35:23 +00:00
Kristoffer Dalby
bb80b679bc Remove RequestMapUpdates function 2022-02-12 21:04:00 +00:00
Kristoffer Dalby
6fa0903a8e Update changelog 2022-02-12 20:50:17 +00:00
Kristoffer Dalby
2bc8051ae5 Remove kv-namespace-worker
This commit removes the namespace kv worker and related code, now that
we talk over gRPC to the server, and not directly to the DB, we should
not need this anymore.
2022-02-12 20:46:05 +00:00
Kristoffer Dalby
4841e16386 Add remote control doc 2022-02-12 20:39:42 +00:00
Kristoffer Dalby
d79ccfc05a Add comment on why grpc is on its own port, replace deprecated 2022-02-12 19:50:12 +00:00
Kristoffer Dalby
ead8b68a03 Fix lint 2022-02-12 19:42:55 +00:00
Kristoffer Dalby
3bb4c28c9a Merge branch 'main' into apiwork 2022-02-12 19:39:30 +00:00
Kristoffer Dalby
2fbcc38f8f Emph trusted cert 2022-02-12 19:36:43 +00:00
Kristoffer Dalby
315ff9daf0 Remove insecure, only allow valid certs 2022-02-12 19:35:55 +00:00
Kristoffer Dalby
4078e75b50 Correct log message 2022-02-12 19:30:25 +00:00
Kristoffer Dalby
58bfea4e64 Update examples and docs 2022-02-12 19:08:59 +00:00
Kristoffer Dalby
e18078d7f8 Rename j 2022-02-12 19:08:41 +00:00
Kristoffer Dalby
c73b57e7dc Use undeprecated method for insecure 2022-02-12 19:08:33 +00:00
Kristoffer Dalby
531298fa59 Fix import 2022-02-12 17:13:51 +00:00
Kristoffer Dalby
30a2ccd975 Add tls certs as creds for grpc 2022-02-12 17:05:30 +00:00
Kristoffer Dalby
59e48993f2 Change the http listener 2022-02-12 16:33:18 +00:00
Kristoffer Dalby
bfc6f6e0eb Split grpc and http 2022-02-12 16:15:26 +00:00
Kristoffer Dalby
811d3d510c Add grpc_listen_addr config option 2022-02-12 16:14:33 +00:00
Kristoffer Dalby
2aba37d2ef Try to support plaintext http2 after termination 2022-02-12 14:42:23 +00:00
Kristoffer Dalby
8853ccd5b4 Terminate tls immediatly, mux after 2022-02-12 13:25:27 +00:00
Kristoffer Dalby
c794f32f58 Merge pull request #312 from hdhoang/patch-1 2022-02-12 12:27:57 +00:00
Kristoffer Dalby
dd8bae8c61 Add link from the docs readme 2022-02-11 18:39:41 +00:00
Kristoffer Dalby
1b47ddd583 Improve the windows client docs as per discord recommendations 2022-02-11 18:36:53 +00:00
Hoàng Đức Hiếu
20991d6883 Merge branch 'main' into patch-1 2022-02-11 17:56:46 +07:00
Kristoffer Dalby
96f09e3f30 Merge pull request #313 from kradalby/windows-client-docs 2022-02-11 09:34:11 +00:00
Kristoffer Dalby
8f40696f35 Merge branch 'main' into windows-client-docs 2022-02-11 09:32:49 +00:00
Kristoffer Dalby
c1845477ef Merge pull request #314 from kradalby/tailscale-204 2022-02-11 09:32:24 +00:00
Kristoffer Dalby
1d40de3095 Update changelog 2022-02-11 08:45:02 +00:00
Kristoffer Dalby
2357fb6f80 Upgrade all dependencies 2022-02-11 08:43:31 +00:00
Kristoffer Dalby
ba8afdb7be Upgrade to tailscale 1.20.4 2022-02-11 08:39:00 +00:00
Kristoffer Dalby
d9aaa0bdfc Add docs on how to set up Windows clients 2022-02-11 08:26:22 +00:00
Hoàng Đức Hiếu
66ff34c2dd apply changelog 2022-02-11 13:49:09 +07:00
Hoàng Đức Hiếu
150652e939 poll: fix swapped machine<->namespace labels 2022-02-11 13:46:36 +07:00
Adrien Raffin-Caboisse
7bdd7748e4 fix(acl): add missing internal namespace communications 2022-02-10 12:03:03 +01:00
Adrien Raffin-Caboisse
0426212348 docs(acls): add example use case 2022-02-10 10:42:26 +01:00
Adrien Raffin-Caboisse
85cf443ac6 docs(acls): Issues with ACL and proposition 2022-02-08 16:59:35 +01:00
Justin Angel
1b2fff4337 Merge branch 'main' into configurable-mtls 2022-02-02 11:54:49 -05:00
Kristoffer Dalby
8c79165b0d Merge pull request #305 from lachy-2849/main 2022-02-02 08:09:06 +00:00
lachy-2849
7b607b3fe8 Forgot to run Prettier 2022-02-01 19:32:13 -05:00
lachy-2849
41fbe47cdf Note when running as another user in systemd
Headscale commands fail when running them as the current user instead of the user defined in the systemd file. This note provides 2 methods of how to correctly run the headscale commands.
2022-02-01 14:23:18 -05:00
Justin Angel
af25aa75d9 Merge branch 'configurable-mtls' of github.com:arch4ngel/headscale into configurable-mtls 2022-01-31 10:27:57 -05:00
Justin Angel
da5250ea32 linting again 2022-01-31 10:27:43 -05:00
Kristoffer Dalby
168b1bd579 Merge branch 'main' into configurable-mtls 2022-01-31 12:28:00 +00:00
Justin Angel
9de5c7f8b8 updating default 2022-01-31 07:22:17 -05:00
Justin Angel
52db80ab0d Merge branch 'configurable-mtls' of github.com:arch4ngel/headscale into configurable-mtls 2022-01-31 07:19:14 -05:00
Justin Angel
0c3fd16113 refining and adding tests 2022-01-31 07:18:50 -05:00
Kristoffer Dalby
e05c5e0b93 Merge pull request #303 from kradalby/migrate_ipaddresses 2022-01-31 08:57:22 +00:00
Justin Angel
310e7b15c7 making alternatives constants 2022-01-30 10:46:57 -05:00
Kristoffer Dalby
9e3318ca27 Simplify postgres uuid-ossp stirng 2022-01-30 14:53:40 +00:00
Kristoffer Dalby
e9adfcd678 Migrate ip_address field to ip_addresses
The automatic migration did not pick up this change and it breaks
current setups as it only adds a new field which is empty.

This means that clients who dont request a new IP will not have any IP
at all.
2022-01-30 14:18:24 +00:00
Justin Angel
d44b2a7c01 adding default for tls_client_auth_mode 2022-01-30 07:26:28 -05:00
Kristoffer Dalby
5b5ecd52e1 Merge pull request #208 from enoperm/ipv6
Support for IPv6 prefixes in namespaces
2022-01-30 10:46:20 +00:00
Kristoffer Dalby
eddd62eee0 Merge branch 'main' into ipv6 2022-01-30 10:09:52 +00:00
Kristoffer Dalby
38c27f6bf8 Merge pull request #300 from stensonb/patch-2
Typo
2022-01-30 10:09:35 +00:00
Kristoffer Dalby
90fb9aa4ed Merge branch 'main' into ipv6 2022-01-30 10:08:44 +00:00
Kristoffer Dalby
3af1253a65 Merge branch 'main' into patch-2 2022-01-30 10:08:28 +00:00
Kristoffer Dalby
eb1ce64b7c Merge pull request #301 from kradalby/goreleaser
Set goreleaser to only care about Go 1.17
2022-01-30 10:08:04 +00:00
Kristoffer Dalby
2c9ed63021 Merge branch 'main' into goreleaser 2022-01-30 10:07:35 +00:00
Kristoffer Dalby
4c779d306b Merge pull request #302 from kradalby/build-avoidance
Set up build avoidance
2022-01-30 10:07:30 +00:00
Kristoffer Dalby
0862f60ff0 Put depth in the correct place 2022-01-30 09:54:26 +00:00
Kristoffer Dalby
991175f2aa Add depth and avoidance for build 2022-01-30 09:49:15 +00:00
Kristoffer Dalby
1815040d98 Set up build build avoidance
This commit configures the CI to run specific parts of the CI when
relevant changes has been made.

This should help us not have to deal with the integration tests when we
do doc/admin changes.
2022-01-30 09:43:48 +00:00
Kristoffer Dalby
71ab4c9b2c Fix type according to config schema 2022-01-30 08:59:25 +00:00
Kristoffer Dalby
e0c22a414b Remove wrong comment 2022-01-30 08:56:28 +00:00
Kristoffer Dalby
4e63bba4fe Only compat go 1.17 in go mod tidy 2022-01-30 08:55:41 +00:00
Kristoffer Dalby
445c04baf7 Fix lint 2022-01-30 08:35:10 +00:00
Kristoffer Dalby
ad4e3a89e0 Format changelog 2022-01-30 08:25:49 +00:00
Kristoffer Dalby
6f6018bad5 Merge branch 'main' into ipv6 2022-01-30 08:21:11 +00:00
Bryan Stenson
ccd41b9a13 Typo 2022-01-29 22:34:51 -08:00
Juan Font
d8ce440309 Merge pull request #299 from kradalby/0124prep
Tag 0.12.4 in CHANGELOG
2022-01-30 01:13:10 +01:00
Kristoffer Dalby
0609c97459 Merge branch 'main' into configurable-mtls 2022-01-29 20:15:58 +00:00
Kristoffer Dalby
2f576b2fb1 Tag 0.12.4 in CHANGELOG 2022-01-29 20:04:56 +00:00
Kristoffer Dalby
853a5288f1 Merge pull request #292 from kradalby/socket-permission
Make Unix socket permissions configurable
2022-01-29 19:55:11 +00:00
Kristoffer Dalby
cd0df1e46f Merge branch 'main' into socket-permission 2022-01-29 19:30:49 +00:00
Juan Font
b195c87418 Merge pull request #290 from kradalby/generate-privkey
Add generate private-key command
2022-01-29 20:24:52 +01:00
Justin Angel
c98a559b4d linting/formatting 2022-01-29 14:15:33 -05:00
Justin Angel
5935b13b67 refining 2022-01-29 13:35:08 -05:00
Justin Angel
9e619fc020 Making client authentication mode configurable 2022-01-29 12:59:31 -05:00
Csaba Sarkadi
45bcf39894 fixup! fixup! cmd/headscale/cli/utils: merge ip_prefix with ip_prefixes in config 2022-01-29 16:52:27 +01:00
Csaba Sarkadi
0a1db89d33 fixup! cmd/headscale/cli/utils: merge ip_prefix with ip_prefixes in config 2022-01-29 16:27:36 +01:00
Kristoffer Dalby
dbfb9e16e0 Merge branch 'main' into socket-permission 2022-01-29 15:26:19 +00:00
Kristoffer Dalby
8aa2606853 Merge branch 'main' into generate-privkey 2022-01-29 15:26:15 +00:00
Kristoffer Dalby
a238a8b33a Merge pull request #291 from kradalby/tailscale-203
Upgrade to latest tailscale
2022-01-29 15:26:06 +00:00
Csaba Sarkadi
74f26d3685 fixup! CHANGELOG: document breaking configuration change regarding multiple prefixes 2022-01-29 16:08:02 +01:00
Csaba Sarkadi
e66f8b0eeb cmd/headscale/cli/utils: merge ip_prefix with ip_prefixes in config 2022-01-29 16:04:15 +01:00
Kristoffer Dalby
e7b69dbf91 Merge branch 'main' into generate-privkey 2022-01-29 14:35:24 +00:00
Kristoffer Dalby
13f23d2e7e Merge branch 'main' into socket-permission 2022-01-29 14:34:36 +00:00
Csaba Sarkadi
7a86321252 CHANGELOG: document breaking configuration change regarding multiple prefixes 2022-01-29 15:33:54 +01:00
Kristoffer Dalby
7aace7eb6b Update CHANGELOG.md 2022-01-29 14:33:12 +00:00
Kristoffer Dalby
7a6be36f46 Merge branch 'main' into tailscale-203 2022-01-29 14:32:04 +00:00
Kristoffer Dalby
bb27c80bad Update CHANGELOG.md 2022-01-29 14:31:42 +00:00
Csaba Sarkadi
c0c3b7d511 Merge remote-tracking branch 'origin/main' into ipv6 2022-01-29 15:27:49 +01:00
Csaba Sarkadi
6220836050 utils: extract GetIPPrefixEndpoints from anonymous function 2022-01-29 15:26:28 +01:00
Kristoffer Dalby
b122d06f12 Merge pull request #278 from enoperm/pollnetmap-update-only 2022-01-29 13:46:08 +00:00
Kristoffer Dalby
6f9ed958ca Merge branch 'main' into pollnetmap-update-only 2022-01-29 13:12:09 +00:00
Kristoffer Dalby
39ce59fcb1 Merge branch 'main' into generate-privkey 2022-01-29 13:11:26 +00:00
Kristoffer Dalby
052fccdc98 Merge pull request #289 from juanfont/whitespace 2022-01-29 13:09:16 +00:00
Csaba Sarkadi
17411b65f3 fixup! fixup! update CHANGELOG
prettier changes
2022-01-29 13:48:14 +01:00
Csaba Sarkadi
bf7ee78324 config-example: add configuration for a dual-stack tailnet 2022-01-28 22:13:45 +01:00
Csaba Sarkadi
fbe5054a67 fixup! update CHANGELOG 2022-01-28 22:00:13 +01:00
Csaba Sarkadi
761147ea3b update CHANGELOG 2022-01-28 21:59:08 +01:00
Csaba Sarkadi
25ccf5ef18 PollNetMapStream: do not create any rows during long-poll operation 2022-01-28 21:59:08 +01:00
Kristoffer Dalby
b4f8961e44 Make Unix socket permissions configurable 2022-01-28 18:58:22 +00:00
Kristoffer Dalby
726ccc8c1f Upgrade to latest tailscale 2022-01-28 18:15:41 +00:00
Kristoffer Dalby
126e694f26 Add generate private-key command
This commit adds a command to generate a private key for headscale.

Mostly useful for systems were you drive the deployment from another
machine and use a secret management system.
2022-01-28 18:08:52 +00:00
Kristoffer Dalby
ab45cd37f8 Only golint new problems 2022-01-28 17:40:39 +00:00
Kristoffer Dalby
f59071ff1c Trim whitespace from privateKey before parsing 2022-01-28 17:23:01 +00:00
Kristoffer Dalby
537cd35cb2 Try to add the grpc cert correctly 2022-01-25 22:22:15 +00:00
Kristoffer Dalby
56b6528e3b Run prettier 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
bae7ba46de Update changelog 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
fa197cc183 Add docs for remote access 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
00c69ce50c Enable remote gRPC and HTTP API
This commit enables the existing gRPC and HTTP API from remote locations
as long as the user can provide a valid API key. This allows users to
control their headscale with the CLI from a workstation. 🎉
2022-01-25 22:11:15 +00:00
Kristoffer Dalby
a6e22387fd Formatting of machine.go 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
a730f007d8 Formatting of DNS files 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
3393363a67 Add safe random hash generators 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
8218ef96ef Formatting of integration tests 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
e8e573de62 Add apikeys command integration test 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
05db1b7109 Formatting and improving logs for config loading 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
6e14fdf0d3 More reusable stuff in cli 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
1fd57a3375 Add apikeys command to create, list and expire 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
b4259fcd79 Add helper function for colouring expiries 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
f9137f3bb0 Create helper functions around gRPC interface 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
b1a9b1ada1 Generate code from proto 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
b8e9024845 Add proto model for api key 2022-01-25 22:11:15 +00:00
Kristoffer Dalby
70d82ea184 Add migration for new data model 2022-01-25 22:11:05 +00:00
Kristoffer Dalby
9dc20580c7 Add api key data model and helpers
This commits introduces a new data model for holding api keys for the
API. The keys are stored in the database with a prefix and a hash and
bcrypt with 10 passes is used to store the hash and it is "one way
safe".

Api keys have an expiry logic similar to pre auth keys.

A key cannot be retrieved after it has created, only verified.
2022-01-25 22:11:05 +00:00
Kristoffer Dalby
4d60aeae18 Merge pull request #282 from ryanfowler/main 2022-01-23 21:23:50 +00:00
Ryan Fowler
67d1dd984f Fix missing return in PollNetMapHandler 2022-01-21 21:48:58 -08:00
Juan Font
b02f8dd45d Merge pull request #274 from majst01/reduce-binary-size
Strip binary, update to go-1.17.6
2022-01-20 11:38:52 +01:00
Kristoffer Dalby
3837f1714a Merge branch 'main' into reduce-binary-size 2022-01-17 08:47:19 +00:00
Kristoffer Dalby
ed5498ef86 Merge pull request #276 from jimt/patch-1 2022-01-17 08:46:42 +00:00
Csaba Sarkadi
e2f8c69e2e integration-test: use tailscale ip to test dual-stack MagicDNS 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
beb3e9abc2 integration-test: taildrop test refactor 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
78039f4cea integration-test: use TUN devices, enable IPv6 addresses on local interfaces in containers 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
ed39b91f71 Dockerfiles: specify origin registry explicitly 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
8f632e9062 machine: isOutdated: handle machines without LastSuccefulUpdate set 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
a32175f791 PollNetMapHandler: refactor with chan lifetimes in mind
* Resolves an issue where sometimes attempted sends on a closed channel
  happened by ensuring the channels remain open for the entire goroutine.
* May be of help with regards to issue #203
2022-01-16 14:18:22 +01:00
Csaba Sarkadi
d35fb8bba0 integration-test: add IPv6 prefix to configuration 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
115d0cbe85 dns: IPv6 roots generation 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
1a6e5d8770 Add support for multiple IP prefixes 2022-01-16 14:18:22 +01:00
Csaba Sarkadi
3a3aecb774 Regenerate files based on ProtoBuf schema. 2022-01-16 14:17:51 +01:00
Csaba Sarkadi
8b40343277 Add multiple IP prefixes support to ProtoBuf schema 2022-01-16 14:17:27 +01:00
Csaba Sarkadi
7ec8346179 Do not assume IPv4 during Tailscale node construction 2022-01-15 16:06:34 +01:00
Csaba Sarkadi
46cdce00af Do not assume IPv4 during address generation 2022-01-15 16:06:34 +01:00
Jim Tittsler
86f3f26a18 Fix typos 2022-01-15 16:44:36 +09:00
Stefan Majer
febbb6006f use most recent go 1.17 for tests 2022-01-14 12:59:53 +01:00
Stefan Majer
1d68509463 Strip binary, update to go-1.17.6 2022-01-14 09:19:16 +01:00
Kristoffer Dalby
b6d0c4f2aa Merge pull request #273 from juanfont/tailscale-1-20
Add new tailscale version to integration test
2022-01-13 22:21:27 +00:00
Kristoffer Dalby
2c057c2d89 Update integration_test.go 2022-01-13 19:16:12 +00:00
Juan Font
cf7effda1b Merge pull request #272 from juanfont/cl-0.12.3
Prepare CHANGELOG for 0.12.3
2022-01-13 17:02:37 +01:00
Juan Font Alonso
19effe7034 Updated changelog for 0.12.3 2022-01-13 12:42:56 +01:00
Juan Font
41053482b3 Merge pull request #271 from juanfont/minor-security-fixes
Minor security updates in go.mod
2022-01-12 22:13:28 +01:00
Juan Font
e463283a58 Merge branch 'main' into minor-security-fixes 2022-01-12 17:10:15 +01:00
Juan Font
99814b468b Merge pull request #270 from artemklevtsov/docker-alpine
Add docker alpine image
2022-01-12 17:10:05 +01:00
Juan Font Alonso
163ecb2a6b go.sum updated 2022-01-12 15:22:16 +01:00
Juan Font Alonso
4660b265d9 Minor security updates in go.mod 2022-01-12 15:17:55 +01:00
Artem Klevtsov
1b6bad0b63 Add docker alpine image 2022-01-12 19:29:59 +07:00
Juan Font
26623d794b Merge pull request #268 from juanfont/prepare-0.12.2
Prepare CHANGELOG for 0.12.2
2022-01-11 16:49:51 +01:00
Juan Font Alonso
e9cc60e49c Updated changelog for 0.12.2 2022-01-11 15:45:13 +01:00
Juan Font
be2a28dd61 Merge pull request #267 from piec/patch-1
Fix example config link
2022-01-11 15:39:55 +01:00
Pierre Carru
cec236ce24 Fix example config link
Everything is in the title :p
2022-01-10 14:44:11 +01:00
Kristoffer Dalby
45d331da99 Merge pull request #263 from JJGadgets/patch-1 2022-01-06 10:40:04 +00:00
JJGadgets
897fa558b0 prettier: Docker docs 2022-01-06 17:25:07 +08:00
JJGadgets
d971cf1295 Improve Docker docs
- Fix URLs referring to files in this repository
- Better explain that we are creating the headscale directory and running the commands on the host Docker node
- Place instructions to download example config file to use as config file, as recommended steps.
2022-01-06 14:10:28 +08:00
Kristoffer Dalby
42bed58329 Merge pull request #262 from kradalby/improve-docs
Rewrite main documentation
2022-01-04 16:26:27 +00:00
Kristoffer Dalby
d9f52efe70 Fix typo 2022-01-02 23:17:48 +00:00
Kristoffer Dalby
25b5eb8d7f Update tests to aline with new config example 2022-01-02 23:17:42 +00:00
Kristoffer Dalby
81c60939c9 Add vertical line for breathing 2022-01-02 19:55:38 +00:00
Kristoffer Dalby
4edc96d14d Make strongly strong 2022-01-02 19:54:37 +00:00
Kristoffer Dalby
6b7c74133d Use markdown numbering so github gets it 2022-01-02 19:53:49 +00:00
Kristoffer Dalby
8da029bd14 Add missing links 2022-01-02 19:48:57 +00:00
Kristoffer Dalby
1d01103b67 Link to example config from docs 2022-01-02 19:43:06 +00:00
Kristoffer Dalby
5df100539c Remove outdated configuration page in favour of config-example 2022-01-02 19:42:35 +00:00
Kristoffer Dalby
11c86acbe3 update case in readme 2022-01-02 19:39:51 +00:00
Kristoffer Dalby
86f36f9a43 Lowercase markdown docs 2022-01-02 19:39:03 +00:00
Kristoffer Dalby
271cb71754 Add more explaination and less redunancy with docs 2022-01-02 19:38:04 +00:00
Kristoffer Dalby
80d196cbfd Remove DNS file, it will be merged into example configuration 2022-01-02 19:37:48 +00:00
Kristoffer Dalby
3ce3ccb559 Reorder tls docs 2022-01-02 19:36:50 +00:00
Kristoffer Dalby
a11c6fd8b9 Fix formatting error in container doc 2022-01-02 20:08:18 +01:00
Kristoffer Dalby
a75c5a4cff Add eradme to examples 2022-01-02 20:08:04 +01:00
Kristoffer Dalby
8d504c35bf Move kubernetes to kustomize, since thats what it is 2022-01-02 20:06:32 +01:00
Kristoffer Dalby
8a07a63b1c Write disclaimer in kubernetes example 2022-01-02 20:06:21 +01:00
Kristoffer Dalby
74fd5de43d Move kubernetes example under docs 2022-01-02 20:04:35 +01:00
Kristoffer Dalby
f9e6722635 Rewrite main documentation
This commit starts restructuring the documentation and updating it to be
compliant with 0.12.x+ releases.

The main change is that the documentation has been rewritten for the
ground up, and hopefully simplified.

The documentation has been split into an official documentation for
running headscale as a binary under Linux with SystemD and a "community"
provided documentation for Docker.

This should make the two documents a lot easier to read and follow than
the mishmash document we had.
2022-01-02 19:11:36 +01:00
Juan Font
0bd4250a53 Merge pull request #261 from juanfont/kradalby-patch-2
Add note about outdated docs until we fix them
2021-12-29 10:53:15 +01:00
Kristoffer Dalby
4b44aa2180 Add note about outdated docs until we fix them 2021-12-28 10:16:20 +01:00
Kristoffer Dalby
f78984f2ef Merge pull request #258 from ohdearaugustin/fix-docker-release
Fix docker release
2021-12-27 22:09:16 +01:00
ohdearaugustin
3de311b7f4 Fix docker release 2021-12-27 21:24:57 +01:00
Juan Font
5192841016 Merge pull request #256 from juanfont/prepare-0.12.1-cl
Prepare CHANGELOG for 0.12.1
2021-12-24 23:40:33 +01:00
Juan Font
07384fd2bb Leave the TDB 2021-12-24 16:46:04 +01:00
Juan Font
a795e7c0c9 Minor correction on the purpose of Headscale 2021-12-24 16:40:18 +01:00
Juan Font
ebfbd4a37d Update changelog for 0.12.1 2021-12-24 16:39:22 +01:00
Juan Font
fb933b7d41 Merge pull request #255 from Wakeful-Cloud/main
Template Fixes
2021-12-24 16:12:33 +01:00
wakeful-cloud
1c7cb98042 Template Fixes 2021-12-22 19:43:53 -07:00
Kristoffer Dalby
fb634cdfc2 Merge pull request #242 from kradalby/changelog 2021-12-07 14:16:34 +00:00
Kristoffer Dalby
f60f62792a Merge branch 'main' into changelog 2021-12-07 13:21:17 +00:00
Kristoffer Dalby
418fde2731 Merge pull request #243 from dragetd/feature/github_templates 2021-12-07 13:20:54 +00:00
Kristoffer Dalby
53108207be Merge branch 'main' into feature/github_templates 2021-12-07 11:07:10 +00:00
Kristoffer Dalby
3fb3db6f20 Merge pull request #248 from negbie/main 2021-12-07 11:07:03 +00:00
Eugen Biegler
5a504fa711 Better error description
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-12-07 11:44:09 +01:00
Eugen Biegler
b4cce22415 Better error description
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-12-07 11:44:00 +01:00
Kristoffer Dalby
54c2306637 Merge branch 'main' into main 2021-12-07 10:39:08 +00:00
Juan Font
bc8f5f484d Merge branch 'main' into feature/github_templates 2021-12-07 11:21:50 +01:00
Juan Font
d00780b574 Merge pull request #250 from juanfont/use-1.17-for-release
Use go1.17 in goreleaser as required my go mod
2021-12-07 11:21:37 +01:00
Eugen
686384ebb7 Merge branch 'main' of https://github.com/negbie/headscale into main 2021-12-07 08:49:08 +01:00
Eugen
3a85c4d367 Better error description 2021-12-07 08:46:55 +01:00
Juan Font
1b007d2208 Use go1.17 in goreleaser as required my go mod 2021-12-06 23:19:08 +01:00
Kristoffer Dalby
5a7f669505 Update .github/ISSUE_TEMPLATE/config.yml 2021-12-05 09:37:11 +00:00
Michael Ko. Gajda
0c13d9da15 Fix format with prettier 2021-12-04 18:51:09 +01:00
Kristoffer Dalby
58ec26ee89 Merge branch 'main' into changelog 2021-12-04 12:16:01 +00:00
Kristoffer Dalby
969bcf17c4 Merge branch 'main' into feature/github_templates 2021-12-04 10:55:43 +00:00
Kristoffer Dalby
04d81a0e5c Merge branch 'main' into main 2021-12-02 08:31:48 +00:00
Eugen
a6e99525ac Add log_level to config, more ACL debug log 2021-12-01 20:02:00 +01:00
Eugen
7e95b3501d Ignoe derp.yaml, don't panic in Serve() 2021-12-01 19:32:47 +01:00
Kristoffer Dalby
ab52acba4b Merge pull request #247 from negbie/main 2021-12-01 16:18:46 +00:00
Eugen
07a437c707 Add private_key_path to example config 2021-12-01 14:34:08 +01:00
Kristoffer Dalby
d56fb1aaa1 Merge pull request #245 from Bpazy/patch-1 2021-12-01 11:53:25 +00:00
ZiYuan
c046ffbceb fix typo
Remove redundant quotation: `ephemeral_node_inactivity_timeout": "30m"` => `ephemeral_node_inactivity_timeout: "30m"`
2021-12-01 14:50:08 +08:00
Kristoffer Dalby
3435d95c80 Clarify and formatting 2021-11-30 09:17:21 +00:00
Kristoffer Dalby
acaab7a3de Add Open ID connect to changelog 2021-11-30 09:16:09 +00:00
Juan Font
74ba452025 Merge branch 'main' into feature/github_templates 2021-11-29 21:28:43 +01:00
Juan Font
500be2de58 Merge branch 'main' into changelog 2021-11-29 21:25:31 +01:00
Juan Font
5bc0398aaf Merge pull request #241 from juanfont/add-goreleaser-prerelease
Enable marking releases as prerelease
2021-11-29 21:24:01 +01:00
Michael Ko. Gajda
78eba97bf9 Add GitHub templates 2021-11-29 20:53:04 +01:00
Kristoffer Dalby
6350d528a7 Change changelog format 2021-11-29 19:45:31 +00:00
Kristoffer Dalby
42eb6b9e01 format 2021-11-29 17:34:41 +00:00
Kristoffer Dalby
2e2fb68715 Remove unreleased 2021-11-29 17:32:05 +00:00
Kristoffer Dalby
6fc6355d66 Add initial CHANGELOG 2021-11-29 17:31:19 +00:00
Michael Ko. Gajda
48fc93bbdc Add simple overview README for docs 2021-11-29 14:36:47 +01:00
Juan Font
3e941ef959 Enable marking releases as prerelease 2021-11-28 22:00:22 +00:00
Kristoffer Dalby
1dc008133c Merge pull request #229 from juanfont/kradalby-patch-2 2021-11-28 21:46:37 +00:00
Kristoffer Dalby
2d2ae62176 Merge branch 'kradalby-patch-2' of github.com:juanfont/headscale into kradalby-patch-2 2021-11-28 11:00:43 +00:00
Kristoffer Dalby
8e9a94613c Remove outdate integration test private key 2021-11-28 11:00:22 +00:00
Kristoffer Dalby
8932133ae7 Merge branch 'main' into kradalby-patch-2 2021-11-28 09:28:32 +00:00
Kristoffer Dalby
5b8587037d Remove non-existing field from oidc test 2021-11-28 09:25:27 +00:00
Kristoffer Dalby
e167be6d64 Remove generate private key step from docs 2021-11-28 09:18:24 +00:00
Kristoffer Dalby
34f4109fbd Add back privatekey, but automatically generate it if it does not exist 2021-11-28 09:17:18 +00:00
Kristoffer Dalby
fa813bc0d7 Merge pull request #239 from cure/debug-make-sure-machine-key-is-correct-length
The `create-node` subcommand under `debug` needs a 64 character key.
2021-11-28 08:38:54 +00:00
Kristoffer Dalby
32006f3a20 Use go 1.17 2021-11-28 08:26:36 +00:00
Kristoffer Dalby
ff8c961dbb Make sure comparison of nodekey is on the same format 2021-11-28 08:23:45 +00:00
Kristoffer Dalby
e9d5214d1c Disable tests which is broken due to split version 2021-11-27 21:04:19 +00:00
Kristoffer Dalby
6295b0bd84 Go mod tidy 2021-11-27 20:34:46 +00:00
Kristoffer Dalby
550f4016dc Merge branch 'kradalby-patch-2' of github.com:juanfont/headscale into kradalby-patch-2 2021-11-27 20:32:08 +00:00
Kristoffer Dalby
2ae882d801 Update go version 2021-11-27 20:31:33 +00:00
Kristoffer Dalby
ef81845deb Merge branch 'main' into kradalby-patch-2 2021-11-27 20:30:27 +00:00
Kristoffer Dalby
d96b681c83 Fix node cli integration test 2021-11-27 20:25:37 +00:00
Kristoffer Dalby
59aeaa8476 Ensure we always have the key prefix when needed 2021-11-27 20:25:12 +00:00
Ward Vandewege
cb2ea300ad Fix linter errors. 2021-11-27 13:59:39 -05:00
Kristoffer Dalby
c38f00fab8 Unmarshal keys in the non-deprecated way 2021-11-26 23:50:42 +00:00
Kristoffer Dalby
0012c76170 Make it easier to run cli integration tests 2021-11-26 23:34:11 +00:00
Kristoffer Dalby
cfd53bc4aa Factor wgkey to types/key
This commit converts all the uses of wgkey to the new key interfaces.

It now has specific  machine, node and discovery keys and we now should
use them correctly.

Please note the new logic which strips a key prefix (in utils.go) that
is now standard inside tailscale.

In theory we could put it in the database, but to preserve backwards
compatibility and not spend a lot of resources on accounting for both,
we just strip them.
2021-11-26 23:30:42 +00:00
Kristoffer Dalby
07418140a2 Remove config loading of private key path 2021-11-26 23:29:41 +00:00
Kristoffer Dalby
c63c259d31 Switch wgkey for types/key
We dont seem to need the wireguard key anymore, we generate a key on
startup based on the new library and the users fetch it from /key.

Clean up app.go and update docs
2021-11-26 23:28:06 +00:00
Kristoffer Dalby
50b47adaa3 Upgrade tailscale to 1.18 2021-11-26 23:27:09 +00:00
Ward Vandewege
b6ae60cc44 The create-node subcommand under debug needs a 64 character key. 2021-11-26 14:49:51 -05:00
Ward Vandewege
d944aa6e79 Merge pull request #237 from cure/preauthkeys-fix-default-expiration
Fix default preauthkey expiration
2021-11-26 11:09:43 -05:00
Kristoffer Dalby
06f05d6cc2 Merge branch 'main' into preauthkeys-fix-default-expiration 2021-11-26 15:46:00 +00:00
Kristoffer Dalby
0819c6515a Merge pull request #238 from juanfont/kradalby-patch-3 2021-11-26 15:45:38 +00:00
Ward Vandewege
c7f3e0632b When creating a preauthkey, the default expiration was passed through as
a nil value, instead of the default value (1h). This resulted in the
preauthkey being created with expiration key '0001-01-01 00:00:00',
which meant the key would not work, because it was already expired.

This commit applies the default expiration time (1h) when a preauthkey
is created without a specific expiration. It also updates an integration
test to make sure this bug does not reoccur.
2021-11-26 10:04:26 -05:00
Kristoffer Dalby
58fd6c4ba5 Revert postgres constant value
changes "postgresql" to "postgres"
2021-11-26 07:13:00 +00:00
Kristoffer Dalby
aab4a6043a Merge branch 'main' into kradalby-patch-2 2021-11-25 08:38:59 +00:00
Kristoffer Dalby
a52a4d45c0 Merge pull request #236 from restanrm/fix-derp-example-config
fix(derp-example): change regionid in node
2021-11-25 08:37:45 +00:00
Juan Font
45bc3f7a09 Merge branch 'main' into fix-derp-example-config 2021-11-24 18:57:31 +01:00
Kristoffer Dalby
5620858549 Merge pull request #227 from kradalby/expired-issue 2021-11-24 17:49:33 +00:00
Adrien Raffin-Caboisse
f2e273b8a2 fix(derp-example): change regionid in nodes
Using a wrong regionid value lead to non working DERP custom server. No checks are performed for this kind of errors making it difficult to find.
2021-11-24 15:54:22 +01:00
Kristoffer Dalby
cec1e86b58 Add missing request arguemnt 2021-11-24 12:16:56 +00:00
Kristoffer Dalby
dcbf289470 Rename idKey to machineKey to keep consistency 2021-11-24 12:15:55 +00:00
Kristoffer Dalby
fdd64d98c8 Add missing iff to handle expired preauthkey machines 2021-11-24 12:15:32 +00:00
Kristoffer Dalby
9968992be0 Fix prettier 2021-11-24 10:47:20 +00:00
Kristoffer Dalby
f50f9ac894 Merge branch 'expired-issue' of github.com:kradalby/headscale into expired-issue 2021-11-24 10:13:49 +00:00
Kristoffer Dalby
2eca344f0e Fix gocritic 2021-11-24 10:13:41 +00:00
Kristoffer Dalby
349264830b Use .1 2021-11-23 11:27:44 +00:00
Kristoffer Dalby
0b5c29022b Merge branch 'main' into expired-issue 2021-11-22 20:13:33 +00:00
Kristoffer Dalby
1f1c45a2c0 Fix cli_test 2021-11-22 19:59:44 +00:00
Kristoffer Dalby
68dc2a70db Update neighbours if node is expired or refreshed
In addition, only pass the map of registered and not expired nodes to
clients.
2021-11-22 19:51:16 +00:00
Kristoffer Dalby
caf1b1cabc Fix typo 2021-11-22 19:35:24 +00:00
Kristoffer Dalby
021c464148 Add cache for requested expiry times
This commit adds a sentral cache to keep track of clients whom has
requested an expiry time, but were we need to keep hold of it until the
second request comes in.
2021-11-22 19:32:52 +00:00
Kristoffer Dalby
e600ead3e9 Make sure nodes can reauthenticate
This commit fixes an issue where nodes were not able to reauthenticate.
2021-11-22 19:32:11 +00:00
Kristoffer Dalby
200c10e48c Add missing return in oidc.go 2021-11-22 17:22:47 +00:00
Kristoffer Dalby
e8faff4fe2 Use uint64 straight instead of converting 2021-11-22 17:22:22 +00:00
Kristoffer Dalby
5cbd4513a4 Simplify register function if 2021-11-22 17:21:56 +00:00
Kristoffer Dalby
a477c808c7 Merge pull request #230 from lion24/patch-1 2021-11-22 09:47:37 +00:00
Kristoffer Dalby
74044f62f4 Remove anouther potential error leak 2021-11-21 21:54:19 +00:00
Kristoffer Dalby
fcd4d94927 Clean up logging and error handling in oidc
We should never expose errors via web, it gives attackers a lot of info
(Insert OWASP guide).

Also handle error that didnt separate not found gorm issue and other
errors.
2021-11-21 21:51:39 +00:00
Kristoffer Dalby
fac33e46e1 Add long description for expire 2021-11-21 21:35:36 +00:00
Kristoffer Dalby
b152e53b13 Use correct type for nodes command 2021-11-21 21:34:03 +00:00
Kristoffer Dalby
1687e3b03f Removed unused parameter 2021-11-21 21:29:27 +00:00
Kristoffer Dalby
c2393685f1 Remove expiry update in expiry, we dont want to extend it just because they _try_ to connect 2021-11-21 21:14:40 +00:00
Kristoffer Dalby
fd5f42c2e6 Move handle expired machine to the end of registration 2021-11-21 21:14:13 +00:00
Kristoffer Dalby
bda2d9c3b0 Remove unused param 2021-11-21 14:00:48 +00:00
Kristoffer Dalby
c4ecc4db91 Simplify control flow in RegistrationHandler
This commits tries to dismantle the complicated "if and or" in the
RegistrationHandler by factoring out the "is Registrated" into a root
if.

This, together with some new comments, should hopefully make it a bit
easier to follow what is happening in all the different cases that needs
to be handled when a Node contacts the registration endpoint.
2021-11-21 13:59:24 +00:00
Kristoffer Dalby
8ccc51ae57 Remove special case for authkey
We no longer have weird expire behaviour, so we dont need this case
2021-11-21 13:45:19 +00:00
Kristoffer Dalby
a2b9f3bede Add expire (logout) machine command 2021-11-21 13:40:44 +00:00
Kristoffer Dalby
bd1d1b1a3b Implement ExpireMachine rpc 2021-11-21 13:40:19 +00:00
Kristoffer Dalby
f1c05f8010 Add ExpireMachine spec to rpc 2021-11-21 13:40:04 +00:00
Kristoffer Dalby
f85a77edb5 Remove println statement 2021-11-21 09:48:59 +00:00
Kristoffer Dalby
1c7aff5dd9 Add expired column to machine list command 2021-11-21 09:44:38 +00:00
lion24
e91f72fe4c Running.md: fix missing backslash (\)
* This would cause otherwise the command to abort after the first statement of the docker command ;)
2021-11-20 23:31:49 +01:00
Kristoffer Dalby
5a2cae5081 Add new Tailscale version to integration tests 2021-11-19 09:16:11 +00:00
Kristoffer Dalby
6a9dd2029e Remove expiry logic, this needs to be redone 2021-11-19 09:02:49 +00:00
Kristoffer Dalby
9aac1fb255 Remove expiry logic, this needs to be redone 2021-11-19 09:02:29 +00:00
Kristoffer Dalby
106b1e7e8d Create constants for other reg methods 2021-11-18 17:51:54 +00:00
Kristoffer Dalby
836986aa59 Merge pull request #225 from fdelucchijr/patch-1 2021-11-18 10:13:07 +00:00
Kristoffer Dalby
58d1255357 Remove unneeded returns 2021-11-18 08:51:33 +00:00
Kristoffer Dalby
981f712660 Remove unused param 2021-11-18 08:51:21 +00:00
Kristoffer Dalby
50dcb8bb75 Use valid handler for registered authkey machines 2021-11-18 08:50:53 +00:00
Kristoffer Dalby
a8a8f01429 Make "authKey" a constant 2021-11-18 08:49:55 +00:00
Kristoffer Dalby
35c3fe9608 Move registration workflow into functions 2021-11-17 22:39:41 +00:00
Fernando De Lucchi
f74b9f5fe2 Styling and prettier 2021-11-17 16:25:37 -05:00
Fernando De Lucchi
de42fe3b04 Merge branch 'main' into patch-1 2021-11-16 19:10:42 -05:00
Kristoffer Dalby
49f835d8cf Merge pull request #214 from ItalyPaleAle/docker-distroless 2021-11-16 07:34:08 +00:00
ItalyPaleAle
7bc2f41b33 Run prettier 💄 2021-11-15 15:37:40 -08:00
Alessandro (Ale) Segala
a10388b709 Merge branch 'main' into docker-distroless 2021-11-15 15:19:06 -08:00
Kristoffer Dalby
bd7b5e97cb Merge branch 'main' into patch-1 2021-11-15 23:00:45 +00:00
Kristoffer Dalby
4b525a3967 Merge pull request #223 from kradalby/golanglint 2021-11-15 22:36:02 +00:00
Kristoffer Dalby
d6739386a0 Get rid of dynamic errors 2021-11-15 19:18:14 +00:00
Kristoffer Dalby
25b790d025 Add and fix forcetypeassert 2021-11-15 18:42:44 +00:00
Kristoffer Dalby
db8be91d8b Add and fix forbidigo 2021-11-15 18:36:02 +00:00
Kristoffer Dalby
c4d4c9c4e4 Add and fix gosec 2021-11-15 18:31:52 +00:00
Kristoffer Dalby
715542ac1c Add and fix stylecheck (golint replacement) 2021-11-15 17:24:24 +00:00
Kristoffer Dalby
0c005a6b01 Add and fix errname 2021-11-15 16:33:16 +00:00
Kristoffer Dalby
0c45f8d252 Add and fix errorlint 2021-11-15 16:26:41 +00:00
Kristoffer Dalby
2dde1242cf Fix formatting 2021-11-15 16:16:35 +00:00
Kristoffer Dalby
78cfba0a31 Add exceptions to var name length 2021-11-15 16:16:16 +00:00
Kristoffer Dalby
8ae682b412 Fix var name length in tests 2021-11-15 16:16:04 +00:00
Kristoffer Dalby
333be80f9c Fix rest of var name in main code 2021-11-15 16:15:50 +00:00
Alessandro (Ale) Segala
2efefca737 Merge pull request #1 from ohdearaugustin/docker-workflows
Docker workflows
2021-11-14 13:33:13 -08:00
ohdearaugustin
c6bc9fffe9 workflows/release: fix docker debug tags 2021-11-14 22:24:27 +01:00
ohdearaugustin
8454c1b52c workflows/release: add docker debug 2021-11-14 21:15:33 +01:00
Kristoffer Dalby
471c0b4993 Initial work eliminating one/two letter variables 2021-11-14 20:32:03 +01:00
Kristoffer Dalby
53ed749f45 Start work on making gocritic pass 2021-11-14 18:44:37 +01:00
Kristoffer Dalby
ba084b9987 Lint fix integration tests 2021-11-14 18:35:49 +01:00
Kristoffer Dalby
85f28a3f4a Remove all instances of undefined numbers (gonmd) 2021-11-14 18:31:51 +01:00
Kristoffer Dalby
796072a5a4 Add and fix ifshort 2021-11-14 18:09:22 +01:00
Kristoffer Dalby
9390348a65 Add and fix goconst 2021-11-14 18:06:25 +01:00
Kristoffer Dalby
c9c16c7fb8 Remove unused params or returns 2021-11-14 18:03:21 +01:00
Kristoffer Dalby
19cd7a4eac Add and fix exhaustive 2021-11-14 17:52:55 +01:00
Kristoffer Dalby
0315f55fcd Add and fix nilnil 2021-11-14 17:51:34 +01:00
Kristoffer Dalby
668e958d3e Add and fix unconvert 2021-11-14 17:49:54 +01:00
Kristoffer Dalby
4ace54c4e1 Move wsl, might not be feasible 2021-11-14 16:49:54 +01:00
Kristoffer Dalby
89eb13c6cb Add and fix nlreturn (new line return) 2021-11-14 16:46:09 +01:00
Kristoffer Dalby
d0ef850035 Add and fix noctx linter 2021-11-14 16:37:43 +01:00
Kristoffer Dalby
2f8e9f272c Merge branch 'main' into docker-distroless 2021-11-14 14:35:44 +01:00
Fernando De Lucchi
1af4a3b958 Merge branch 'main' into patch-1 2021-11-14 04:16:00 -05:00
Kristoffer Dalby
1969802c6b Fix golanglint 2021-11-14 08:32:58 +00:00
Kristoffer Dalby
052883aa55 Fix merge conflict 2021-11-14 08:30:48 +00:00
Kristoffer Dalby
d2918edc14 Merge pull request #224 from cure/namespace-deletion-fixes
Improvements for namespace deletion
2021-11-14 09:27:58 +01:00
Kristoffer Dalby
f3da299457 Format readme 2021-11-14 08:09:33 +00:00
Kristoffer Dalby
e8726b1e22 Add readme note about codestyle 2021-11-14 08:08:03 +00:00
Fernando De Lucchi
b897a26f42 arm64 docker image build in release process 2021-11-13 21:08:59 -05:00
Alessandro (Ale) Segala
5ec7158b5d Merge branch 'main' into docker-distroless 2021-11-13 14:16:53 -08:00
Alessandro (Ale) Segala
7d77acd88e Docs for debug container 2021-11-13 22:16:37 +00:00
Alessandro (Ale) Segala
c0f16603c5 Copy headscale binary in /bin in the container
This way, we don't need to alter the PATH
2021-11-13 22:10:58 +00:00
Ward Vandewege
34dba0ade8 Fix missing error check. 2021-11-13 15:24:32 -05:00
Ward Vandewege
acf7e462ad Improvements for namespace deletion: add a confirmation prompt, and make
sure to also delete any associated preauthkeys.
2021-11-13 14:01:05 -05:00
Kristoffer Dalby
f94b0b54d8 Remove lint install, update go 2021-11-13 09:39:20 +00:00
Kristoffer Dalby
806f0d3e6c Format lint 2021-11-13 09:20:59 +00:00
Kristoffer Dalby
b653572272 Make format shuld format, not lint 2021-11-13 09:20:51 +00:00
Kristoffer Dalby
fa0922d5bb define proto dir for buf 2021-11-13 09:18:00 +00:00
Kristoffer Dalby
95b9f03fb3 update buf setup 2021-11-13 09:13:17 +00:00
Kristoffer Dalby
24e0c944b1 Align with update golangci-lint 2021-11-13 09:11:03 +00:00
Kristoffer Dalby
148437f716 Setup more linters and goals for golangci 2021-11-13 08:53:34 +00:00
Kristoffer Dalby
3ddd9962ce Add format make entry 2021-11-13 08:39:20 +00:00
Kristoffer Dalby
2634215f12 golangci-lint --fix 2021-11-13 08:39:04 +00:00
Kristoffer Dalby
dae34ca8c5 Proto format 2021-11-13 08:36:56 +00:00
Kristoffer Dalby
03b7ec62ca Go format with shorter lines 2021-11-13 08:36:45 +00:00
Kristoffer Dalby
edfcdc466c Update lint ci file with prettier and proto 2021-11-13 08:13:38 +00:00
Kristoffer Dalby
6b3114ad6f Run prettier 2021-11-13 08:11:55 +00:00
Kristoffer Dalby
ba65092926 Merge pull request #212 from kradalby/cli-grpc
Rework the CLI to use gRPC
2021-11-12 14:39:39 +00:00
Alessandro (Ale) Segala
f44138c944 Added debug container 2021-11-12 02:20:46 +00:00
Alessandro (Ale) Segala
c290ce4b91 Revert "Fixed integration tests"
This reverts commit 67953bfe2f.
2021-11-09 16:24:10 +00:00
Alessandro (Ale) Segala
3b34c7b89a Removed / from docker commands in docs
Essentially reverts 6076656373
2021-11-09 16:23:36 +00:00
Alessandro (Ale) Segala
83e72ec57d Allow running headscale without leading / 2021-11-09 16:20:58 +00:00
Kristoffer Dalby
49893305b4 Only turn on response log in grpc in trace mode 2021-11-08 22:06:25 +00:00
Kristoffer Dalby
0803c407a9 Fix Reusable typo, add tests for Augustines scenario 2021-11-08 20:49:03 +00:00
Kristoffer Dalby
6371135459 Try to address issue raised by cure 2021-11-08 20:48:20 +00:00
Kristoffer Dalby
43af11c46a Fix typo in generated code 2021-11-08 20:47:40 +00:00
Kristoffer Dalby
b210858dc5 Remove unused dep 2021-11-08 18:28:06 +00:00
Kristoffer Dalby
e1f45f9d07 Remove unused dep 2021-11-08 18:27:57 +00:00
Kristoffer Dalby
dce6b8d72e Add test case and fix nil pointer in preauthkeys command without expiration 2021-11-08 08:02:01 +00:00
Alessandro (Ale) Segala
67953bfe2f Fixed integration tests 2021-11-07 19:09:51 +00:00
Alessandro (Ale) Segala
6076656373 Updated docs 2021-11-07 18:57:37 +00:00
Kristoffer Dalby
9a26fa7989 Ensure logging is off for integration test commands 2021-11-07 10:40:05 +00:00
Kristoffer Dalby
d47b83f80b Unwrap grpc errors to make nicer user facing errors 2021-11-07 10:15:32 +00:00
Kristoffer Dalby
b11acad1c9 Fix typo 2021-11-07 09:57:39 +00:00
Kristoffer Dalby
b15efb5201 Ensure unix socket is removed before we startup 2021-11-07 09:55:32 +00:00
Kristoffer Dalby
2dfd42f80c Attempt to dry up CLI client, add proepr config
This commit is trying to DRY up the initiation of the gRPC client in
each command:

It renames the function to CLI instead of GRPC as it actually set up a
CLI client, not a generic grpc client

It also moves the configuration of address, timeout (which is now
consistent) and api to use Viper, allowing users to set it via env vars
and configuration file
2021-11-07 09:41:14 +00:00
Kristoffer Dalby
ce3f79a3bf Add yaml to output help 2021-11-07 08:58:45 +00:00
Kristoffer Dalby
a249d3fe39 Fix color for current namespace in nodes command 2021-11-07 08:58:03 +00:00
Alessandro (Ale) Segala
a6d487de00 Using debian11-based distroless image 2021-11-06 23:19:56 +00:00
Alessandro (Ale) Segala
3720da6386 Using distroless base image for Docker 2021-11-06 23:18:13 +00:00
Kristoffer Dalby
26718e8308 Revert gorm upgrade 2021-11-06 20:23:04 +00:00
Kristoffer Dalby
f5a196088a Merge branch 'main' into cli-grpc 2021-11-06 20:12:19 +00:00
Kristoffer Dalby
74f0d08f50 Merge pull request #199 from rcursaru/patch-1
update Running.md
2021-11-06 20:05:27 +00:00
Kristoffer Dalby
046681f4ef Merge branch 'main' into patch-1 2021-11-06 19:46:06 +00:00
Kristoffer Dalby
29531a5e90 Merge branch 'main' into cli-grpc 2021-11-06 19:29:00 +00:00
Ward Vandewege
137a9d6333 Merge pull request #213 from aberoham/patch-1
Typo in golang URL
2021-11-06 14:23:17 -04:00
Abraham Ingersoll
8115f50d03 Typo in golang URL 2021-11-06 07:43:41 +00:00
Kristoffer Dalby
b75e8ae2bd Merge branch 'main' into patch-1 2021-11-05 18:27:55 +00:00
Kristoffer Dalby
3ad2350c79 Fix new version of hujson 2021-11-05 07:24:00 +00:00
Kristoffer Dalby
204f99dd51 Add CLI integration tests
This PR adds a new part to the integration test suite which spins up a
new headscale and runs through a scenario of test cases for each
command.

The intent is to check that all commands work as intended and produce
the expected output.

I think they have been pretty well covered, but would appreciate
additional test cases if I have missed some.

Please note: headscale is set up, and teared down for _each_ "test
function" in this file, this means that its more suitable for specific
cases.
2021-11-04 22:45:15 +00:00
Kristoffer Dalby
8df41b069f Formatting 2021-11-04 22:45:08 +00:00
Kristoffer Dalby
be4256b1d0 Convert routes command to use gRPC 2021-11-04 22:44:59 +00:00
Kristoffer Dalby
77a973878c Convert preauthkeys command to use gRPC 2021-11-04 22:44:49 +00:00
Kristoffer Dalby
7b0d2dfb4a Convert nodes command to use gRPC 2021-11-04 22:44:35 +00:00
Kristoffer Dalby
79871d2463 Make namespace command use gRPC
This commit is a first in a series of commits migrating the command
interfaces to use the new gRPC client.

As a part of this commit, they have been streamlined and each command
_should_ be a bit more similar and use consistent output.

By using the new output function, we now make sure its always json
(errors and everything) if the user asks for JSON.
2021-11-04 22:42:21 +00:00
Kristoffer Dalby
dce82f4323 Use new json wrapper for version command 2021-11-04 22:41:55 +00:00
Kristoffer Dalby
9e9049307e Simplify loglevel parser, turn off logs when machine output is set 2021-11-04 22:32:13 +00:00
Kristoffer Dalby
cd34a5d6f3 Expand json output to support yaml, make more generic 2021-11-04 22:31:47 +00:00
Kristoffer Dalby
319237910b Resolve new dependencies 2021-11-04 22:28:35 +00:00
Kristoffer Dalby
3eed356d70 Implement rpc calls with new helper functions, implementing the proto spec 2021-11-04 22:19:27 +00:00
Kristoffer Dalby
706ff59d70 Clean pointer list in app.go, add grpc logging and simplify naming 2021-11-04 22:18:55 +00:00
Kristoffer Dalby
c2eb3f4d36 Use long command in example and remove pointerlist 2021-11-04 22:18:06 +00:00
Kristoffer Dalby
9acc3e0e73 Add a set of ip prefix convert helpers 2021-11-04 22:17:44 +00:00
Kristoffer Dalby
94dbaa6822 Clean up the return of "pointer list"
This commit is getting rid of a bunch of returned list pointers.
2021-11-04 22:16:56 +00:00
Kristoffer Dalby
5526ccc696 Namespaces are no longer a pointer 2021-11-04 22:15:46 +00:00
Kristoffer Dalby
95690e614e Simplify and streamline namespace functions for new cli/rpc/api 2021-11-04 22:15:17 +00:00
Kristoffer Dalby
77f5f8bd1c Simplify and streamline preauth commands for new cli/rpc/api 2021-11-04 22:14:39 +00:00
Kristoffer Dalby
787814ea89 Consolidate machine related lookups
This commit moves the routes lookup functions to be subcommands of
Machine, making them a lot simpler and more specific/composable.

It also moves the register command from cli.go into machine, so we can
clear out the extra file.

Finally a toProto function has been added to convert between the machine
database model and the proto/rpc model.
2021-11-04 22:11:38 +00:00
Kristoffer Dalby
67adea5cab Move common integration test commands into common file 2021-11-04 22:10:57 +00:00
Kristoffer Dalby
4226da3d6b Add "debug" command
This commit adds a debug command tree, intended to host commands used
for debugging and testing.

It adds a create node/machine command which will be used later to create
machines that can be used to test the registration command.
2021-11-04 22:08:45 +00:00
Kristoffer Dalby
5270361989 Add generated files from protobuf 2021-11-04 22:07:59 +00:00
Kristoffer Dalby
a6aa6a4f7b Add proto rpc interface for cli
This commit adds proto rpc definitions for the communication needed for
the CLI interface.
This will allow us to move the rest of the CLI interface over to gRPC
and in the future allow remote access
2021-11-04 22:02:10 +00:00
Kristoffer Dalby
1c530be66c Merge pull request #206 from kradalby/initial-api-cli-work 2021-11-04 14:09:06 +00:00
Kristoffer Dalby
7c774bc547 Remove flag that cant be trapped 2021-11-02 21:49:19 +00:00
Kristoffer Dalby
9954a3c599 Add handling for closing the socket 2021-11-02 21:46:15 +00:00
Kristoffer Dalby
b91c115ade Remove "auth skip" for socket traffic 2021-10-31 19:57:42 +00:00
Kristoffer Dalby
53df9afc2a Fix step naming error 2021-10-31 19:54:38 +00:00
Kristoffer Dalby
8db45a4e75 Setup a seperate, non-tls, no auth, socket grpc 2021-10-31 19:52:34 +00:00
Kristoffer Dalby
1c9b1ea91a Add todo 2021-10-31 16:34:20 +00:00
Kristoffer Dalby
12f2a7cee0 Move context per cure's suggestion 2021-10-31 16:26:51 +00:00
Kristoffer Dalby
3f30bf1e33 Ensure we set up TLS for http 2021-10-31 16:19:38 +00:00
Kristoffer Dalby
f968b0abdf Merge branch 'main' into initial-api-cli-work 2021-10-31 12:17:47 +00:00
Kristoffer Dalby
16ccbf4cdb Merge pull request #207 from juanfont/update-contributors 2021-10-31 12:17:31 +00:00
Kristoffer Dalby
d803fe6123 Merge branch 'main' into update-contributors 2021-10-31 09:58:17 +00:00
Kristoffer Dalby
ca15a53fad Add timeout to integration test for execCommand to fail faster 2021-10-31 09:58:01 +00:00
Kristoffer Dalby
264e5964f6 Resolve merge conflict 2021-10-31 09:40:43 +00:00
github-actions[bot]
223c611820 docs(README): update contributors 2021-10-31 09:34:07 +00:00
Kristoffer Dalby
fbdfa55629 Merge pull request #126 from unreality/main
Initial work on OIDC (SSO) integration
2021-10-31 09:33:35 +00:00
unreality
73d22cdf54 Merge pull request #2 from kradalby/oidc-1
Fix conflict, prepare for merge
2021-10-31 07:04:04 +08:00
Kristoffer Dalby
bac81176b2 Remove lint from generated testcode 2021-10-30 15:39:05 +00:00
Kristoffer Dalby
cd2914dbc9 Make note about oidc being experimental 2021-10-30 15:35:58 +00:00
Kristoffer Dalby
cbf3f5d640 Resolve merge conflict 2021-10-30 15:33:01 +00:00
Kristoffer Dalby
018e42acad Merge branch 'main' into initial-api-cli-work 2021-10-30 15:31:34 +01:00
Kristoffer Dalby
482a31b66b Setup swagger and swagger UI properly 2021-10-30 14:29:53 +00:00
Kristoffer Dalby
2b340e8fa4 Rename protofile 2021-10-30 14:29:41 +00:00
Kristoffer Dalby
434fac52b7 Fix lint error 2021-10-30 14:29:03 +00:00
Kristoffer Dalby
6aacada852 Switch from gRPC localhost to socket
This commit changes the way CLI and grpc-gateway communicates with the
gRPC backend to socket, instead of localhost. Unauthenticated access now
goes on the socket, while the network interface will require API key (in
the future).
2021-10-30 14:08:16 +00:00
Ward Vandewege
7301d7eb67 Merge pull request #200 from cure/cli-improvements
Cli improvements -- nodes subcommand
2021-10-29 17:36:54 -04:00
Ward Vandewege
b2d2d5653e Merge branch 'main' into cli-improvements 2021-10-29 17:20:05 -04:00
Kristoffer Dalby
72fd2a2780 Fix lint error 2021-10-29 17:36:11 +00:00
Kristoffer Dalby
9ef031f0f8 Port create, delete and list of namespace to grpc 2021-10-29 17:16:54 +00:00
Kristoffer Dalby
81b8610dff Add helper function to setup grpc client for cli 2021-10-29 17:15:52 +00:00
Kristoffer Dalby
eefd82a574 Move config loading out of the headscale app setup 2021-10-29 17:09:06 +00:00
Kristoffer Dalby
002b5c1dad Add grpc token auth struct 2021-10-29 17:08:21 +00:00
Kristoffer Dalby
68dab0fe7b Move localhost check to utils 2021-10-29 17:04:58 +00:00
Kristoffer Dalby
6d10be8fff Change order of print/nil check in integration test 2021-10-29 16:49:44 +00:00
Kristoffer Dalby
a23d82e33a Setup API and prepare for API keys
This commit sets up the API and gRPC endpoints and adds authentication
to them. Currently there is no actual authentication implemented but it
has been prepared for API keys.

In addition, there is a allow put in place for gRPC traffic over
localhost. This has two purposes:

1. grpc-gateway, which is the base of the API, connects to the gRPC
   service over localhost.
2. We do not want to break current "on server" behaviour which allows
   users to use the cli on the server without any fuzz
2021-10-29 16:45:06 +00:00
Kristoffer Dalby
c7fa9b6e4a Setup create, delete and list namespace over grpc 2021-10-29 16:44:32 +00:00
Kristoffer Dalby
07bbeafa3b Fix lint errors, add initial namespace rpc 2021-10-29 16:43:10 +00:00
Kristoffer Dalby
06700c1dc4 Setup proto linting 2021-10-29 16:42:56 +00:00
Raal Goff
2d252da221 suggested documentation and comments 2021-10-29 21:35:07 +08:00
Kristoffer Dalby
2c071a8a2d Merge pull request #204 from kradalby/api-playground 2021-10-28 20:17:20 +01:00
Ward Vandewege
f9187bdfc4 Switch to named arguments for all nodes subcommands. Update docs
accordingly. Fix integration test failure.
2021-10-28 09:31:15 -04:00
Ward Vandewege
25c67cf2aa Update integration_test.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-28 08:40:30 -04:00
Ward Vandewege
b00a2729e3 Update cmd/headscale/cli/nodes.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-28 08:39:42 -04:00
Ward Vandewege
6c01b86e4c Update cmd/headscale/cli/nodes.go
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-28 08:39:27 -04:00
Ward Vandewege
d086cf4691 Move the namespace argument back to a flag for the share and unshare
commands.
2021-10-27 17:51:42 -04:00
Kristoffer Dalby
5054ed41ac Make ci lint fix if it can 2021-10-27 07:10:32 +00:00
Kristoffer Dalby
e91174e83f Add gen explicitly to skip list 2021-10-27 07:08:24 +00:00
Kristoffer Dalby
c9bd25d05c Remove golint from github actions 2021-10-27 07:07:44 +00:00
Kristoffer Dalby
f779372154 Add golangcilint config 2021-10-27 07:07:19 +00:00
Kristoffer Dalby
acd9ebbdf8 Let lint ignore grpcv1.go as it is placeholder 2021-10-27 07:06:39 +00:00
Kristoffer Dalby
6369cea10e Remove golint, its deprecated
This commit removed `golint`, its deprecated:
https://github.com/golang/lint

and golangci-lint has overlapping features.
2021-10-27 06:58:16 +00:00
Kristoffer Dalby
2d92719095 Dont try to generate code on every make build 2021-10-27 06:48:30 +00:00
Kristoffer Dalby
d4265779ef Check in generated code
This does not have to be reviewed, here is some reasoning:

Go (and go mod) is designed for having code available and we need to
check in the generated code to make sure it is "go gettable". If we dont
we give ourselves a headache trying to setup all the ci, tests etc to
install and generate the code before it runs.

Because the code isnt there, the plugins needed to generate the code
fail to install...

I didnt find any good documentation for this, but there is this github
comment:
https://github.com/golang/go/issues/34514#issuecomment-535406759
2021-10-27 06:44:04 +00:00
Kristoffer Dalby
8f2ef6a57d Prepare for checking in generated code 2021-10-27 06:40:39 +00:00
Kristoffer Dalby
6e764942a2 Add grpc step to dockerfile 2021-10-26 21:35:18 +00:00
Kristoffer Dalby
11d987549f Ignore generated files for docker 2021-10-26 21:34:51 +00:00
Kristoffer Dalby
b8c89cd63c Add readme and makefile entry about code generation 2021-10-26 20:53:10 +00:00
Kristoffer Dalby
2f045b20fb Refactor tls and wire up grpc, grpc gateway/api
This commit moves the TLS configuration into a seperate function.

It also wires up the gRPC interface and prepares handing the API
endpoints to the grpc gateway.
2021-10-26 20:42:56 +00:00
Kristoffer Dalby
caa4d33cbd Add an initial grpcv1 service (implementing the proto generated service) 2021-10-26 20:42:20 +00:00
Kristoffer Dalby
a9da7c8fd9 Update go.mod 2021-10-26 20:41:35 +00:00
Kristoffer Dalby
b096a2e7e5 Create an initial gRPC service
This commit adds protobuf files and tooling surrounding generating APIs
and datatypes.
2021-10-26 20:37:37 +00:00
Ward Vandewege
f9ece0087d Make the cli help a little more explicit for the nodes subcommand. 2021-10-26 08:50:25 -04:00
Kristoffer Dalby
c76d3b53d9 Merge branch 'main' into cli-improvements 2021-10-25 22:11:02 +01:00
Kristoffer Dalby
e8277595f5 Merge pull request #202 from juanfont/kradalby-patch-1
Add note about main containing unreleased  changes
2021-10-25 20:14:36 +01:00
Kristoffer Dalby
4d3b638a3d Add note about main containing unreleased changes
#201
2021-10-25 19:38:11 +01:00
Ward Vandewege
1d9954d8e9 Fix integration test. 2021-10-24 20:11:47 -04:00
Ward Vandewege
dd7557850e cli changes for the nodes subcommand:
* when listing nodes, a namespace is now optional, when it is not
  provided, all nodes are shown
* when deleting, and sharing a node, remove the `namespace` flag, it was
  superfluous and unused
* when unsharing a node, specify the namespace as an argument not a
  flag, making the UX the same as for sharing.

Also refactor the share/unshare code to reuse the shared bits.
2021-10-24 17:50:28 -04:00
Ward Vandewege
c8e1afb14b When attempting to unshare a node from the primary namespace, return
errorMachineNotShared, not errorSameNamespace. Add test for same.
2021-10-24 17:50:21 -04:00
Kristoffer Dalby
6d162eeff9 Merge pull request #197 from kradalby/config-simplification 2021-10-24 22:27:18 +01:00
Kristoffer Dalby
746d4037da Fix config and tests 2021-10-24 21:30:51 +01:00
Kristoffer Dalby
1237e02f7c Merge branch 'config-simplification' of github.com:kradalby/headscale into config-simplification 2021-10-24 21:21:08 +01:00
Kristoffer Dalby
7da3d4ba50 Resolve merge conflict 2021-10-24 21:21:01 +01:00
Remus CURSARU
c22b93734e update Running.md
clarify setup steps; use a single directory to hold all configuration for docker container
2021-10-24 14:09:07 +02:00
Kristoffer Dalby
8853315dcc Update config-example.yaml
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2021-10-23 10:40:15 +01:00
Kristoffer Dalby
5aaffaaecb Merge pull request #196 from kradalby/derp-improvements
Add ability to fetch DERP from url and file
2021-10-23 09:20:27 +01:00
Kristoffer Dalby
389a8d47a3 Merge branch 'main' into derp-improvements 2021-10-22 23:58:48 +01:00
Kristoffer Dalby
a355769416 Update derp-example.yaml
Co-authored-by: Juan Font <juanfontalonso@gmail.com>
2021-10-22 23:58:27 +01:00
Juan Font
1a8c9216d6 Merge pull request #194 from juanfont/update-contributors
docs(README): update contributors
2021-10-23 00:11:22 +02:00
Juan Font
81316ef644 Merge branch 'main' into update-contributors 2021-10-22 21:28:27 +02:00
Kristoffer Dalby
4d4d0de356 Start adding comments to config 2021-10-22 18:27:11 +01:00
Kristoffer Dalby
b85adbc40a Remove the need for multiple config files
This commit removes the almost a 100% redundant tests (two fields were
checked differently) and makes a single example configuration for users.
2021-10-22 18:14:29 +01:00
Kristoffer Dalby
aefbd66317 Remove derpmap volume from integration tests 2021-10-22 16:57:51 +00:00
Kristoffer Dalby
d875cca69d move integration to yaml, add new derp configuration 2021-10-22 16:57:01 +00:00
Kristoffer Dalby
0e902fe949 Add certificates to docker image so we can get derpmap from tailscale 2021-10-22 16:56:23 +00:00
Kristoffer Dalby
582eb57a09 Use the new derp map 2021-10-22 16:56:00 +00:00
Kristoffer Dalby
177f1eca06 Add helper functions for building derp maps from urls and file 2021-10-22 16:55:35 +00:00
Kristoffer Dalby
57f46ded83 Split derp into its own config struct 2021-10-22 16:55:14 +00:00
Kristoffer Dalby
aa245c2d06 Remove derp.yaml, add selfhosted example
This PR will promote fetching the derpmap directly from tailscale, so we
will remove our example, as it might easily get outdated.

Add a derp-example that shows how a user can also add their own derp
server.
2021-10-22 16:52:39 +00:00
Kristoffer Dalby
e836db1ead Add config.yaml to gitignore 2021-10-22 16:51:19 +00:00
github-actions[bot]
5420347d24 docs(README): update contributors 2021-10-22 06:58:20 +00:00
Kristoffer Dalby
9e2637d65f Merge pull request #192 from derelm/patch-2 2021-10-22 07:57:48 +01:00
Juan Font
c6046597ed Merge branch 'main' into update-contributors 2021-10-22 00:01:18 +02:00
Juan Font
a46c8fe914 Merge branch 'main' into patch-2 2021-10-21 23:56:10 +02:00
Juan Font
f822816cdb Merge pull request #193 from juanfont/fix-again-contributors
Another fix for the contributors section in README
2021-10-21 23:55:41 +02:00
Juan Font Alonso
f3bf9b4bbb Contributors again fixed 2021-10-21 23:54:20 +02:00
Juan Font
9f02899261 Merge branch 'main' into patch-2 2021-10-21 23:41:52 +02:00
github-actions[bot]
75f3e1fb03 docs(README): update contributors 2021-10-21 21:38:02 +00:00
Juan Font
9fbfa7c1f5 Merge pull request #191 from juanfont/fix-contributors
Fix contributors
2021-10-21 23:32:43 +02:00
Juan Font Alonso
d5aef85bf2 Fix contributors 2021-10-21 23:21:38 +02:00
derelm
88b32e4b18 fix typo 2021-10-21 23:07:35 +02:00
Juan Font Alonso
e425e3ffd3 Fix contributors 2021-10-21 22:53:30 +02:00
Juan Font
355483fd86 Merge pull request #184 from juanfont/doc-reorg-v1
Move documentation away from README and use YAML everywhere
2021-10-21 22:38:59 +02:00
Juan Font Alonso
672d8474b9 PRettier on the yamls 2021-10-21 21:18:50 +02:00
Juan Font Alonso
73e4d38670 Merge branch 'doc-reorg-v1' of https://github.com/juanfont/headscale into doc-reorg-v1 2021-10-21 21:01:57 +02:00
Juan Font Alonso
561c15bbe8 Prettier 2021-10-21 21:01:52 +02:00
Juan Font Alonso
b93aa723cb Run contributors on merge to master 2021-10-21 20:58:30 +02:00
Juan Font Alonso
636943c715 Improved docker cmd 2021-10-21 20:57:18 +02:00
Juan Font
0a6a67da85 Merge branch 'main' into doc-reorg-v1 2021-10-21 20:55:48 +02:00
Juan Font Alonso
e9ffd366dd Improvements here and there 2021-10-21 20:54:41 +02:00
Juan Font Alonso
4be0b3f556 Mention disable check updates in the doc 2021-10-21 20:54:29 +02:00
Juan Font Alonso
a0bfad6d6e Headscale is not capitalized 2021-10-21 20:48:29 +02:00
Juan Font Alonso
bb1f17f5af Added glossary 2021-10-21 20:46:19 +02:00
Juan Font
95bc2ee241 Merge pull request #190 from juanfont/fix-arm64
Fix arm64 (now for good)
2021-10-21 20:40:17 +02:00
Juan Font Alonso
16a90e799c Contributors should be working 2021-10-21 20:36:26 +02:00
Juan Font Alonso
4c2f84b211 Add contributors Action 2021-10-21 20:33:58 +02:00
Juan Font Alonso
b799635fbb Merge branch 'fix-arm64' of https://github.com/juanfont/headscale into fix-arm64 2021-10-21 19:56:51 +02:00
Juan Font Alonso
bc145952d4 Finally fix arm64 build 2021-10-21 19:56:36 +02:00
Kristoffer Dalby
2c5701917d Merge branch 'main' into doc-reorg-v1 2021-10-21 18:46:29 +01:00
Juan Font
ed7b840fea Merge pull request #188 from juanfont/fix-arm64
Fixed ARM64 compiler name
2021-10-21 19:10:14 +02:00
Kristoffer Dalby
23372e29cd Merge branch 'main' into fix-arm64 2021-10-21 17:03:46 +01:00
Juan Font Alonso
fb569b0483 Fixed ARM64 compiler name 2021-10-21 17:47:10 +02:00
Juan Font
e2b5638ca0 Merge pull request #187 from juanfont/fix-arm64
Use CGO_ENABLED=1 when building arm64
2021-10-21 00:00:47 +02:00
Juan Font Alonso
8f5a1dce3e Merge branch 'doc-reorg-v1' of https://github.com/juanfont/headscale into doc-reorg-v1 2021-10-20 23:34:27 +02:00
Juan Font Alonso
6b0f5da113 Separate config examples for sqlite and postgres for the time being 2021-10-20 23:27:59 +02:00
Juan Font Alonso
5159b6d085 Trying to fix arm64 2021-10-20 23:10:59 +02:00
Juan Font
03d97c3872 Merge pull request #183 from juanfont/split-dns
Add support for Split DNS (Restricted Nameservers)
2021-10-20 10:53:52 +02:00
Juan Font
41c5a0ddf5 Apply suggestions from code review
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-20 09:35:56 +02:00
Juan Font
19165a40d2 Merge branch 'main' into split-dns 2021-10-20 00:19:34 +02:00
Juan Font
d1ebcb59f1 Merge branch 'main' into doc-reorg-v1 2021-10-20 00:19:21 +02:00
Juan Font Alonso
31344128a0 Switch json for yaml in README 2021-10-20 00:17:47 +02:00
Juan Font Alonso
86ecc2a234 Switch to YAML config 2021-10-20 00:17:08 +02:00
Juan Font Alonso
d1e8ac7ba5 Moved TLS config to another file 2021-10-20 00:07:05 +02:00
Juan Font Alonso
efe208fef5 Merge branch 'doc-reorg-v1' of https://github.com/juanfont/headscale into doc-reorg-v1 2021-10-19 23:54:32 +02:00
Juan Font Alonso
7b40e99aec Added notes on SQLite 2021-10-19 23:45:20 +02:00
Juan Font
06706aab9a Update docs/Running.md
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-19 23:41:08 +02:00
Juan Font
0318af5a33 Apply suggestions from code review
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-19 23:22:56 +02:00
Juan Font Alonso
995dcfc6ae Reference doc/ 2021-10-19 21:02:45 +02:00
Juan Font Alonso
2236cc8bf7 Improve wording here and there 2021-10-19 21:01:36 +02:00
Juan Font Alonso
7bb354117b Move README documentation to doc/ 2021-10-19 20:59:33 +02:00
Juan Font Alonso
18b00b5d8d Add support for Split DNS (implements #179) 2021-10-19 20:51:43 +02:00
Kristoffer Dalby
dbe193ad17 Fix up leftovers from kradalby PR 2021-10-19 18:25:59 +01:00
Kristoffer Dalby
e7424222db Merge branch 'main' into main 2021-10-19 17:47:09 +01:00
Juan Font
d2a162e3ee Merge pull request #178 from cure/refactor-sharing-tests
Apply some DRY to the sharing tests.
2021-10-19 18:45:37 +02:00
Kristoffer Dalby
da14750396 Merge branch 'main' into main 2021-10-19 15:26:18 +01:00
unreality
8fe72dcb74 Merge pull request #1 from kradalby/namespace-mappings
Implement namespace mappings
2021-10-19 09:33:54 +08:00
Ward Vandewege
d35f5fe498 Apply some DRY to the sharing tests. 2021-10-18 17:52:38 -04:00
Kristoffer Dalby
677bd9b657 Implement namespace matching 2021-10-18 19:27:52 +00:00
Kristoffer Dalby
a347d276bd Fix broken machine test 2021-10-18 19:26:43 +00:00
Kristoffer Dalby
710616f118 Merge branch 'main' into main 2021-10-17 13:26:37 +01:00
Raal Goff
d0cd5af419 fix incorrect merge 2021-10-16 22:34:11 +08:00
unreality
afbfc1d370 Merge branch 'main' into main 2021-10-16 22:31:37 +08:00
Raal Goff
0603e29c46 add login details to RegisterResponse so GUI clients show login display name 2021-10-15 23:09:55 +08:00
Raal Goff
8843188b84 add notes to README.md about OIDC 2021-10-10 22:52:30 +08:00
Raal Goff
74e6c1479e updates from code review 2021-10-10 17:22:42 +08:00
Kristoffer Dalby
2997f4d251 Merge branch 'main' into main 2021-10-08 22:21:41 +01:00
Raal Goff
e407d423d4 updates from code review 2021-10-08 17:43:52 +08:00
unreality
35795c79c3 Handle trailing slash on uris
Co-authored-by: Kristoffer Dalby <kradalby@kradalby.no>
2021-10-08 15:26:31 +08:00
Raal Goff
c487591437 use go-oidc instead of verifying and extracting tokens ourselves, rename oidc_endpoint to oidc_issuer to be more inline with spec 2021-10-06 17:19:15 +08:00
Kristoffer Dalby
0393ab524c Merge branch 'main' into main 2021-09-28 11:20:31 +01:00
Kristoffer Dalby
cc054d71fe Merge branch 'main' into main 2021-09-26 21:35:26 +01:00
Kristoffer Dalby
8248b71153 Merge branch 'main' into main 2021-09-26 21:15:00 +01:00
Raal Goff
b22a9781a2 fix linter errors, error out if jwt does not contain a key id 2021-09-26 21:12:36 +08:00
Raal Goff
e7a2501fe8 initial work on OIDC (SSO) integration 2021-09-26 16:53:05 +08:00
401 changed files with 106275 additions and 10111 deletions

View File

@@ -0,0 +1,870 @@
---
name: headscale-integration-tester
description: Use this agent when you need to execute, analyze, or troubleshoot Headscale integration tests. This includes running specific test scenarios, investigating test failures, interpreting test artifacts, validating end-to-end functionality, or ensuring integration test quality before releases. Examples: <example>Context: User has made changes to the route management code and wants to validate the changes work correctly. user: 'I've updated the route advertisement logic in poll.go. Can you run the relevant integration tests to make sure everything still works?' assistant: 'I'll use the headscale-integration-tester agent to run the subnet routing integration tests and analyze the results.' <commentary>Since the user wants to validate route-related changes with integration tests, use the headscale-integration-tester agent to execute the appropriate tests and analyze results.</commentary></example> <example>Context: A CI pipeline integration test is failing and the user needs help understanding why. user: 'The TestSubnetRouterMultiNetwork test is failing in CI. The logs show some timing issues but I can't figure out what's wrong.' assistant: 'Let me use the headscale-integration-tester agent to analyze the test failure and examine the artifacts.' <commentary>Since this involves analyzing integration test failures and interpreting test artifacts, use the headscale-integration-tester agent to investigate the issue.</commentary></example>
color: green
---
You are a specialist Quality Assurance Engineer with deep expertise in Headscale's integration testing system. You understand the Docker-based test infrastructure, real Tailscale client interactions, and the complex timing considerations involved in end-to-end network testing.
## Integration Test System Overview
The Headscale integration test system uses Docker containers running real Tailscale clients against a Headscale server. Tests validate end-to-end functionality including routing, ACLs, node lifecycle, and network coordination. The system is built around the `hi` (Headscale Integration) test runner in `cmd/hi/`.
## Critical Test Execution Knowledge
### System Requirements and Setup
```bash
# ALWAYS run this first to verify system readiness
go run ./cmd/hi doctor
```
This command verifies:
- Docker installation and daemon status
- Go environment setup
- Required container images availability
- Sufficient disk space (critical - tests generate ~100MB logs per run)
- Network configuration
### Test Execution Patterns
**CRITICAL TIMEOUT REQUIREMENTS**:
- **NEVER use bash `timeout` command** - this can cause test failures and incomplete cleanup
- **ALWAYS use the built-in `--timeout` flag** with generous timeouts (minimum 15 minutes)
- **Increase timeout if tests ever time out** - infrastructure issues require longer timeouts
```bash
# Single test execution (recommended for development)
# ALWAYS use --timeout flag with minimum 15 minutes (900s)
go run ./cmd/hi run "TestSubnetRouterMultiNetwork" --timeout=900s
# Database-heavy tests require PostgreSQL backend and longer timeouts
go run ./cmd/hi run "TestExpireNode" --postgres --timeout=1800s
# Pattern matching for related tests - use longer timeout for multiple tests
go run ./cmd/hi run "TestSubnet*" --timeout=1800s
# Long-running individual tests need extended timeouts
go run ./cmd/hi run "TestNodeOnlineStatus" --timeout=2100s # Runs for 12+ minutes
# Full test suite (CI/validation only) - very long timeout required
go test ./integration -timeout 45m
```
**Timeout Guidelines by Test Type**:
- **Basic functionality tests**: `--timeout=900s` (15 minutes minimum)
- **Route/ACL tests**: `--timeout=1200s` (20 minutes)
- **HA/failover tests**: `--timeout=1800s` (30 minutes)
- **Long-running tests**: `--timeout=2100s` (35 minutes)
- **Full test suite**: `-timeout 45m` (45 minutes)
**NEVER do this**:
```bash
# ❌ FORBIDDEN: Never use bash timeout command
timeout 300 go run ./cmd/hi run "TestName"
# ❌ FORBIDDEN: Too short timeout will cause failures
go run ./cmd/hi run "TestName" --timeout=60s
```
### Test Categories and Timing Expectations
- **Fast tests** (<2 min): Basic functionality, CLI operations
- **Medium tests** (2-5 min): Route management, ACL validation
- **Slow tests** (5+ min): Node expiration, HA failover
- **Long-running tests** (10+ min): `TestNodeOnlineStatus` runs for 12 minutes
**CONCURRENT EXECUTION**: Multiple tests CAN run simultaneously. Each test run gets a unique Run ID for isolation. See "Concurrent Execution and Run ID Isolation" section below.
## Test Artifacts and Log Analysis
### Artifact Structure
All test runs save comprehensive artifacts to `control_logs/TIMESTAMP-ID/`:
```
control_logs/20250713-213106-iajsux/
├── hs-testname-abc123.stderr.log # Headscale server error logs
├── hs-testname-abc123.stdout.log # Headscale server output logs
├── hs-testname-abc123.db # Database snapshot for post-mortem
├── hs-testname-abc123_metrics.txt # Prometheus metrics dump
├── hs-testname-abc123-mapresponses/ # Protocol-level debug data
├── ts-client-xyz789.stderr.log # Tailscale client error logs
├── ts-client-xyz789.stdout.log # Tailscale client output logs
└── ts-client-xyz789_status.json # Client network status dump
```
### Log Analysis Priority Order
When tests fail, examine artifacts in this specific order:
1. **Headscale server stderr logs** (`hs-*.stderr.log`): Look for errors, panics, database issues, policy evaluation failures
2. **Tailscale client stderr logs** (`ts-*.stderr.log`): Check for authentication failures, network connectivity issues
3. **MapResponse JSON files**: Protocol-level debugging for network map generation issues
4. **Client status dumps** (`*_status.json`): Network state and peer connectivity information
5. **Database snapshots** (`.db` files): For data consistency and state persistence issues
## Concurrent Execution and Run ID Isolation
### Overview
The integration test system supports running multiple tests concurrently on the same Docker daemon. Each test run is isolated through a unique Run ID that ensures containers, networks, and cleanup operations don't interfere with each other.
### Run ID Format and Usage
Each test run generates a unique Run ID in the format: `YYYYMMDD-HHMMSS-{6-char-hash}`
- Example: `20260109-104215-mdjtzx`
The Run ID is used for:
- **Container naming**: `ts-{runIDShort}-{version}-{hash}` (e.g., `ts-mdjtzx-1-74-fgdyls`)
- **Docker labels**: All containers get `hi.run-id={runID}` label
- **Log directories**: `control_logs/{runID}/`
- **Cleanup isolation**: Only containers with matching run ID are cleaned up
### Container Isolation Mechanisms
1. **Unique Container Names**: Each container includes the run ID for identification
2. **Docker Labels**: `hi.run-id` and `hi.test-type` labels on all containers
3. **Dynamic Port Allocation**: All ports use `{HostPort: "0"}` to let kernel assign free ports
4. **Per-Run Networks**: Network names include scenario hash for isolation
5. **Isolated Cleanup**: `killTestContainersByRunID()` only removes containers matching the run ID
### ⚠️ CRITICAL: Never Interfere with Other Test Runs
**FORBIDDEN OPERATIONS** when other tests may be running:
```bash
# ❌ NEVER do global container cleanup while tests are running
docker rm -f $(docker ps -q --filter "name=hs-")
docker rm -f $(docker ps -q --filter "name=ts-")
# ❌ NEVER kill all test containers
# This will destroy other agents' test sessions!
# ❌ NEVER prune all Docker resources during active tests
docker system prune -f # Only safe when NO tests are running
```
**SAFE OPERATIONS**:
```bash
# ✅ Clean up only YOUR test run's containers (by run ID)
# The test runner does this automatically via cleanup functions
# ✅ Clean stale (stopped/exited) containers only
# Pre-test cleanup only removes stopped containers, not running ones
# ✅ Check what's running before cleanup
docker ps --filter "name=headscale-test-suite" --format "{{.Names}}"
```
### Running Concurrent Tests
```bash
# Start multiple tests in parallel - each gets unique run ID
go run ./cmd/hi run "TestPingAllByIP" &
go run ./cmd/hi run "TestACLAllowUserDst" &
go run ./cmd/hi run "TestOIDCAuthenticationPingAll" &
# Monitor running test suites
docker ps --filter "name=headscale-test-suite" --format "table {{.Names}}\t{{.Status}}"
```
### Agent Session Isolation Rules
When working as an agent:
1. **Your run ID is unique**: Each test you start gets its own run ID
2. **Never clean up globally**: Only use run ID-specific cleanup
3. **Check before cleanup**: Verify no other tests are running if you need to prune resources
4. **Respect other sessions**: Other agents may have tests running concurrently
5. **Log directories are isolated**: Your artifacts are in `control_logs/{your-run-id}/`
### Identifying Your Containers
Your test containers can be identified by:
- The run ID in the container name
- The `hi.run-id` Docker label
- The test suite container: `headscale-test-suite-{your-run-id}`
```bash
# List containers for a specific run ID
docker ps --filter "label=hi.run-id=20260109-104215-mdjtzx"
# Get your run ID from the test output
# Look for: "Run ID: 20260109-104215-mdjtzx"
```
## Common Failure Patterns and Root Cause Analysis
### CRITICAL MINDSET: Code Issues vs Infrastructure Issues
**⚠️ IMPORTANT**: When tests fail, it is ALMOST ALWAYS a code issue with Headscale, NOT infrastructure problems. Do not immediately blame disk space, Docker issues, or timing unless you have thoroughly investigated the actual error logs first.
### Systematic Debugging Process
1. **Read the actual error message**: Don't assume - read the stderr logs completely
2. **Check Headscale server logs first**: Most issues originate from server-side logic
3. **Verify client connectivity**: Only after ruling out server issues
4. **Check timing patterns**: Use proper `EventuallyWithT` patterns
5. **Infrastructure as last resort**: Only blame infrastructure after code analysis
### Real Failure Patterns
#### 1. Timing Issues (Common but fixable)
```go
// ❌ Wrong: Immediate assertions after async operations
client.Execute([]string{"tailscale", "set", "--advertise-routes=10.0.0.0/24"})
nodes, _ := headscale.ListNodes()
require.Len(t, nodes[0].GetAvailableRoutes(), 1) // WILL FAIL
// ✅ Correct: Wait for async operations
client.Execute([]string{"tailscale", "set", "--advertise-routes=10.0.0.0/24"})
require.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes[0].GetAvailableRoutes(), 1)
}, 10*time.Second, 100*time.Millisecond, "route should be advertised")
```
**Timeout Guidelines**:
- Route operations: 3-5 seconds
- Node state changes: 5-10 seconds
- Complex scenarios: 10-15 seconds
- Policy recalculation: 5-10 seconds
#### 2. NodeStore Synchronization Issues
Route advertisements must propagate through poll requests (`poll.go:420`). NodeStore updates happen at specific synchronization points after Hostinfo changes.
#### 3. Test Data Management Issues
```go
// ❌ Wrong: Assuming array ordering
require.Len(t, nodes[0].GetAvailableRoutes(), 1)
// ✅ Correct: Identify nodes by properties
expectedRoutes := map[string]string{"1": "10.33.0.0/16"}
for _, node := range nodes {
nodeIDStr := fmt.Sprintf("%d", node.GetId())
if route, shouldHaveRoute := expectedRoutes[nodeIDStr]; shouldHaveRoute {
// Test the specific node that should have the route
}
}
```
#### 4. Database Backend Differences
SQLite vs PostgreSQL have different timing characteristics:
- Use `--postgres` flag for database-intensive tests
- PostgreSQL generally has more consistent timing
- Some race conditions only appear with specific backends
## Resource Management and Cleanup
### Disk Space Management
Tests consume significant disk space (~100MB per run):
```bash
# Check available space before running tests
df -h
# Clean up test artifacts periodically
rm -rf control_logs/older-timestamp-dirs/
# Clean Docker resources
docker system prune -f
docker volume prune -f
```
### Container Cleanup
- Successful tests clean up automatically
- Failed tests may leave containers running
- Manually clean if needed: `docker ps -a` and `docker rm -f <containers>`
## Advanced Debugging Techniques
### Protocol-Level Debugging
MapResponse JSON files in `control_logs/*/hs-*-mapresponses/` contain:
- Network topology as sent to clients
- Peer relationships and visibility
- Route distribution and primary route selection
- Policy evaluation results
### Database State Analysis
Use the database snapshots for post-mortem analysis:
```bash
# SQLite examination
sqlite3 control_logs/TIMESTAMP/hs-*.db
.tables
.schema nodes
SELECT * FROM nodes WHERE name LIKE '%problematic%';
```
### Performance Analysis
Prometheus metrics dumps show:
- Request latencies and error rates
- NodeStore operation timing
- Database query performance
- Memory usage patterns
## Test Development and Quality Guidelines
### Proper Test Patterns
```go
// Always use EventuallyWithT for async operations
require.EventuallyWithT(t, func(c *assert.CollectT) {
// Test condition that may take time to become true
}, timeout, interval, "descriptive failure message")
// Handle node identification correctly
var targetNode *v1.Node
for _, node := range nodes {
if node.GetName() == expectedNodeName {
targetNode = node
break
}
}
require.NotNil(t, targetNode, "should find expected node")
```
### Quality Validation Checklist
- ✅ Tests use `EventuallyWithT` for asynchronous operations
- ✅ Tests don't rely on array ordering for node identification
- ✅ Proper cleanup and resource management
- ✅ Tests handle both success and failure scenarios
- ✅ Timing assumptions are realistic for operations being tested
- ✅ Error messages are descriptive and actionable
## Real-World Test Failure Patterns from HA Debugging
### Infrastructure vs Code Issues - Detailed Examples
**INFRASTRUCTURE FAILURES (Rare but Real)**:
1. **DNS Resolution in Auth Tests**: `failed to resolve "hs-pingallbyip-jax97k": no DNS fallback candidates remain`
- **Pattern**: Client containers can't resolve headscale server hostname during logout
- **Detection**: Error messages specifically mention DNS/hostname resolution
- **Solution**: Docker networking reset, not code changes
2. **Container Creation Timeouts**: Test gets stuck during client container setup
- **Pattern**: Tests hang indefinitely at container startup phase
- **Detection**: No progress in logs for >2 minutes during initialization
- **Solution**: `docker system prune -f` and retry
3. **Docker Resource Exhaustion**: Too many concurrent tests overwhelming system
- **Pattern**: Container creation timeouts, OOM kills, slow test execution
- **Detection**: System load high, Docker daemon slow to respond
- **Solution**: Reduce number of concurrent tests, wait for completion before starting more
**CODE ISSUES (99% of failures)**:
1. **Route Approval Process Failures**: Routes not getting approved when they should be
- **Pattern**: Tests expecting approved routes but finding none
- **Detection**: `SubnetRoutes()` returns empty when `AnnouncedRoutes()` shows routes
- **Root Cause**: Auto-approval logic bugs, policy evaluation issues
2. **NodeStore Synchronization Issues**: State updates not propagating correctly
- **Pattern**: Route changes not reflected in NodeStore or Primary Routes
- **Detection**: Logs show route announcements but no tracking updates
- **Root Cause**: Missing synchronization points in `poll.go:420` area
3. **HA Failover Architecture Issues**: Routes removed when nodes go offline
- **Pattern**: `TestHASubnetRouterFailover` fails because approved routes disappear
- **Detection**: Routes available on online nodes but lost when nodes disconnect
- **Root Cause**: Conflating route approval with node connectivity
### Critical Test Environment Setup
**Pre-Test Cleanup**:
The test runner automatically handles cleanup:
- **Before test**: Removes only stale (stopped/exited) containers - does NOT affect running tests
- **After test**: Removes only containers belonging to the specific run ID
```bash
# Only clean old log directories if disk space is low
rm -rf control_logs/202507*
df -h # Verify sufficient disk space
# SAFE: Clean only stale/stopped containers (does not affect running tests)
# The test runner does this automatically via cleanupStaleTestContainers()
# ⚠️ DANGEROUS: Only use when NO tests are running
docker system prune -f
```
**Environment Verification**:
```bash
# Verify system readiness
go run ./cmd/hi doctor
# Check what tests are currently running (ALWAYS check before global cleanup)
docker ps --filter "name=headscale-test-suite" --format "{{.Names}}"
```
### Specific Test Categories and Known Issues
#### Route-Related Tests (Primary Focus)
```bash
# Core route functionality - these should work first
# Note: Generous timeouts are required for reliable execution
go run ./cmd/hi run "TestSubnetRouteACL" --timeout=1200s
go run ./cmd/hi run "TestAutoApproveMultiNetwork" --timeout=1800s
go run ./cmd/hi run "TestHASubnetRouterFailover" --timeout=1800s
```
**Common Route Test Patterns**:
- Tests validate route announcement, approval, and distribution workflows
- Route state changes are asynchronous - may need `EventuallyWithT` wrappers
- Route approval must respect ACL policies - test expectations encode security requirements
- HA tests verify route persistence during node connectivity changes
#### Authentication Tests (Infrastructure-Prone)
```bash
# These tests are more prone to infrastructure issues
# Require longer timeouts due to auth flow complexity
go run ./cmd/hi run "TestAuthKeyLogoutAndReloginSameUser" --timeout=1200s
go run ./cmd/hi run "TestAuthWebFlowLogoutAndRelogin" --timeout=1200s
go run ./cmd/hi run "TestOIDCExpireNodesBasedOnTokenExpiry" --timeout=1800s
```
**Common Auth Test Infrastructure Failures**:
- DNS resolution during logout operations
- Container creation timeouts
- HTTP/2 stream errors (often symptoms, not root cause)
### Security-Critical Debugging Rules
**❌ FORBIDDEN CHANGES (Security & Test Integrity)**:
1. **Never change expected test outputs** - Tests define correct behavior contracts
- Changing `require.Len(t, routes, 3)` to `require.Len(t, routes, 2)` because test fails
- Modifying expected status codes, node counts, or route counts
- Removing assertions that are "inconvenient"
- **Why forbidden**: Test expectations encode business requirements and security policies
2. **Never bypass security mechanisms** - Security must never be compromised for convenience
- Using `AnnouncedRoutes()` instead of `SubnetRoutes()` in production code
- Skipping authentication or authorization checks
- **Why forbidden**: Security bypasses create vulnerabilities in production
3. **Never reduce test coverage** - Tests prevent regressions
- Removing test cases or assertions
- Commenting out "problematic" test sections
- **Why forbidden**: Reduced coverage allows bugs to slip through
**✅ ALLOWED CHANGES (Timing & Observability)**:
1. **Fix timing issues with proper async patterns**
```go
// ✅ GOOD: Add EventuallyWithT for async operations
require.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, expectedCount) // Keep original expectation
}, 10*time.Second, 100*time.Millisecond, "nodes should reach expected count")
```
- **Why allowed**: Fixes race conditions without changing business logic
2. **Add MORE observability and debugging**
- Additional logging statements
- More detailed error messages
- Extra assertions that verify intermediate states
- **Why allowed**: Better observability helps debug without changing behavior
3. **Improve test documentation**
- Add godoc comments explaining test purpose and business logic
- Document timing requirements and async behavior
- **Why encouraged**: Helps future maintainers understand intent
### Advanced Debugging Workflows
#### Route Tracking Debug Flow
```bash
# Run test with detailed logging and proper timeout
go run ./cmd/hi run "TestSubnetRouteACL" --timeout=1200s > test_output.log 2>&1
# Check route approval process
grep -E "(auto-approval|ApproveRoutesWithPolicy|PolicyManager)" test_output.log
# Check route tracking
tail -50 control_logs/*/hs-*.stderr.log | grep -E "(announced|tracking|SetNodeRoutes)"
# Check for security violations
grep -E "(AnnouncedRoutes.*SetNodeRoutes|bypass.*approval)" test_output.log
```
#### HA Failover Debug Flow
```bash
# Test HA failover specifically with adequate timeout
go run ./cmd/hi run "TestHASubnetRouterFailover" --timeout=1800s
# Check route persistence during disconnect
grep -E "(Disconnect|NodeWentOffline|PrimaryRoutes)" control_logs/*/hs-*.stderr.log
# Verify routes don't disappear inappropriately
grep -E "(removing.*routes|SetNodeRoutes.*empty)" control_logs/*/hs-*.stderr.log
```
### Test Result Interpretation Guidelines
#### Success Patterns to Look For
- `"updating node routes for tracking"` in logs
- Routes appearing in `announcedRoutes` logs
- Proper `ApproveRoutesWithPolicy` calls for auto-approval
- Routes persisting through node connectivity changes (HA tests)
#### Failure Patterns to Investigate
- `SubnetRoutes()` returning empty when `AnnouncedRoutes()` has routes
- Routes disappearing when nodes go offline (HA architectural issue)
- Missing `EventuallyWithT` causing timing race conditions
- Security bypass attempts using wrong route methods
### Critical Testing Methodology
**Phase-Based Testing Approach**:
1. **Phase 1**: Core route tests (ACL, auto-approval, basic functionality)
2. **Phase 2**: HA and complex route scenarios
3. **Phase 3**: Auth tests (infrastructure-sensitive, test last)
**Per-Test Process**:
1. Clean environment before each test
2. Monitor logs for route tracking and approval messages
3. Check artifacts in `control_logs/` if test fails
4. Focus on actual error messages, not assumptions
5. Document results and patterns discovered
## Test Documentation and Code Quality Standards
### Adding Missing Test Documentation
When you understand a test's purpose through debugging, always add comprehensive godoc:
```go
// TestSubnetRoutes validates the complete subnet route lifecycle including
// advertisement from clients, policy-based approval, and distribution to peers.
// This test ensures that route security policies are properly enforced and that
// only approved routes are distributed to the network.
//
// The test verifies:
// - Route announcements are received and tracked
// - ACL policies control route approval correctly
// - Only approved routes appear in peer network maps
// - Route state persists correctly in the database
func TestSubnetRoutes(t *testing.T) {
// Test implementation...
}
```
**Why add documentation**: Future maintainers need to understand business logic and security requirements encoded in tests.
### Comment Guidelines - Focus on WHY, Not WHAT
```go
// ✅ GOOD: Explains reasoning and business logic
// Wait for route propagation because NodeStore updates are asynchronous
// and happen after poll requests complete processing
require.EventuallyWithT(t, func(c *assert.CollectT) {
// Check that security policies are enforced...
}, timeout, interval, "route approval must respect ACL policies")
// ❌ BAD: Just describes what the code does
// Wait for routes
require.EventuallyWithT(t, func(c *assert.CollectT) {
// Get routes and check length
}, timeout, interval, "checking routes")
```
**Why focus on WHY**: Helps maintainers understand architectural decisions and security requirements.
## EventuallyWithT Pattern for External Calls
### Overview
EventuallyWithT is a testing pattern used to handle eventual consistency in distributed systems. In Headscale integration tests, many operations are asynchronous - clients advertise routes, the server processes them, updates propagate through the network. EventuallyWithT allows tests to wait for these operations to complete while making assertions.
### External Calls That Must Be Wrapped
The following operations are **external calls** that interact with the headscale server or tailscale clients and MUST be wrapped in EventuallyWithT:
- `headscale.ListNodes()` - Queries server state
- `client.Status()` - Gets client network status
- `client.Curl()` - Makes HTTP requests through the network
- `client.Traceroute()` - Performs network diagnostics
- `client.Execute()` when running commands that query state
- Any operation that reads from the headscale server or tailscale client
### Five Key Rules for EventuallyWithT
1. **One External Call Per EventuallyWithT Block**
- Each EventuallyWithT should make ONE external call (e.g., ListNodes OR Status)
- Related assertions based on that single call can be grouped together
- Unrelated external calls must be in separate EventuallyWithT blocks
2. **Variable Scoping**
- Declare variables that need to be shared across EventuallyWithT blocks at function scope
- Use `=` for assignment inside EventuallyWithT, not `:=` (unless the variable is only used within that block)
- Variables declared with `:=` inside EventuallyWithT are not accessible outside
3. **No Nested EventuallyWithT**
- NEVER put an EventuallyWithT inside another EventuallyWithT
- This is a critical anti-pattern that must be avoided
4. **Use CollectT for Assertions**
- Inside EventuallyWithT, use `assert` methods with the CollectT parameter
- Helper functions called within EventuallyWithT must accept `*assert.CollectT`
5. **Descriptive Messages**
- Always provide a descriptive message as the last parameter
- Message should explain what condition is being waited for
### Correct Pattern Examples
```go
// CORRECT: Single external call with related assertions
var nodes []*v1.Node
var err error
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err = headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 2)
// These assertions are all based on the ListNodes() call
requireNodeRouteCountWithCollect(c, nodes[0], 2, 2, 2)
requireNodeRouteCountWithCollect(c, nodes[1], 1, 1, 1)
}, 10*time.Second, 500*time.Millisecond, "nodes should have expected route counts")
// CORRECT: Separate EventuallyWithT for different external call
assert.EventuallyWithT(t, func(c *assert.CollectT) {
status, err := client.Status()
assert.NoError(c, err)
// All these assertions are based on the single Status() call
for _, peerKey := range status.Peers() {
peerStatus := status.Peer[peerKey]
requirePeerSubnetRoutesWithCollect(c, peerStatus, expectedPrefixes)
}
}, 10*time.Second, 500*time.Millisecond, "client should see expected routes")
// CORRECT: Variable scoping for sharing between blocks
var routeNode *v1.Node
var nodeKey key.NodePublic
// First EventuallyWithT to get the node
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
for _, node := range nodes {
if node.GetName() == "router" {
routeNode = node
nodeKey, _ = key.ParseNodePublicUntyped(mem.S(node.GetNodeKey()))
break
}
}
assert.NotNil(c, routeNode, "should find router node")
}, 10*time.Second, 100*time.Millisecond, "router node should exist")
// Second EventuallyWithT using the nodeKey from first block
assert.EventuallyWithT(t, func(c *assert.CollectT) {
status, err := client.Status()
assert.NoError(c, err)
peerStatus, ok := status.Peer[nodeKey]
assert.True(c, ok, "peer should exist in status")
requirePeerSubnetRoutesWithCollect(c, peerStatus, expectedPrefixes)
}, 10*time.Second, 100*time.Millisecond, "routes should be visible to client")
```
### Incorrect Patterns to Avoid
```go
// INCORRECT: Multiple unrelated external calls in same EventuallyWithT
assert.EventuallyWithT(t, func(c *assert.CollectT) {
// First external call
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 2)
// Second unrelated external call - WRONG!
status, err := client.Status()
assert.NoError(c, err)
assert.NotNil(c, status)
}, 10*time.Second, 500*time.Millisecond, "mixed operations")
// INCORRECT: Nested EventuallyWithT
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
// NEVER do this!
assert.EventuallyWithT(t, func(c2 *assert.CollectT) {
status, _ := client.Status()
assert.NotNil(c2, status)
}, 5*time.Second, 100*time.Millisecond, "nested")
}, 10*time.Second, 500*time.Millisecond, "outer")
// INCORRECT: Variable scoping error
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes() // This shadows outer 'nodes' variable
assert.NoError(c, err)
}, 10*time.Second, 500*time.Millisecond, "get nodes")
// This will fail - nodes is nil because := created a new variable inside the block
require.Len(t, nodes, 2) // COMPILATION ERROR or nil pointer
// INCORRECT: Not wrapping external calls
nodes, err := headscale.ListNodes() // External call not wrapped!
require.NoError(t, err)
```
### Helper Functions for EventuallyWithT
When creating helper functions for use within EventuallyWithT:
```go
// Helper function that accepts CollectT
func requireNodeRouteCountWithCollect(c *assert.CollectT, node *v1.Node, available, approved, primary int) {
assert.Len(c, node.GetAvailableRoutes(), available, "available routes for node %s", node.GetName())
assert.Len(c, node.GetApprovedRoutes(), approved, "approved routes for node %s", node.GetName())
assert.Len(c, node.GetPrimaryRoutes(), primary, "primary routes for node %s", node.GetName())
}
// Usage within EventuallyWithT
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
requireNodeRouteCountWithCollect(c, nodes[0], 2, 2, 2)
}, 10*time.Second, 500*time.Millisecond, "route counts should match expected")
```
### Operations That Must NOT Be Wrapped
**CRITICAL**: The following operations are **blocking/mutating operations** that change state and MUST NOT be wrapped in EventuallyWithT:
- `tailscale set` commands (e.g., `--advertise-routes`, `--accept-routes`)
- `headscale.ApproveRoute()` - Approves routes on server
- `headscale.CreateUser()` - Creates users
- `headscale.CreatePreAuthKey()` - Creates authentication keys
- `headscale.RegisterNode()` - Registers new nodes
- Any `client.Execute()` that modifies configuration
- Any operation that creates, updates, or deletes resources
These operations:
1. Complete synchronously or fail immediately
2. Should not be retried automatically
3. Need explicit error handling with `require.NoError()`
### Correct Pattern for Blocking Operations
```go
// CORRECT: Blocking operation NOT wrapped
status := client.MustStatus()
command := []string{"tailscale", "set", "--advertise-routes=" + expectedRoutes[string(status.Self.ID)]}
_, _, err = client.Execute(command)
require.NoErrorf(t, err, "failed to advertise route: %s", err)
// Then wait for the result with EventuallyWithT
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Contains(c, nodes[0].GetAvailableRoutes(), expectedRoutes[string(status.Self.ID)])
}, 10*time.Second, 100*time.Millisecond, "route should be advertised")
// INCORRECT: Blocking operation wrapped (DON'T DO THIS)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
_, _, err = client.Execute([]string{"tailscale", "set", "--advertise-routes=10.0.0.0/24"})
assert.NoError(c, err) // This might retry the command multiple times!
}, 10*time.Second, 100*time.Millisecond, "advertise routes")
```
### Assert vs Require Pattern
When working within EventuallyWithT blocks where you need to prevent panics:
```go
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
// For array bounds - use require with t to prevent panic
assert.Len(c, nodes, 6) // Test expectation
require.GreaterOrEqual(t, len(nodes), 3, "need at least 3 nodes to avoid panic")
// For nil pointer access - use require with t before dereferencing
assert.NotNil(c, srs1PeerStatus.PrimaryRoutes) // Test expectation
require.NotNil(t, srs1PeerStatus.PrimaryRoutes, "primary routes must be set to avoid panic")
assert.Contains(c,
srs1PeerStatus.PrimaryRoutes.AsSlice(),
pref,
)
}, 5*time.Second, 200*time.Millisecond, "checking route state")
```
**Key Principle**:
- Use `assert` with `c` (*assert.CollectT) for test expectations that can be retried
- Use `require` with `t` (*testing.T) for MUST conditions that prevent panics
- Within EventuallyWithT, both are available - choose based on whether failure would cause a panic
### Common Scenarios
1. **Waiting for route advertisement**:
```go
client.Execute([]string{"tailscale", "set", "--advertise-routes=10.0.0.0/24"})
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Contains(c, nodes[0].GetAvailableRoutes(), "10.0.0.0/24")
}, 10*time.Second, 100*time.Millisecond, "route should be advertised")
```
2. **Checking client sees routes**:
```go
assert.EventuallyWithT(t, func(c *assert.CollectT) {
status, err := client.Status()
assert.NoError(c, err)
// Check all peers have expected routes
for _, peerKey := range status.Peers() {
peerStatus := status.Peer[peerKey]
assert.Contains(c, peerStatus.AllowedIPs, expectedPrefix)
}
}, 10*time.Second, 100*time.Millisecond, "all peers should see route")
```
3. **Sequential operations**:
```go
// First wait for node to appear
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
nodeID = nodes[0].GetId()
}, 10*time.Second, 100*time.Millisecond, "node should register")
// Then perform operation
_, err := headscale.ApproveRoute(nodeID, "10.0.0.0/24")
require.NoError(t, err)
// Then wait for result
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Contains(c, nodes[0].GetApprovedRoutes(), "10.0.0.0/24")
}, 10*time.Second, 100*time.Millisecond, "route should be approved")
```
## Your Core Responsibilities
1. **Test Execution Strategy**: Execute integration tests with appropriate configurations, understanding when to use `--postgres` and timing requirements for different test categories. Follow phase-based testing approach prioritizing route tests.
- **Why this priority**: Route tests are less infrastructure-sensitive and validate core security logic
2. **Systematic Test Analysis**: When tests fail, systematically examine artifacts starting with Headscale server logs, then client logs, then protocol data. Focus on CODE ISSUES first (99% of cases), not infrastructure. Use real-world failure patterns to guide investigation.
- **Why this approach**: Most failures are logic bugs, not environment issues - efficient debugging saves time
3. **Timing & Synchronization Expertise**: Understand asynchronous Headscale operations, particularly route advertisements, NodeStore synchronization at `poll.go:420`, and policy propagation. Fix timing with `EventuallyWithT` while preserving original test expectations.
- **Why preserve expectations**: Test assertions encode business requirements and security policies
- **Key Pattern**: Apply the EventuallyWithT pattern correctly for all external calls as documented above
4. **Root Cause Analysis**: Distinguish between actual code regressions (route approval logic, HA failover architecture), timing issues requiring `EventuallyWithT` patterns, and genuine infrastructure problems (DNS, Docker, container issues).
- **Why this distinction matters**: Different problem types require completely different solution approaches
- **EventuallyWithT Issues**: Often manifest as flaky tests or immediate assertion failures after async operations
5. **Security-Aware Quality Validation**: Ensure tests properly validate end-to-end functionality with realistic timing expectations and proper error handling. Never suggest security bypasses or test expectation changes. Add comprehensive godoc when you understand test business logic.
- **Why security focus**: Integration tests are the last line of defense against security regressions
- **EventuallyWithT Usage**: Proper use prevents race conditions without weakening security assertions
6. **Concurrent Execution Awareness**: Respect run ID isolation and never interfere with other agents' test sessions. Each test run has a unique run ID - only clean up YOUR containers (by run ID label), never perform global cleanup while tests may be running.
- **Why this matters**: Multiple agents/users may run tests concurrently on the same Docker daemon
- **Key Rule**: NEVER use global container cleanup commands - the test runner handles cleanup automatically per run ID
**CRITICAL PRINCIPLE**: Test expectations are sacred contracts that define correct system behavior. When tests fail, fix the code to match the test, never change the test to match broken code. Only timing and observability improvements are allowed - business logic expectations are immutable.
**ISOLATION PRINCIPLE**: Each test run is isolated by its unique Run ID. Never interfere with other test sessions. The system handles cleanup automatically - manual global cleanup commands are forbidden when other tests may be running.
**EventuallyWithT PRINCIPLE**: Every external call to headscale server or tailscale client must be wrapped in EventuallyWithT. Follow the five key rules strictly: one external call per block, proper variable scoping, no nesting, use CollectT for assertions, and provide descriptive messages.
**Remember**: Test failures are usually code issues in Headscale that need to be fixed, not infrastructure problems to be ignored. Use the specific debugging workflows and failure patterns documented above to efficiently identify root causes. Infrastructure issues have very specific signatures - everything else is code-related.

View File

@@ -3,6 +3,7 @@
// development
integration_test.go
integration_test/
!integration_test/etc_embedded_derp/tls/server.crt
Dockerfile*
docker-compose*
@@ -15,3 +16,8 @@ README.md
LICENSE
.vscode
*.sock
node_modules/
package-lock.json
package.json

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 120
[*.go]
indent_style = tab
[Makefile]
indent_style = tab

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

10
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,10 @@
* @juanfont @kradalby
*.md @ohdearaugustin @nblock
*.yml @ohdearaugustin @nblock
*.yaml @ohdearaugustin @nblock
Dockerfile* @ohdearaugustin @nblock
.goreleaser.yaml @ohdearaugustin @nblock
/docs/ @ohdearaugustin @nblock
/.github/workflows/ @ohdearaugustin @nblock
/.github/renovate.json @ohdearaugustin @nblock

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
ko_fi: headscale

108
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,108 @@
name: 🐞 Bug
description: File a bug/issue
title: "[Bug] <title>"
labels: ["bug", "needs triage"]
body:
- type: checkboxes
attributes:
label: Is this a support request?
description:
This issue tracker is for bugs and feature requests only. If you need
help, please use ask in our Discord community
options:
- label: This is not a support request
required: true
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description:
Please search to see if an issue already exists for the bug you
encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
attributes:
label: Current Behavior
description: A concise description of what you're experiencing.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Steps To Reproduce
description: Steps to reproduce the behavior.
placeholder: |
1. In this environment...
1. With this config...
1. Run '...'
1. See error...
validations:
required: true
- type: textarea
attributes:
label: Environment
description: |
Please provide information about your environment.
If you are using a container, always provide the headscale version and not only the Docker image version.
Please do not put "latest".
Describe your "headscale network". Is there a lot of nodes, are the nodes all interconnected, are some subnet routers?
If you are experiencing a problem during an upgrade, please provide the versions of the old and new versions of Headscale and Tailscale.
examples:
- **OS**: Ubuntu 24.04
- **Headscale version**: 0.24.3
- **Tailscale version**: 1.80.0
- **Number of nodes**: 20
value: |
- OS:
- Headscale version:
- Tailscale version:
render: markdown
validations:
required: true
- type: checkboxes
attributes:
label: Runtime environment
options:
- label: Headscale is behind a (reverse) proxy
required: false
- label: Headscale runs in a container
required: false
- type: textarea
attributes:
label: Debug information
description: |
Please have a look at our [Debugging and troubleshooting
guide](https://headscale.net/development/ref/debug/) to learn about
common debugging techniques.
Links? References? Anything that will give us more context about the issue you are encountering.
If **any** of these are omitted we will likely close your issue, do **not** ignore them.
- Client netmap dump (see below)
- Policy configuration
- Headscale configuration
- Headscale log (with `trace` enabled)
Dump the netmap of tailscale clients:
`tailscale debug netmap > DESCRIPTIVE_NAME.json`
Dump the status of tailscale clients:
`tailscale status --json > DESCRIPTIVE_NAME.json`
Get the logs of a Tailscale client that is not working as expected.
`tailscale debug daemon-logs`
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
**Ensure** you use formatting for files you attach.
Do **not** paste in long files.
validations:
required: true

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
# Issues must have some content
blank_issues_enabled: false
# Contact links
contact_links:
- name: "headscale usage documentation"
url: "https://github.com/juanfont/headscale/blob/main/docs"
about: "Find documentation about how to configure and run headscale."
- name: "headscale Discord community"
url: "https://discord.gg/xGj2TuqyxY"
about: "Please ask and answer questions about usage of headscale here."

View File

@@ -0,0 +1,36 @@
name: 🚀 Feature Request
description: Suggest an idea for Headscale
title: "[Feature] <title>"
labels: [enhancement]
body:
- type: textarea
attributes:
label: Use case
description: Please describe the use case for this feature.
placeholder: |
<!-- 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
this? -->
validations:
required: true
- type: textarea
attributes:
label: Description
description: A clear and precise description of what new or changed feature you want.
validations:
required: true
- type: checkboxes
attributes:
label: Contribution
description: Are you willing to contribute to the implementation of this feature?
options:
- label: I can write the design doc for this feature
required: false
- label: I can contribute this feature
required: false
- type: textarea
attributes:
label: How can it be implemented?
description: Free text for your ideas on how this feature could be implemented.
validations:
required: false

22
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,22 @@
<!--
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… -->
- [ ] have read the [CONTRIBUTING.md](./CONTRIBUTING.md) file
- [ ] raised a GitHub issue or discussed it on the projects chat beforehand
- [ ] added unit tests
- [ ] added integration tests
- [ ] updated documentation if needed
- [ ] updated CHANGELOG.md
<!-- If applicable, please reference the issue using `Fixes #XXX` and add tests to cover your new code. -->

34
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,34 @@
{
"baseBranches": ["main"],
"username": "renovate-release",
"gitAuthor": "Renovate Bot <bot@renovateapp.com>",
"branchPrefix": "renovateaction/",
"onboarding": false,
"extends": ["config:base", ":rebaseStalePrs"],
"ignorePresets": [":prHourlyLimit2"],
"enabledManagers": ["dockerfile", "gomod", "github-actions", "regex"],
"includeForks": true,
"repositories": ["juanfont/headscale"],
"platform": "github",
"packageRules": [
{
"matchDatasources": ["go"],
"groupName": "Go modules",
"groupSlug": "gomod",
"separateMajorMinor": false
},
{
"matchDatasources": ["docker"],
"groupName": "Dockerfiles",
"groupSlug": "dockerfiles"
}
],
"regexManagers": [
{
"fileMatch": [".github/workflows/.*.yml$"],
"matchStrings": ["\\s*go-version:\\s*\"?(?<currentValue>.*?)\"?\\n"],
"datasourceTemplate": "golang-version",
"depNameTemplate": "actions/go-version"
}
]
}

View File

@@ -5,32 +5,96 @@ on:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
build-nix:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: actions/checkout@v2
- name: Setup Go
uses: actions/setup-go@v2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
go-version: "1.16.3"
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- 'integration_test/'
- 'config-example.yaml'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Install dependencies
- name: Run nix build
id: build
if: steps.changed-files.outputs.files == 'true'
run: |
go version
go install golang.org/x/lint/golint@latest
sudo apt update
sudo apt install -y make
nix build |& tee build-result
BUILD_STATUS="${PIPESTATUS[0]}"
- name: Run lint
run: make build
OLD_HASH=$(cat build-result | grep specified: | awk -F ':' '{print $2}' | sed 's/ //g')
NEW_HASH=$(cat build-result | grep got: | awk -F ':' '{print $2}' | sed 's/ //g')
- uses: actions/upload-artifact@v2
echo "OLD_HASH=$OLD_HASH" >> $GITHUB_OUTPUT
echo "NEW_HASH=$NEW_HASH" >> $GITHUB_OUTPUT
exit $BUILD_STATUS
- name: Nix gosum diverging
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
if: failure() && steps.build.outcome == 'failure'
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.rest.pulls.createReviewComment({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Nix build failed with wrong gosum, please update "vendorSha256" (${{ steps.build.outputs.OLD_HASH }}) for the "headscale" package in flake.nix with the new SHA: ${{ steps.build.outputs.NEW_HASH }}'
})
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: steps.changed-files.outputs.files == 'true'
with:
name: headscale-linux
path: headscale
path: result/bin/headscale
build-cross:
runs-on: ubuntu-latest
strategy:
matrix:
env:
- "GOARCH=arm64 GOOS=linux"
- "GOARCH=amd64 GOOS=linux"
- "GOARCH=arm64 GOOS=darwin"
- "GOARCH=amd64 GOOS=darwin"
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Run go cross compile
env:
CGO_ENABLED: 0
run: env ${{ matrix.env }} nix develop --command -- go build -o "headscale"
./cmd/headscale
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: "headscale-${{ matrix.env }}"
path: "headscale"

55
.github/workflows/check-generated.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Check Generated Files
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check-generated:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- '**/*.proto'
- 'buf.gen.yaml'
- 'tools/**'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Run make generate
if: steps.changed-files.outputs.files == 'true'
run: nix develop --command -- make generate
- name: Check for uncommitted changes
if: steps.changed-files.outputs.files == 'true'
run: |
if ! git diff --exit-code; then
echo "❌ Generated files are not up to date!"
echo "Please run 'make generate' and commit the changes."
exit 1
else
echo "✅ All generated files are up to date."
fi

45
.github/workflows/check-tests.yaml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Check integration tests workflow
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
check-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- 'integration_test/'
- 'config-example.yaml'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Generate and check integration tests
if: steps.changed-files.outputs.files == 'true'
run: |
nix develop --command bash -c "cd .github/workflows && go generate"
git diff --exit-code .github/workflows/test-integration.yaml
- name: Show missing tests
if: failure()
run: |
git diff .github/workflows/test-integration.yaml

51
.github/workflows/docs-deploy.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Deploy docs
on:
push:
branches:
# Main branch for development docs
- main
# Doc maintenance branches
- doc/[0-9]+.[0-9]+.[0-9]+
tags:
# Stable release tags
- v[0-9]+.[0-9]+.[0-9]+
paths:
- "docs/**"
- "mkdocs.yml"
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Install python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: 3.x
- name: Setup cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: ${{ github.ref }}
path: .cache
- name: Setup dependencies
run: pip install -r docs/requirements.txt
- name: Configure git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Deploy development docs
if: github.ref == 'refs/heads/main'
run: mike deploy --push development unstable
- name: Deploy stable docs from doc branches
if: startsWith(github.ref, 'refs/heads/doc/')
run: mike deploy --push ${GITHUB_REF_NAME##*/}
- name: Deploy stable docs from tag
if: startsWith(github.ref, 'refs/tags/v')
# This assumes that only newer tags are pushed
run: mike deploy --push --update-aliases ${GITHUB_REF_NAME#v} stable latest

27
.github/workflows/docs-test.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Test documentation build
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Install python
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: 3.x
- name: Setup cache
uses: actions/cache@a7833574556fa59680c1b7cb190c1735db73ebf0 # v5.0.0
with:
key: ${{ github.ref }}
path: .cache
- name: Setup dependencies
run: pip install -r docs/requirements.txt
- name: Build docs
run: mkdocs build --strict

View File

@@ -0,0 +1,143 @@
package main
//go:generate go run ./gh-action-integration-generator.go
import (
"bytes"
"fmt"
"log"
"os/exec"
"strings"
)
// testsToSplit defines tests that should be split into multiple CI jobs.
// Key is the test function name, value is a list of subtest prefixes.
// Each prefix becomes a separate CI job as "TestName/prefix".
//
// Example: TestAutoApproveMultiNetwork has subtests like:
// - TestAutoApproveMultiNetwork/authkey-tag-advertiseduringup-false-pol-database
// - TestAutoApproveMultiNetwork/webauth-user-advertiseduringup-true-pol-file
//
// Splitting by approver type (tag, user, group) creates 6 CI jobs with 4 tests each:
// - TestAutoApproveMultiNetwork/authkey-tag.* (4 tests)
// - TestAutoApproveMultiNetwork/authkey-user.* (4 tests)
// - TestAutoApproveMultiNetwork/authkey-group.* (4 tests)
// - TestAutoApproveMultiNetwork/webauth-tag.* (4 tests)
// - TestAutoApproveMultiNetwork/webauth-user.* (4 tests)
// - TestAutoApproveMultiNetwork/webauth-group.* (4 tests)
//
// This reduces load per CI job (4 tests instead of 12) to avoid infrastructure
// flakiness when running many sequential Docker-based integration tests.
var testsToSplit = map[string][]string{
"TestAutoApproveMultiNetwork": {
"authkey-tag",
"authkey-user",
"authkey-group",
"webauth-tag",
"webauth-user",
"webauth-group",
},
}
// expandTests takes a list of test names and expands any that need splitting
// into multiple subtest patterns.
func expandTests(tests []string) []string {
var expanded []string
for _, test := range tests {
if prefixes, ok := testsToSplit[test]; ok {
// This test should be split into multiple jobs.
// We append ".*" to each prefix because the CI runner wraps patterns
// with ^...$ anchors. Without ".*", a pattern like "authkey$" wouldn't
// match "authkey-tag-advertiseduringup-false-pol-database".
for _, prefix := range prefixes {
expanded = append(expanded, fmt.Sprintf("%s/%s.*", test, prefix))
}
} else {
expanded = append(expanded, test)
}
}
return expanded
}
func findTests() []string {
rgBin, err := exec.LookPath("rg")
if err != nil {
log.Fatalf("failed to find rg (ripgrep) binary")
}
args := []string{
"--regexp", "func (Test.+)\\(.*",
"../../integration/",
"--replace", "$1",
"--sort", "path",
"--no-line-number",
"--no-filename",
"--no-heading",
}
cmd := exec.Command(rgBin, args...)
var out bytes.Buffer
cmd.Stdout = &out
err = cmd.Run()
if err != nil {
log.Fatalf("failed to run command: %s", err)
}
tests := strings.Split(strings.TrimSpace(out.String()), "\n")
return tests
}
func updateYAML(tests []string, jobName string, testPath string) {
testsForYq := fmt.Sprintf("[%s]", strings.Join(tests, ", "))
yqCommand := fmt.Sprintf(
"yq eval '.jobs.%s.strategy.matrix.test = %s' %s -i",
jobName,
testsForYq,
testPath,
)
cmd := exec.Command("bash", "-c", yqCommand)
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
log.Printf("stdout: %s", stdout.String())
log.Printf("stderr: %s", stderr.String())
log.Fatalf("failed to run yq command: %s", err)
}
fmt.Printf("YAML file (%s) job %s updated successfully\n", testPath, jobName)
}
func main() {
tests := findTests()
// Expand tests that should be split into multiple jobs
expandedTests := expandTests(tests)
quotedTests := make([]string, len(expandedTests))
for i, test := range expandedTests {
quotedTests[i] = fmt.Sprintf("\"%s\"", test)
}
// Define selected tests for PostgreSQL
postgresTestNames := []string{
"TestACLAllowUserDst",
"TestPingAllByIP",
"TestEphemeral2006DeletedTooQuickly",
"TestPingAllByIPManyUpDown",
"TestSubnetRouterMultiNetwork",
}
quotedPostgresTests := make([]string, len(postgresTestNames))
for i, test := range postgresTestNames {
quotedPostgresTests[i] = fmt.Sprintf("\"%s\"", test)
}
// Update both SQLite and PostgreSQL job matrices
updateYAML(quotedTests, "sqlite", "./test-integration.yaml")
updateYAML(quotedPostgresTests, "postgres", "./test-integration.yaml")
}

View File

@@ -0,0 +1,23 @@
name: GitHub Actions Version Updater
on:
schedule:
# Automatically run on every Sunday
- cron: "0 0 * * 0"
jobs:
build:
if: github.repository == 'juanfont/headscale'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# [Required] Access token with `workflow` scope.
token: ${{ secrets.WORKFLOW_SECRET }}
- name: Run GitHub Actions Version Updater
uses: saadmk11/github-actions-version-updater@d8781caf11d11168579c8e5e94f62b068038f442 # v0.9.0
with:
# [Required] Access token with `workflow` scope.
token: ${{ secrets.WORKFLOW_SECRET }}

View File

@@ -0,0 +1,112 @@
name: Integration Test Template
on:
workflow_call:
inputs:
test:
required: true
type: string
postgres_flag:
required: false
type: string
default: ""
database_name:
required: true
type: string
jobs:
test:
runs-on: ubuntu-latest
env:
# Github does not allow us to access secrets in pull requests,
# so this env var is used to check if we have the secret or not.
# If we have the secrets, meaning we are running on push in a fork,
# there might be secrets available for more debugging.
# If TS_OAUTH_CLIENT_ID and TS_OAUTH_SECRET is set, then the job
# will join a debug tailscale network, set up SSH and a tmux session.
# The SSH will be configured to use the SSH key of the Github user
# that triggered the build.
HAS_TAILSCALE_SECRET: ${{ secrets.TS_OAUTH_CLIENT_ID }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Tailscale
if: ${{ env.HAS_TAILSCALE_SECRET }}
uses: tailscale/github-action@a392da0a182bba0e9613b6243ebd69529b1878aa # v4.1.0
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_OAUTH_SECRET }}
tags: tag:gh
- name: Setup SSH server for Actor
if: ${{ env.HAS_TAILSCALE_SECRET }}
uses: alexellis/setup-sshd-actor@master
- name: Download headscale image
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: headscale-image
path: /tmp/artifacts
- name: Download tailscale HEAD image
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: tailscale-head-image
path: /tmp/artifacts
- name: Download hi binary
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: hi-binary
path: /tmp/artifacts
- name: Download Go cache
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: go-cache
path: /tmp/artifacts
- name: Download postgres image
if: ${{ inputs.postgres_flag == '--postgres=1' }}
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: postgres-image
path: /tmp/artifacts
- name: Load Docker images, Go cache, and prepare binary
run: |
gunzip -c /tmp/artifacts/headscale-image.tar.gz | docker load
gunzip -c /tmp/artifacts/tailscale-head-image.tar.gz | docker load
if [ -f /tmp/artifacts/postgres-image.tar.gz ]; then
gunzip -c /tmp/artifacts/postgres-image.tar.gz | docker load
fi
chmod +x /tmp/artifacts/hi
docker images
# Extract Go cache to host directories for bind mounting
mkdir -p /tmp/go-cache
tar -xzf /tmp/artifacts/go-cache.tar.gz -C /tmp/go-cache
ls -la /tmp/go-cache/ /tmp/go-cache/.cache/
- name: Run Integration Test
env:
HEADSCALE_INTEGRATION_HEADSCALE_IMAGE: headscale:${{ github.sha }}
HEADSCALE_INTEGRATION_TAILSCALE_IMAGE: tailscale-head:${{ github.sha }}
HEADSCALE_INTEGRATION_POSTGRES_IMAGE: ${{ inputs.postgres_flag == '--postgres=1' && format('postgres:{0}', github.sha) || '' }}
HEADSCALE_INTEGRATION_GO_CACHE: /tmp/go-cache/go
HEADSCALE_INTEGRATION_GO_BUILD_CACHE: /tmp/go-cache/.cache/go-build
run: /tmp/artifacts/hi run --stats --ts-memory-limit=300 --hs-memory-limit=1500 "^${{ inputs.test }}$" \
--timeout=120m \
${{ inputs.postgres_flag }}
# Sanitize test name for artifact upload (replace invalid characters: " : < > | * ? \ / with -)
- name: Sanitize test name for artifacts
if: always()
id: sanitize
run: echo "name=${TEST_NAME//[\":<>|*?\\\/]/-}" >> $GITHUB_OUTPUT
env:
TEST_NAME: ${{ inputs.test }}
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: always()
with:
name: ${{ inputs.database_name }}-${{ steps.sanitize.outputs.name }}-logs
path: "control_logs/*/*.log"
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: always()
with:
name: ${{ inputs.database_name }}-${{ steps.sanitize.outputs.name }}-artifacts
path: control_logs/
- name: Setup a blocking tmux session
if: ${{ env.HAS_TAILSCALE_SECRET }}
uses: alexellis/block-with-tmux-action@master

View File

@@ -1,39 +1,93 @@
name: CI
name: Lint
on: [push, pull_request]
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
# The "build" workflow
lint:
# The type of runner that the job will run on
golangci-lint:
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Install and run golangci-lint as a separate step, it's much faster this
# way because this action has caching. It'll get run again in `make lint`
# below, but it's still much faster in the end than installing
# golangci-lint manually in the `Run lint` step.
- uses: golangci/golangci-lint-action@v2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
args: --timeout 5m
# Setup Go
- name: Setup Go
uses: actions/setup-go@v2
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
go-version: "1.16.3" # The Go version to download (if necessary) and use.
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- 'integration_test/'
- 'config-example.yaml'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
# Install all the dependencies
- name: Install dependencies
run: |
go version
go install golang.org/x/lint/golint@latest
sudo apt update
sudo apt install -y make
- name: golangci-lint
if: steps.changed-files.outputs.files == 'true'
run: nix develop --command -- golangci-lint run
--new-from-rev=${{github.event.pull_request.base.sha}}
--output.text.path=stdout
--output.text.print-linter-name
--output.text.print-issued-lines
--output.text.colors
- name: Run lint
run: make lint
prettier-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- '**/*.md'
- '**/*.yml'
- '**/*.yaml'
- '**/*.ts'
- '**/*.js'
- '**/*.sass'
- '**/*.css'
- '**/*.scss'
- '**/*.html'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Prettify code
if: steps.changed-files.outputs.files == 'true'
run: nix develop --command -- prettier --no-error-on-unmatched-pattern
--ignore-unknown --check **/*.{ts,js,md,yaml,yml,sass,css,scss,html}
proto-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Buf lint
run: nix develop --command -- buf lint proto

55
.github/workflows/nix-module-test.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: NixOS Module Tests
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
nix-module-check:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
nix:
- 'nix/**'
- 'flake.nix'
- 'flake.lock'
go:
- 'go.*'
- '**/*.go'
- 'cmd/**'
- 'hscontrol/**'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.nix == 'true' || steps.changed-files.outputs.go == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.nix == 'true' || steps.changed-files.outputs.go == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Run NixOS module tests
if: steps.changed-files.outputs.nix == 'true' || steps.changed-files.outputs.go == 'true'
run: |
echo "Running NixOS module integration test..."
nix build .#checks.x86_64-linux.headscale -L

View File

@@ -1,77 +1,43 @@
---
name: release
name: Release
on:
push:
tags:
- "*" # triggers only if push new tag version
- "*" # triggers only if push new tag version
workflow_dispatch:
jobs:
goreleaser:
runs-on: ubuntu-18.04 # due to CGO we need to user an older version
steps:
-
name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker-release:
if: github.repository == 'juanfont/headscale'
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
-
name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
# list of Docker images to use as base name for tags
images: |
${{ secrets.DOCKERHUB_USERNAME }}/headscale
ghcr.io/${{ github.repository_owner }}/headscale
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
-
name: Login to DockerHub
uses: docker/login-action@v1
- name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GHCR
uses: docker/login-action@v1
- name: Login to GHCR
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v2
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
with:
push: true
context: .
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Run goreleaser
run: nix develop --command -- goreleaser release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

27
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Close inactive issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
if: github.repository == 'juanfont/headscale'
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
with:
days-before-issue-stale: 90
days-before-issue-close: 7
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 90 days with no
activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days
since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
exempt-issue-labels: "no-stale-bot"
repo-token: ${{ secrets.GITHUB_TOKEN }}

264
.github/workflows/test-integration.yaml vendored Normal file
View File

@@ -0,0 +1,264 @@
name: integration
# To debug locally on a branch, and when needing secrets
# change this to include `push` so the build is ran on
# the main repository.
on: [pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
# build: Builds binaries and Docker images once, uploads as artifacts for reuse.
# build-postgres: Pulls postgres image separately to avoid Docker Hub rate limits.
# sqlite: Runs all integration tests with SQLite backend.
# postgres: Runs a subset of tests with PostgreSQL to verify database compatibility.
build:
runs-on: ubuntu-latest
outputs:
files-changed: ${{ steps.changed-files.outputs.files }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 2
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- 'integration/**'
- 'config-example.yaml'
- '.github/workflows/test-integration.yaml'
- '.github/workflows/integration-test-template.yml'
- 'Dockerfile.*'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Build binaries and warm Go cache
if: steps.changed-files.outputs.files == 'true'
run: |
# Build all Go binaries in one nix shell to maximize cache reuse
nix develop --command -- bash -c '
go build -o hi ./cmd/hi
CGO_ENABLED=0 GOOS=linux go build -o headscale ./cmd/headscale
# Build integration test binary to warm the cache with all dependencies
go test -c ./integration -o /dev/null 2>/dev/null || true
'
- name: Upload hi binary
if: steps.changed-files.outputs.files == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: hi-binary
path: hi
retention-days: 10
- name: Package Go cache
if: steps.changed-files.outputs.files == 'true'
run: |
# Package Go module cache and build cache
tar -czf go-cache.tar.gz -C ~ go .cache/go-build
- name: Upload Go cache
if: steps.changed-files.outputs.files == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: go-cache
path: go-cache.tar.gz
retention-days: 10
- name: Build headscale image
if: steps.changed-files.outputs.files == 'true'
run: |
docker build \
--file Dockerfile.integration-ci \
--tag headscale:${{ github.sha }} \
.
docker save headscale:${{ github.sha }} | gzip > headscale-image.tar.gz
- name: Build tailscale HEAD image
if: steps.changed-files.outputs.files == 'true'
run: |
docker build \
--file Dockerfile.tailscale-HEAD \
--tag tailscale-head:${{ github.sha }} \
.
docker save tailscale-head:${{ github.sha }} | gzip > tailscale-head-image.tar.gz
- name: Upload headscale image
if: steps.changed-files.outputs.files == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: headscale-image
path: headscale-image.tar.gz
retention-days: 10
- name: Upload tailscale HEAD image
if: steps.changed-files.outputs.files == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: tailscale-head-image
path: tailscale-head-image.tar.gz
retention-days: 10
build-postgres:
runs-on: ubuntu-latest
needs: build
if: needs.build.outputs.files-changed == 'true'
steps:
- name: Pull and save postgres image
run: |
docker pull postgres:latest
docker tag postgres:latest postgres:${{ github.sha }}
docker save postgres:${{ github.sha }} | gzip > postgres-image.tar.gz
- name: Upload postgres image
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: postgres-image
path: postgres-image.tar.gz
retention-days: 10
sqlite:
needs: build
if: needs.build.outputs.files-changed == 'true'
strategy:
fail-fast: false
matrix:
test:
- TestACLHostsInNetMapTable
- TestACLAllowUser80Dst
- TestACLDenyAllPort80
- TestACLAllowUserDst
- TestACLAllowStarDst
- TestACLNamedHostsCanReachBySubnet
- TestACLNamedHostsCanReach
- TestACLDevice1CanAccessDevice2
- TestPolicyUpdateWhileRunningWithCLIInDatabase
- TestACLAutogroupMember
- TestACLAutogroupTagged
- TestACLAutogroupSelf
- TestACLPolicyPropagationOverTime
- TestACLTagPropagation
- TestACLTagPropagationPortSpecific
- TestAPIAuthenticationBypass
- TestAPIAuthenticationBypassCurl
- TestGRPCAuthenticationBypass
- TestCLIWithConfigAuthenticationBypass
- TestAuthKeyLogoutAndReloginSameUser
- TestAuthKeyLogoutAndReloginNewUser
- TestAuthKeyLogoutAndReloginSameUserExpiredKey
- TestAuthKeyDeleteKey
- TestAuthKeyLogoutAndReloginRoutesPreserved
- TestOIDCAuthenticationPingAll
- TestOIDCExpireNodesBasedOnTokenExpiry
- TestOIDC024UserCreation
- TestOIDCAuthenticationWithPKCE
- TestOIDCReloginSameNodeNewUser
- TestOIDCFollowUpUrl
- TestOIDCMultipleOpenedLoginUrls
- TestOIDCReloginSameNodeSameUser
- TestOIDCExpiryAfterRestart
- TestOIDCACLPolicyOnJoin
- TestOIDCReloginSameUserRoutesPreserved
- TestAuthWebFlowAuthenticationPingAll
- TestAuthWebFlowLogoutAndReloginSameUser
- TestAuthWebFlowLogoutAndReloginNewUser
- TestUserCommand
- TestPreAuthKeyCommand
- TestPreAuthKeyCommandWithoutExpiry
- TestPreAuthKeyCommandReusableEphemeral
- TestPreAuthKeyCorrectUserLoggedInCommand
- TestTaggedNodesCLIOutput
- TestApiKeyCommand
- TestNodeCommand
- TestNodeExpireCommand
- TestNodeRenameCommand
- TestPolicyCommand
- TestPolicyBrokenConfigCommand
- TestDERPVerifyEndpoint
- TestResolveMagicDNS
- TestResolveMagicDNSExtraRecordsPath
- TestDERPServerScenario
- TestDERPServerWebsocketScenario
- TestPingAllByIP
- TestPingAllByIPPublicDERP
- TestEphemeral
- TestEphemeralInAlternateTimezone
- TestEphemeral2006DeletedTooQuickly
- TestPingAllByHostname
- TestTaildrop
- TestUpdateHostnameFromClient
- TestExpireNode
- TestSetNodeExpiryInFuture
- TestNodeOnlineStatus
- TestPingAllByIPManyUpDown
- Test2118DeletingOnlineNodePanics
- TestEnablingRoutes
- TestHASubnetRouterFailover
- TestSubnetRouteACL
- TestEnablingExitRoutes
- TestSubnetRouterMultiNetwork
- TestSubnetRouterMultiNetworkExitNode
- TestAutoApproveMultiNetwork/authkey-tag.*
- TestAutoApproveMultiNetwork/authkey-user.*
- TestAutoApproveMultiNetwork/authkey-group.*
- TestAutoApproveMultiNetwork/webauth-tag.*
- TestAutoApproveMultiNetwork/webauth-user.*
- TestAutoApproveMultiNetwork/webauth-group.*
- TestSubnetRouteACLFiltering
- TestHeadscale
- TestTailscaleNodesJoiningHeadcale
- TestSSHOneUserToAll
- TestSSHMultipleUsersAllToAll
- TestSSHNoSSHConfigured
- TestSSHIsBlockedInACL
- TestSSHUserOnlyIsolation
- TestSSHAutogroupSelf
- TestTagsAuthKeyWithTagRequestDifferentTag
- TestTagsAuthKeyWithTagNoAdvertiseFlag
- TestTagsAuthKeyWithTagCannotAddViaCLI
- TestTagsAuthKeyWithTagCannotChangeViaCLI
- TestTagsAuthKeyWithTagAdminOverrideReauthPreserves
- TestTagsAuthKeyWithTagCLICannotModifyAdminTags
- TestTagsAuthKeyWithoutTagCannotRequestTags
- TestTagsAuthKeyWithoutTagRegisterNoTags
- TestTagsAuthKeyWithoutTagCannotAddViaCLI
- TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset
- TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise
- TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag
- TestTagsUserLoginOwnedTagAtRegistration
- TestTagsUserLoginNonExistentTagAtRegistration
- TestTagsUserLoginUnownedTagAtRegistration
- TestTagsUserLoginAddTagViaCLIReauth
- TestTagsUserLoginRemoveTagViaCLIReauth
- TestTagsUserLoginCLINoOpAfterAdminAssignment
- TestTagsUserLoginCLICannotRemoveAdminTags
- TestTagsAuthKeyWithTagRequestNonExistentTag
- TestTagsAuthKeyWithTagRequestUnownedTag
- TestTagsAuthKeyWithoutTagRequestNonExistentTag
- TestTagsAuthKeyWithoutTagRequestUnownedTag
- TestTagsAdminAPICannotSetNonExistentTag
- TestTagsAdminAPICanSetUnownedTag
- TestTagsAdminAPICannotRemoveAllTags
- TestTagsAdminAPICannotSetInvalidFormat
uses: ./.github/workflows/integration-test-template.yml
secrets: inherit
with:
test: ${{ matrix.test }}
postgres_flag: "--postgres=0"
database_name: "sqlite"
postgres:
needs: [build, build-postgres]
if: needs.build.outputs.files-changed == 'true'
strategy:
fail-fast: false
matrix:
test:
- TestACLAllowUserDst
- TestPingAllByIP
- TestEphemeral2006DeletedTooQuickly
- TestPingAllByIPManyUpDown
- TestSubnetRouterMultiNetwork
uses: ./.github/workflows/integration-test-template.yml
secrets: inherit
with:
test: ${{ matrix.test }}
postgres_flag: "--postgres=1"
database_name: "postgres"

View File

@@ -1,23 +0,0 @@
name: CI
on: [pull_request]
jobs:
# The "build" workflow
integration-test:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Setup Go
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: "1.16.3"
- name: Run Integration tests
run: go test -tags integration -timeout 30m

View File

@@ -1,33 +1,47 @@
name: CI
name: Tests
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-$${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
# The "build" workflow
test:
# The type of runner that the job will run on
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
# Setup Go
- name: Setup Go
uses: actions/setup-go@v2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
go-version: "1.16.3" # The Go version to download (if necessary) and use.
fetch-depth: 2
# Install all the dependencies
- name: Install dependencies
run: |
go version
sudo apt update
sudo apt install -y make
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
files:
- '*.nix'
- 'go.*'
- '**/*.go'
- 'integration_test/'
- 'config-example.yaml'
- uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
if: steps.changed-files.outputs.files == 'true'
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
if: steps.changed-files.outputs.files == 'true'
with:
primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix',
'**/flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }}
- name: Run tests
run: make test
- name: Run build
run: make
if: steps.changed-files.outputs.files == 'true'
env:
# As of 2025-01-06, these env vars was not automatically
# set anymore which breaks the initdb for postgres on
# some of the database migration tests.
LC_ALL: "en_US.UTF-8"
LC_CTYPE: "en_US.UTF-8"
run: nix develop --command -- gotestsum

19
.github/workflows/update-flake.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: update-flake-lock
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: "0 0 * * 0" # runs weekly on Sunday at 00:00
jobs:
lockfile:
if: github.repository == 'juanfont/headscale'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@21a544727d0c62386e78b4befe52d19ad12692e3 # v17
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@428c2b58a4b7414dabd372acb6a03dba1084d3ab # v25
with:
pr-title: "Update flake.lock"

36
.gitignore vendored
View File

@@ -1,3 +1,11 @@
ignored/
tailscale/
.vscode/
.claude/
logs/
*.prof
# Binaries for programs and plugins
*.exe
*.exe~
@@ -12,10 +20,15 @@
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
vendor/
dist/
/headscale
config.json
config.yaml
config*.yaml
!config-example.yaml
derp.yaml
*.hujson
*.key
/db.sqlite
*.sqlite3
@@ -23,4 +36,21 @@ config.json
# Exclude Jetbrains Editors
.idea
test_output/
test_output/
control_logs/
# Nix build output
result
.direnv/
integration_test/etc/config.dump.yaml
# MkDocs
.cache
/site
__debug_bin
node_modules/
package-lock.json
package.json

90
.golangci.yaml Normal file
View File

@@ -0,0 +1,90 @@
---
version: "2"
linters:
default: all
disable:
- cyclop
- depguard
- dupl
- exhaustruct
- funcorder
- funlen
- gochecknoglobals
- gochecknoinits
- gocognit
- godox
- interfacebloat
- ireturn
- lll
- maintidx
- makezero
- musttag
- nestif
- nolintlint
- paralleltest
- revive
- tagliatelle
- testpackage
- varnamelen
- wrapcheck
- wsl
settings:
forbidigo:
forbid:
# Forbid time.Sleep everywhere with context-appropriate alternatives
- pattern: 'time\.Sleep'
msg: >-
time.Sleep is forbidden.
In tests: use assert.EventuallyWithT for polling/waiting patterns.
In production code: use a backoff strategy (e.g., cenkalti/backoff) or proper synchronization primitives.
analyze-types: true
gocritic:
disabled-checks:
- appendAssign
- ifElseChain
nlreturn:
block-size: 4
varnamelen:
ignore-names:
- err
- db
- id
- ip
- ok
- c
- tt
- tx
- rx
- sb
- wg
- pr
- p
- p2
ignore-type-assert-ok: true
ignore-map-index-ok: true
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
- gen
formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
- gen

View File

@@ -1,87 +1,187 @@
# This is an example .goreleaser.yml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
---
version: 2
before:
hooks:
- go mod tidy
- go mod tidy -compat=1.25
- go mod vendor
release:
prerelease: auto
draft: true
header: |
## Upgrade
Please follow the steps outlined in the [upgrade guide](https://headscale.net/stable/setup/upgrade/) to update your existing Headscale installation.
**It's best to update from one stable version to the next** (e.g., 0.24.0 → 0.25.1 → 0.26.1) in case you are multiple releases behind. You should always pick the latest available patch release.
Be sure to check the changelog above for version-specific upgrade instructions and breaking changes.
### Backup Your Database
**Always backup your database before upgrading.** Here's how to backup a SQLite database:
```bash
# Stop headscale
systemctl stop headscale
# Backup sqlite database
cp /var/lib/headscale/db.sqlite /var/lib/headscale/db.sqlite.backup
# Backup sqlite WAL/SHM files (if they exist)
cp /var/lib/headscale/db.sqlite-wal /var/lib/headscale/db.sqlite-wal.backup
cp /var/lib/headscale/db.sqlite-shm /var/lib/headscale/db.sqlite-shm.backup
# Start headscale (migration will run automatically)
systemctl start headscale
```
builds:
- id: darwin-amd64
main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}'
goos:
- darwin
goarch:
- amd64
- id: headscale
main: ./cmd/headscale
mod_timestamp: "{{ .CommitTimestamp }}"
env:
- PKG_CONFIG_SYSROOT_DIR=/sysroot/macos/amd64
- PKG_CONFIG_PATH=/sysroot/macos/amd64/usr/local/lib/pkgconfig
- CC=o64-clang
- CXX=o64-clang++
- CGO_ENABLED=0
targets:
- darwin_amd64
- darwin_arm64
- freebsd_amd64
- linux_amd64
- linux_arm64
flags:
- -mod=readonly
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
- id: linux-armhf
main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}'
goos:
- linux
goarch:
- arm
goarm:
- 7
env:
- CC=arm-linux-gnueabihf-gcc
- CXX=arm-linux-gnueabihf-g++
- CGO_FLAGS=--sysroot=/sysroot/linux/armhf
- CGO_LDFLAGS=--sysroot=/sysroot/linux/armhf
- PKG_CONFIG_SYSROOT_DIR=/sysroot/linux/armhf
- PKG_CONFIG_PATH=/sysroot/linux/armhf/opt/vc/lib/pkgconfig:/sysroot/linux/armhf/usr/lib/arm-linux-gnueabihf/pkgconfig:/sysroot/linux/armhf/usr/lib/pkgconfig:/sysroot/linux/armhf/usr/local/lib/pkgconfig
flags:
- -mod=readonly
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
- id: linux-amd64
env:
- CGO_ENABLED=1
goos:
- linux
goarch:
- amd64
main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}'
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
- id: linux-arm64
goos:
- linux
goarch:
- arm64
main: ./cmd/headscale/headscale.go
mod_timestamp: '{{ .CommitTimestamp }}'
ldflags:
- -s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=v{{.Version}}
tags:
- ts2019
archives:
- id: golang-cross
builds:
- darwin-amd64
- linux-armhf
- linux-amd64
- linux-arm64
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format: binary
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
formats:
- binary
source:
enabled: true
name_template: "{{ .ProjectName }}_{{ .Version }}"
format: tar.gz
files:
- "vendor/"
nfpms:
# Configure nFPM for .deb and .rpm releases
#
# See https://nfpm.goreleaser.com/configuration/
# and https://goreleaser.com/customization/nfpm/
#
# Useful tools for debugging .debs:
# List file contents: dpkg -c dist/headscale...deb
# Package metadata: dpkg --info dist/headscale....deb
#
- ids:
- headscale
package_name: headscale
priority: optional
vendor: headscale
maintainer: Kristoffer Dalby <kristoffer@dalby.cc>
homepage: https://github.com/juanfont/headscale
description: |-
Open source implementation of the Tailscale control server.
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 narrow scope, a single Tailscale network (tailnet),
suitable for a personal use, or a small open-source organisation.
bindir: /usr/bin
section: net
formats:
- deb
contents:
- src: ./config-example.yaml
dst: /etc/headscale/config.yaml
type: config|noreplace
file_info:
mode: 0644
- src: ./packaging/systemd/headscale.service
dst: /usr/lib/systemd/system/headscale.service
- dst: /var/lib/headscale
type: dir
- src: LICENSE
dst: /usr/share/doc/headscale/copyright
scripts:
postinstall: ./packaging/deb/postinst
postremove: ./packaging/deb/postrm
preremove: ./packaging/deb/prerm
deb:
lintian_overrides:
- no-changelog # Our CHANGELOG.md uses a different formatting
- no-manual-page
- statically-linked-binary
kos:
- id: ghcr
repositories:
- ghcr.io/juanfont/headscale
- headscale/headscale
# bare tells KO to only use the repository
# for tagging and naming the container.
bare: true
base_image: gcr.io/distroless/base-debian13
build: headscale
main: ./cmd/headscale
env:
- CGO_ENABLED=0
platforms:
- linux/amd64
- linux/arm64
tags:
- "{{ if not .Prerelease }}latest{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}.{{ .Minor }}.{{ .Patch }}{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}.{{ .Minor }}{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}.{{ .Patch }}{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}{{ end }}"
- "{{ if not .Prerelease }}stable{{ else }}unstable{{ end }}"
- "{{ .Tag }}"
- '{{ trimprefix .Tag "v" }}'
- "sha-{{ .ShortCommit }}"
creation_time: "{{.CommitTimestamp}}"
ko_data_creation_time: "{{.CommitTimestamp}}"
- id: ghcr-debug
repositories:
- ghcr.io/juanfont/headscale
- headscale/headscale
bare: true
base_image: gcr.io/distroless/base-debian13:debug
build: headscale
main: ./cmd/headscale
env:
- CGO_ENABLED=0
platforms:
- linux/amd64
- linux/arm64
tags:
- "{{ if not .Prerelease }}latest-debug{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}.{{ .Minor }}.{{ .Patch }}-debug{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}.{{ .Minor }}-debug{{ end }}"
- "{{ if not .Prerelease }}{{ .Major }}-debug{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}.{{ .Patch }}-debug{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}-debug{{ end }}"
- "{{ if not .Prerelease }}v{{ .Major }}-debug{{ end }}"
- "{{ if not .Prerelease }}stable-debug{{ else }}unstable-debug{{ end }}"
- "{{ .Tag }}-debug"
- '{{ trimprefix .Tag "v" }}-debug'
- "sha-{{ .ShortCommit }}-debug"
checksum:
name_template: 'checksums.txt'
name_template: "checksums.txt"
snapshot:
name_template: "{{ .Tag }}-next"
version_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- "^docs:"
- "^test:"

34
.mcp.json Normal file
View File

@@ -0,0 +1,34 @@
{
"mcpServers": {
"claude-code-mcp": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@steipete/claude-code-mcp@latest"],
"env": {}
},
"sequential-thinking": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"],
"env": {}
},
"nixos": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-nixos"],
"env": {}
},
"context7": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@upstash/context7-mcp"],
"env": {}
},
"git": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/git-mcp-server"],
"env": {}
}
}
}

68
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,68 @@
# prek/pre-commit configuration for headscale
# See: https://prek.j178.dev/quickstart/
# See: https://prek.j178.dev/builtin/
# Global exclusions - ignore generated code
exclude: ^gen/
repos:
# Built-in hooks from pre-commit/pre-commit-hooks
# prek will use fast-path optimized versions automatically
# See: https://prek.j178.dev/builtin/
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-json
- id: check-merge-conflict
- id: check-symlinks
- id: check-toml
- id: check-xml
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: fix-byte-order-marker
- id: mixed-line-ending
- id: trailing-whitespace
# Local hooks for project-specific tooling
- repo: local
hooks:
# nixpkgs-fmt for Nix files
- id: nixpkgs-fmt
name: nixpkgs-fmt
entry: nixpkgs-fmt
language: system
files: \.nix$
# Prettier for formatting
- id: prettier
name: prettier
entry: prettier --write --list-different
language: system
exclude: ^docs/
types_or:
[
javascript,
jsx,
ts,
tsx,
yaml,
json,
toml,
html,
css,
scss,
sass,
markdown,
]
# golangci-lint for Go code quality
- id: golangci-lint
name: golangci-lint
entry: nix develop --command golangci-lint run --new-from-rev=HEAD~1 --timeout=5m --fix
language: system
types: [go]
pass_filenames: false

5
.prettierignore Normal file
View File

@@ -0,0 +1,5 @@
.github/workflows/test-integration-v2*
docs/about/features.md
docs/ref/api.md
docs/ref/configuration.md
docs/ref/oidc.md

1051
AGENTS.md Normal file

File diff suppressed because it is too large Load Diff

1105
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

1
CLAUDE.md Normal file
View File

@@ -0,0 +1 @@
@AGENTS.md

134
CODE_OF_CONDUCT.md Normal file
View File

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

34
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,34 @@
# Contributing
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the maintainers before being added to the project.
This model has been chosen to reduce the risk of burnout by limiting the maintenance overhead of reviewing and validating third-party code.
## Why do we have this model?
Headscale has a small maintainer team that tries to balance working on the project, fixing bugs and reviewing contributions.
When we work on issues ourselves, we develop first hand knowledge of the code and it makes it possible for us to maintain and own the code as the project develops.
Code contributions are seen as a positive thing. People enjoy and engage with our project, but it also comes with some challenges; we have to understand the code, we have to understand the feature, we might have to become familiar with external libraries or services and we think about security implications. All those steps are required during the reviewing process. After the code has been merged, the feature has to be maintained. Any changes reliant on external services must be updated and expanded accordingly.
The review and day-1 maintenance adds a significant burden on the maintainers. Often we hope that the contributor will help out, but we found that most of the time, they disappear after their new feature was added.
This means that when someone contributes, we are mostly happy about it, but we do have to run it through a series of checks to establish if we actually can maintain this feature.
## What do we require?
A general description is provided here and an explicit list is provided in our pull request template.
All new features have to start out with a design document, which should be discussed on the issue tracker (not discord). It should include a use case for the feature, how it can be implemented, who will implement it and a plan for maintaining it.
All features have to be end-to-end tested (integration tests) and have good unit test coverage to ensure that they work as expected. This will also ensure that the feature continues to work as expected over time. If a change cannot be tested, a strong case for why this is not possible needs to be presented.
The contributor should help to maintain the feature over time. In case the feature is not maintained probably, the maintainers reserve themselves the right to remove features they redeem as unmaintainable. This should help to improve the quality of the software and keep it in a maintainable state.
## Bug fixes
Headscale is open to code contributions for bug fixes without discussion.
## Documentation
If you find mistakes in the documentation, please submit a fix to the documentation.

View File

@@ -1,19 +0,0 @@
FROM golang:1.17.1-bullseye AS build
ENV GOPATH /go
COPY go.mod go.sum /go/src/headscale/
WORKDIR /go/src/headscale
RUN go mod download
COPY . /go/src/headscale
RUN go install -a -ldflags="-extldflags=-static" -tags netgo,sqlite_omit_load_extension ./cmd/headscale
RUN test -e /go/bin/headscale
FROM ubuntu:20.04
COPY --from=build /go/bin/headscale /usr/local/bin/headscale
ENV TZ UTC
EXPOSE 8080/tcp
CMD ["headscale"]

19
Dockerfile.derper Normal file
View File

@@ -0,0 +1,19 @@
# For testing purposes only
FROM golang:alpine AS build-env
WORKDIR /go/src
RUN apk add --no-cache git
ARG VERSION_BRANCH=main
RUN git clone https://github.com/tailscale/tailscale.git --branch=$VERSION_BRANCH --depth=1
WORKDIR /go/src/tailscale
ARG TARGETARCH
RUN GOARCH=$TARGETARCH go install -v ./cmd/derper
FROM alpine:3.22
RUN apk add --no-cache ca-certificates iptables iproute2 ip6tables curl
COPY --from=build-env /go/bin/* /usr/local/bin/
ENTRYPOINT [ "/usr/local/bin/derper" ]

44
Dockerfile.integration Normal file
View File

@@ -0,0 +1,44 @@
# This Dockerfile and the images produced are for testing headscale,
# and are in no way endorsed by Headscale's maintainers as an
# official nor supported release or distribution.
FROM docker.io/golang:1.25-trixie AS builder
ARG VERSION=dev
ENV GOPATH /go
WORKDIR /go/src/headscale
# Install delve debugger first - rarely changes, good cache candidate
RUN go install github.com/go-delve/delve/cmd/dlv@latest
# Download dependencies - only invalidated when go.mod/go.sum change
COPY go.mod go.sum /go/src/headscale/
RUN go mod download
# Copy source and build - invalidated on any source change
COPY . .
# Build debug binary with debug symbols for delve
RUN CGO_ENABLED=0 GOOS=linux go build -gcflags="all=-N -l" -o /go/bin/headscale ./cmd/headscale
# Runtime stage
FROM debian:trixie-slim
RUN apt-get --update install --no-install-recommends --yes \
bash ca-certificates curl dnsutils findutils iproute2 jq less procps python3 sqlite3 \
&& apt-get dist-clean
RUN mkdir -p /var/run/headscale
# Copy binaries from builder
COPY --from=builder /go/bin/headscale /usr/local/bin/headscale
COPY --from=builder /go/bin/dlv /usr/local/bin/dlv
# Copy source code for delve source-level debugging
COPY --from=builder /go/src/headscale /go/src/headscale
WORKDIR /go/src/headscale
# Need to reset the entrypoint or everything will run as a busybox script
ENTRYPOINT []
EXPOSE 8080/tcp 40000/tcp
CMD ["dlv", "--listen=0.0.0.0:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/usr/local/bin/headscale", "--"]

17
Dockerfile.integration-ci Normal file
View File

@@ -0,0 +1,17 @@
# Minimal CI image - expects pre-built headscale binary in build context
# For local development with delve debugging, use Dockerfile.integration instead
FROM debian:trixie-slim
RUN apt-get --update install --no-install-recommends --yes \
bash ca-certificates curl dnsutils findutils iproute2 jq less procps python3 sqlite3 \
&& apt-get dist-clean
RUN mkdir -p /var/run/headscale
# Copy pre-built headscale binary from build context
COPY headscale /usr/local/bin/headscale
ENTRYPOINT []
EXPOSE 8080/tcp
CMD ["/usr/local/bin/headscale"]

View File

@@ -1,11 +0,0 @@
FROM ubuntu:latest
ARG TAILSCALE_VERSION
RUN apt-get update \
&& apt-get install -y gnupg curl \
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
&& apt-get update \
&& apt-get install -y tailscale=${TAILSCALE_VERSION} \
&& rm -rf /var/lib/apt/lists/*

47
Dockerfile.tailscale-HEAD Normal file
View File

@@ -0,0 +1,47 @@
# Copyright (c) Tailscale Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause
# This Dockerfile is more or less lifted from tailscale/tailscale
# to ensure a similar build process when testing the HEAD of tailscale.
FROM golang:1.25-alpine AS build-env
WORKDIR /go/src
RUN apk add --no-cache git
# Replace `RUN git...` with `COPY` and a local checked out version of Tailscale in `./tailscale`
# to test specific commits of the Tailscale client. This is useful when trying to find out why
# something specific broke between two versions of Tailscale with for example `git bisect`.
# COPY ./tailscale .
RUN git clone https://github.com/tailscale/tailscale.git
WORKDIR /go/src/tailscale
# see build_docker.sh
ARG VERSION_LONG=""
ENV VERSION_LONG=$VERSION_LONG
ARG VERSION_SHORT=""
ENV VERSION_SHORT=$VERSION_SHORT
ARG VERSION_GIT_HASH=""
ENV VERSION_GIT_HASH=$VERSION_GIT_HASH
ARG TARGETARCH
ARG BUILD_TAGS=""
RUN GOARCH=$TARGETARCH go install -tags="${BUILD_TAGS}" -ldflags="\
-X tailscale.com/version.longStamp=$VERSION_LONG \
-X tailscale.com/version.shortStamp=$VERSION_SHORT \
-X tailscale.com/version.gitCommitStamp=$VERSION_GIT_HASH" \
-v ./cmd/tailscale ./cmd/tailscaled ./cmd/containerboot
FROM alpine:3.22
# Upstream: ca-certificates ip6tables iptables iproute2
# Tests: curl python3 (traceroute via BusyBox)
RUN apk add --no-cache ca-certificates curl ip6tables iptables iproute2 python3
COPY --from=build-env /go/bin/* /usr/local/bin/
# For compat with the previous run.sh, although ideally you should be
# using build_docker.sh which sets an entrypoint for the image.
RUN mkdir /tailscale && ln -s /usr/local/bin/containerboot /tailscale/run.sh

137
Makefile
View File

@@ -1,27 +1,128 @@
# Calculate version
version = $(shell ./scripts/version-at-commit.sh)
# Headscale Makefile
# Modern Makefile following best practices
build:
go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go
# Version calculation
VERSION ?= $(shell git describe --always --tags --dirty)
dev: lint test build
# Build configuration
GOOS ?= $(shell uname | tr '[:upper:]' '[:lower:]')
ifeq ($(filter $(GOOS), openbsd netbsd solaris plan9), )
PIE_FLAGS = -buildmode=pie
endif
test:
@go test -coverprofile=coverage.out ./...
# Tool availability check with nix warning
define check_tool
@command -v $(1) >/dev/null 2>&1 || { \
echo "Warning: $(1) not found. Run 'nix develop' to ensure all dependencies are available."; \
exit 1; \
}
endef
test_integration:
go test -tags integration -timeout 30m ./...
# Source file collections using shell find for better performance
GO_SOURCES := $(shell find . -name '*.go' -not -path './gen/*' -not -path './vendor/*')
PROTO_SOURCES := $(shell find . -name '*.proto' -not -path './gen/*' -not -path './vendor/*')
DOC_SOURCES := $(shell find . \( -name '*.md' -o -name '*.yaml' -o -name '*.yml' -o -name '*.ts' -o -name '*.js' -o -name '*.html' -o -name '*.css' -o -name '*.scss' -o -name '*.sass' \) -not -path './gen/*' -not -path './vendor/*' -not -path './node_modules/*')
coverprofile_func:
go tool cover -func=coverage.out
# Default target
.PHONY: all
all: lint test build
coverprofile_html:
go tool cover -html=coverage.out
# Dependency checking
.PHONY: check-deps
check-deps:
$(call check_tool,go)
$(call check_tool,golangci-lint)
$(call check_tool,gofumpt)
$(call check_tool,prettier)
$(call check_tool,clang-format)
$(call check_tool,buf)
lint:
golint
golangci-lint run --timeout 5m
# Build targets
.PHONY: build
build: check-deps $(GO_SOURCES) go.mod go.sum
@echo "Building headscale..."
go build $(PIE_FLAGS) -ldflags "-X main.version=$(VERSION)" -o headscale ./cmd/headscale
compress: build
upx --brute headscale
# Test targets
.PHONY: test
test: check-deps $(GO_SOURCES) go.mod go.sum
@echo "Running Go tests..."
go test -race ./...
# Formatting targets
.PHONY: fmt
fmt: fmt-go fmt-prettier fmt-proto
.PHONY: fmt-go
fmt-go: check-deps $(GO_SOURCES)
@echo "Formatting Go code..."
gofumpt -l -w .
golangci-lint run --fix
.PHONY: fmt-prettier
fmt-prettier: check-deps $(DOC_SOURCES)
@echo "Formatting documentation and config files..."
prettier --write '**/*.{ts,js,md,yaml,yml,sass,css,scss,html}'
.PHONY: fmt-proto
fmt-proto: check-deps $(PROTO_SOURCES)
@echo "Formatting Protocol Buffer files..."
clang-format -i $(PROTO_SOURCES)
# Linting targets
.PHONY: lint
lint: lint-go lint-proto
.PHONY: lint-go
lint-go: check-deps $(GO_SOURCES) go.mod go.sum
@echo "Linting Go code..."
golangci-lint run --timeout 10m
.PHONY: lint-proto
lint-proto: check-deps $(PROTO_SOURCES)
@echo "Linting Protocol Buffer files..."
cd proto/ && buf lint
# Code generation
.PHONY: generate
generate: check-deps
@echo "Generating code..."
go generate ./...
# Clean targets
.PHONY: clean
clean:
rm -rf headscale gen
# Development workflow
.PHONY: dev
dev: fmt lint test build
# Help target
.PHONY: help
help:
@echo "Headscale Development Makefile"
@echo ""
@echo "Main targets:"
@echo " all - Run lint, test, and build (default)"
@echo " build - Build headscale binary"
@echo " test - Run Go tests"
@echo " fmt - Format all code (Go, docs, proto)"
@echo " lint - Lint all code (Go, proto)"
@echo " generate - Generate code from Protocol Buffers"
@echo " dev - Full development workflow (fmt + lint + test + build)"
@echo " clean - Clean build artifacts"
@echo ""
@echo "Specific targets:"
@echo " fmt-go - Format Go code only"
@echo " fmt-prettier - Format documentation only"
@echo " fmt-proto - Format Protocol Buffer files only"
@echo " lint-go - Lint Go code only"
@echo " lint-proto - Lint Protocol Buffer files only"
@echo ""
@echo "Dependencies:"
@echo " check-deps - Verify required tools are available"
@echo ""
@echo "Note: If not running in a nix shell, ensure dependencies are available:"
@echo " nix develop"

397
README.md
View File

@@ -1,273 +1,174 @@
# headscale
![headscale logo](./docs/assets/logo/headscale3_header_stacked_left.png)
![ci](https://github.com/juanfont/headscale/actions/workflows/test.yml/badge.svg)
An open source, self-hosted implementation of the Tailscale coordination server.
An open source, self-hosted implementation of the Tailscale control server.
Join our [Discord](https://discord.gg/XcQxk2VHjx) server for a chat.
Join our [Discord server](https://discord.gg/c84AZQhmpx) for a chat.
## Overview
**Note:** Always select the same GitHub tag as the released version you use
to ensure you have the correct example configuration. The `main` branch might
contain unreleased changes. The documentation is available for stable and
development versions:
Tailscale is [a modern VPN](https://tailscale.com/) built on top of [Wireguard](https://www.wireguard.com/). It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/) between the computers of your networks - using all kinds of [NAT traversal sorcery](https://tailscale.com/blog/how-nat-traversal-works/).
- [Documentation for the stable version](https://headscale.net/stable/)
- [Documentation for the development version](https://headscale.net/development/)
Everything in Tailscale is Open Source, except the GUI clients for proprietary OS (Windows and macOS/iOS), and the 'coordination/control server'.
## What is Tailscale
The control server works as an exchange point of Wireguard public keys for the nodes in the Tailscale network. It also assigns the IP addresses of the clients, creates the boundaries between each user, enables sharing machines between users, and exposes the advertised routes of your nodes.
Tailscale is [a modern VPN](https://tailscale.com/) built on top of
[Wireguard](https://www.wireguard.com/).
It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/)
between the computers of your networks - using
[NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/).
Headscale implements this coordination server.
Everything in Tailscale is Open Source, except the GUI clients for proprietary OS
(Windows and macOS/iOS), and the control server.
## Status
The control server works as an exchange point of Wireguard public keys for the
nodes in the Tailscale network. It assigns the IP addresses of the clients,
creates the boundaries between each user, enables sharing machines between users,
and exposes the advertised routes of your nodes.
- [x] Base functionality (nodes can communicate with each other)
- [x] Node registration through the web flow
- [x] Network changes are relayed to the nodes
- [x] Namespace support (~equivalent to multi-user in Tailscale.com)
- [x] Routing (advertise & accept, including exit nodes)
- [x] Node registration via pre-auth keys (including reusable keys, and ephemeral node support)
- [x] JSON-formatted output
- [x] ACLs
- [x] Taildrop (File Sharing)
- [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10)
- [x] DNS (passing DNS servers to nodes)
- [x] Share nodes between ~~users~~ namespaces
- [x] MagicDNS (see `docs/`)
A [Tailscale network (tailnet)](https://tailscale.com/kb/1136/tailnet/) is private
network which Tailscale assigns to a user in terms of private users or an
organisation.
## Design goal
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_ Tailscale
network (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.
## Features
Please see ["Features" in the documentation](https://headscale.net/stable/about/features/).
## Client OS support
| OS | Supports headscale |
| ------- | ----------------------------------------------------------------------------------------------------------------- |
| Linux | Yes |
| OpenBSD | Yes |
| macOS | Yes (see `/apple` on your headscale for more information) |
| Windows | Yes |
| Android | [You need to compile the client yourself](https://github.com/juanfont/headscale/issues/58#issuecomment-885255270) |
| iOS | Not yet |
Please see ["Client and operating system support" in the documentation](https://headscale.net/stable/about/clients/).
## Roadmap 🤷
## Running headscale
Suggestions/PRs welcomed!
**Please note that we do not support nor encourage the use of reverse proxies
and container to run Headscale.**
## Running it
Please have a look at the [`documentation`](https://headscale.net/stable/).
1. Download the Headscale binary https://github.com/juanfont/headscale/releases, and place it somewhere in your PATH or use the docker container
For NixOS users, a module is available in [`nix/`](./nix/).
```shell
docker pull headscale/headscale:x.x.x
```
## Talks
<!--
or
```shell
docker pull ghrc.io/juanfont/headscale:x.x.x
``` -->
2. (Optional, you can also use SQLite) Get yourself a PostgreSQL DB running
```shell
docker run --name headscale -e POSTGRES_DB=headscale -e \
POSTGRES_USER=foo -e POSTGRES_PASSWORD=bar -p 5432:5432 -d postgres
```
3. Set some stuff up (headscale Wireguard keys & the config.json file)
```shell
wg genkey > private.key
wg pubkey < private.key > public.key # not needed
# Postgres
cp config.json.postgres.example config.json
# or
# SQLite
cp config.json.sqlite.example config.json
```
4. Create a namespace (a namespace is a 'tailnet', a group of Tailscale nodes that can talk to each other)
```shell
headscale namespaces create myfirstnamespace
```
or docker:
the db.sqlite mount is only needed if you use sqlite
```shell
touch db.sqlite
docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale namespaces create myfirstnamespace
```
or if your server is already running in docker:
```shell
docker exec <container_name> headscale create myfirstnamespace
```
5. Run the server
```shell
headscale serve
```
or docker:
the db.sqlite mount is only needed if you use sqlite
```shell
docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite -p 127.0.0.1:8080:8080 headscale/headscale:x.x.x headscale serve
```
6. If you used tailscale.com before in your nodes, make sure you clear the tailscald data folder
```shell
systemctl stop tailscaled
rm -fr /var/lib/tailscale
systemctl start tailscaled
```
7. Add your first machine
```shell
tailscale up --login-server YOUR_HEADSCALE_URL
```
8. Navigate to the URL you will get with `tailscale up`, where you'll find your machine key.
9. In the server, register your machine to a namespace with the CLI
```shell
headscale -n myfirstnamespace nodes register YOURMACHINEKEY
```
or docker:
```shell
docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v $(pwd)/derp.yaml:/derp.yaml headscale/headscale:x.x.x headscale -n myfirstnamespace nodes register YOURMACHINEKEY
```
or if your server is already running in docker:
```shell
docker exec <container_name> headscale -n myfirstnamespace nodes register YOURMACHINEKEY
```
Alternatively, you can use Auth Keys to register your machines:
1. Create an authkey
```shell
headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h
```
or docker:
```shell
docker run -v $(pwd)/private.key:/private.key -v $(pwd)/config.json:/config.json -v$(pwd)/derp.yaml:/derp.yaml -v $(pwd)/db.sqlite:/db.sqlite headscale/headscale:x.x.x headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h
```
or if your server is already running in docker:
```shell
docker exec <container_name> headscale -n myfirstnamespace preauthkeys create --reusable --expiration 24h
```
2. Use the authkey from your machine to register it
```shell
tailscale up --login-server YOUR_HEADSCALE_URL --authkey YOURAUTHKEY
```
If you create an authkey with the `--ephemeral` flag, that key will create ephemeral nodes. This implies that `--reusable` is true.
Please bear in mind that all the commands from headscale support adding `-o json` or `-o json-line` to get a nicely JSON-formatted output.
## Configuration reference
Headscale's configuration file is named `config.json` or `config.yaml`. Headscale will look for it in `/etc/headscale`, `~/.headscale` and finally the directory from where the Headscale binary is executed.
```
"server_url": "http://192.168.1.12:8080",
"listen_addr": "0.0.0.0:8080",
"ip_prefix": "100.64.0.0/10"
```
`server_url` is the external URL via which Headscale is reachable. `listen_addr` is the IP address and port the Headscale program should listen on. `ip_prefix` is the IP prefix (range) in which IP addresses for nodes will be allocated (default 100.64.0.0/10, e.g., 192.168.4.0/24, 10.0.0.0/8)
```
"log_level": "debug"
```
`log_level` can be used to set the Log level for Headscale, it defaults to `debug`, and the available levels are: `trace`, `debug`, `info`, `warn` and `error`.
```
"private_key_path": "private.key",
```
`private_key_path` is the path to the Wireguard private key. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
```
"derp_map_path": "derp.yaml",
```
`derp_map_path` is the path to the [DERP](https://pkg.go.dev/tailscale.com/derp) map file. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
```
"ephemeral_node_inactivity_timeout": "30m",
```
`ephemeral_node_inactivity_timeout` is the timeout after which inactive ephemeral node records will be deleted from the database. The default is 30 minutes. This value must be higher than 65 seconds (the keepalive timeout for the HTTP long poll is 60 seconds, plus a few seconds to avoid race conditions).
```
"db_host": "localhost",
"db_port": 5432,
"db_name": "headscale",
"db_user": "foo",
"db_pass": "bar",
```
The fields starting with `db_` are used for the PostgreSQL connection information.
### Running the service via TLS (optional)
```
"tls_cert_path": ""
"tls_key_path": ""
```
Headscale can be configured to expose its web service via TLS. To configure the certificate and key file manually, set the `tls_cert_path` and `tls_cert_path` configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
```
"tls_letsencrypt_hostname": "",
"tls_letsencrypt_listen": ":http",
"tls_letsencrypt_cache_dir": ".cache",
"tls_letsencrypt_challenge_type": "HTTP-01",
```
To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) Headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from. The certificate will automatically be renewed as needed.
#### Challenge type HTTP-01
The default challenge type `HTTP-01` requires that Headscale is reachable on port 80 for the Let's Encrypt automated validation, in addition to whatever port is configured in `listen_addr`. By default, Headscale listens on port 80 on all local IPs for Let's Encrypt automated validation.
If you need to change the ip and/or port used by Headscale for the Let's Encrypt validation process, set `tls_letsencrypt_listen` to the appropriate value. This can be handy if you are running Headscale as a non-root user (or can't run `setcap`). Keep in mind, however, that Let's Encrypt will _only_ connect to port 80 for the validation callback, so if you change `tls_letsencrypt_listen` you will also need to configure something else (e.g. a firewall rule) to forward the traffic from port 80 to the ip:port combination specified in `tls_letsencrypt_listen`.
#### Challenge type TLS-ALPN-01
Alternatively, `tls_letsencrypt_challenge_type` can be set to `TLS-ALPN-01`. In this configuration, Headscale listens on the ip:port combination defined in `listen_addr`. Let's Encrypt will _only_ connect to port 443 for the validation callback, so if `listen_addr` is not set to port 443, something else (e.g. a firewall rule) will be required to forward the traffic from port 443 to the ip:port combination specified in `listen_addr`.
### Policy ACLs
Headscale implements the same policy ACLs as Tailscale.com, adapted to the self-hosted environment.
For instance, instead of referring to users when defining groups you must
use namespaces (which are the equivalent to user/logins in Tailscale.com).
Please check https://tailscale.com/kb/1018/acls/, and `./tests/acls/` in this repo for working examples.
### Apple devices
An endpoint with information on how to connect your Apple devices (currently macOS only) is available at `/apple` on your running instance.
- Fosdem 2023 (video): [Headscale: How we are using integration testing to reimplement Tailscale](https://fosdem.org/2023/schedule/event/goheadscale/)
- presented by Juan Font Alonso and Kristoffer Dalby
## Disclaimer
1. We have nothing to do with Tailscale, or Tailscale Inc.
2. The purpose of writing this was to learn how Tailscale works.
This project is not associated with Tailscale Inc.
## More on Tailscale
However, one of the active maintainers for Headscale [is employed by Tailscale](https://tailscale.com/blog/opensource) and he is allowed to spend work hours contributing to the project. Contributions from this maintainer are reviewed by other maintainers.
- https://tailscale.com/blog/how-tailscale-works/
- https://tailscale.com/blog/tailscale-key-management/
- https://tailscale.com/blog/an-unlikely-database-migration/
The maintainers work together on setting the direction for the project. The underlying principle is to serve the community of self-hosters, enthusiasts and hobbyists - while having a sustainable project.
## Contributing
Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) file.
### Requirements
To contribute to headscale you would need the latest version of [Go](https://golang.org)
and [Buf](https://buf.build) (Protobuf generator).
We recommend using [Nix](https://nixos.org/) to setup a development environment. This can
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.
### Code style
To ensure we have some consistency with a growing number of contributions,
this project has adopted linting and style/formatting rules:
The **Go** code is linted with [`golangci-lint`](https://golangci-lint.run) and
formatted with [`golines`](https://github.com/segmentio/golines) (width 88) and
[`gofumpt`](https://github.com/mvdan/gofumpt).
Please configure your editor to run the tools while developing and make sure to
run `make lint` and `make fmt` before committing any code.
The **Proto** code is linted with [`buf`](https://docs.buf.build/lint/overview) and
formatted with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html).
The **rest** (Markdown, YAML, etc) is formatted with [`prettier`](https://prettier.io).
Check out the `.golangci.yaml` and `Makefile` to see the specific configuration.
### Install development tools
- Go
- Buf
- Protobuf tools
Install and activate:
```shell
nix develop
```
### Testing and building
Some parts of the project require the generation of Go code from Protobuf
(if changes are made in `proto/`) and it must be (re-)generated with:
```shell
make generate
```
**Note**: Please check in changes from `gen/` in a separate commit to make it easier to review.
To run the tests:
```shell
make test
```
To build the program:
```shell
make build
```
### Development workflow
We recommend using Nix for dependency management to ensure you have all required tools. If you prefer to manage dependencies yourself, you can use Make directly:
**With Nix (recommended):**
```shell
nix develop
make test
make build
```
**With your own dependencies:**
```shell
make test
make build
```
The Makefile will warn you if any required tools are missing and suggest running `nix develop`. Run `make help` to see all available targets.
## Contributors
<a href="https://github.com/juanfont/headscale/graphs/contributors">
<img src="https://contrib.rocks/image?repo=juanfont/headscale" />
</a>
Made with [contrib.rocks](https://contrib.rocks).

266
acls.go
View File

@@ -1,266 +0,0 @@
package headscale
import (
"encoding/json"
"fmt"
"io"
"os"
"strconv"
"strings"
"github.com/rs/zerolog/log"
"github.com/tailscale/hujson"
"inet.af/netaddr"
"tailscale.com/tailcfg"
)
const errorEmptyPolicy = Error("empty policy")
const errorInvalidAction = Error("invalid action")
const errorInvalidUserSection = Error("invalid user section")
const errorInvalidGroup = Error("invalid group")
const errorInvalidTag = Error("invalid tag")
const errorInvalidNamespace = Error("invalid namespace")
const errorInvalidPortFormat = Error("invalid port format")
// LoadACLPolicy loads the ACL policy from the specify path, and generates the ACL rules
func (h *Headscale) LoadACLPolicy(path string) error {
policyFile, err := os.Open(path)
if err != nil {
return err
}
defer policyFile.Close()
var policy ACLPolicy
b, err := io.ReadAll(policyFile)
if err != nil {
return err
}
err = hujson.Unmarshal(b, &policy)
if err != nil {
return err
}
if policy.IsZero() {
return errorEmptyPolicy
}
h.aclPolicy = &policy
rules, err := h.generateACLRules()
if err != nil {
return err
}
h.aclRules = rules
return nil
}
func (h *Headscale) generateACLRules() (*[]tailcfg.FilterRule, error) {
rules := []tailcfg.FilterRule{}
for i, a := range h.aclPolicy.ACLs {
if a.Action != "accept" {
return nil, errorInvalidAction
}
r := tailcfg.FilterRule{}
srcIPs := []string{}
for j, u := range a.Users {
srcs, err := h.generateACLPolicySrcIP(u)
if err != nil {
log.Error().
Msgf("Error parsing ACL %d, User %d", i, j)
return nil, err
}
srcIPs = append(srcIPs, *srcs...)
}
r.SrcIPs = srcIPs
destPorts := []tailcfg.NetPortRange{}
for j, d := range a.Ports {
dests, err := h.generateACLPolicyDestPorts(d)
if err != nil {
log.Error().
Msgf("Error parsing ACL %d, Port %d", i, j)
return nil, err
}
destPorts = append(destPorts, *dests...)
}
rules = append(rules, tailcfg.FilterRule{
SrcIPs: srcIPs,
DstPorts: destPorts,
})
}
return &rules, nil
}
func (h *Headscale) generateACLPolicySrcIP(u string) (*[]string, error) {
return h.expandAlias(u)
}
func (h *Headscale) generateACLPolicyDestPorts(d string) (*[]tailcfg.NetPortRange, error) {
tokens := strings.Split(d, ":")
if len(tokens) < 2 || len(tokens) > 3 {
return nil, errorInvalidPortFormat
}
var alias string
// We can have here stuff like:
// git-server:*
// 192.168.1.0/24:22
// tag:montreal-webserver:80,443
// tag:api-server:443
// example-host-1:*
if len(tokens) == 2 {
alias = tokens[0]
} else {
alias = fmt.Sprintf("%s:%s", tokens[0], tokens[1])
}
expanded, err := h.expandAlias(alias)
if err != nil {
return nil, err
}
ports, err := h.expandPorts(tokens[len(tokens)-1])
if err != nil {
return nil, err
}
dests := []tailcfg.NetPortRange{}
for _, d := range *expanded {
for _, p := range *ports {
pr := tailcfg.NetPortRange{
IP: d,
Ports: p,
}
dests = append(dests, pr)
}
}
return &dests, nil
}
func (h *Headscale) expandAlias(s string) (*[]string, error) {
if s == "*" {
return &[]string{"*"}, nil
}
if strings.HasPrefix(s, "group:") {
if _, ok := h.aclPolicy.Groups[s]; !ok {
return nil, errorInvalidGroup
}
ips := []string{}
for _, n := range h.aclPolicy.Groups[s] {
nodes, err := h.ListMachinesInNamespace(n)
if err != nil {
return nil, errorInvalidNamespace
}
for _, node := range *nodes {
ips = append(ips, node.IPAddress)
}
}
return &ips, nil
}
if strings.HasPrefix(s, "tag:") {
if _, ok := h.aclPolicy.TagOwners[s]; !ok {
return nil, errorInvalidTag
}
// This will have HORRIBLE performance.
// We need to change the data model to better store tags
machines := []Machine{}
if err := h.db.Where("registered").Find(&machines).Error; err != nil {
return nil, err
}
ips := []string{}
for _, m := range machines {
hostinfo := tailcfg.Hostinfo{}
if len(m.HostInfo) != 0 {
hi, err := m.HostInfo.MarshalJSON()
if err != nil {
return nil, err
}
err = json.Unmarshal(hi, &hostinfo)
if err != nil {
return nil, err
}
// FIXME: Check TagOwners allows this
for _, t := range hostinfo.RequestTags {
if s[4:] == t {
ips = append(ips, m.IPAddress)
break
}
}
}
}
return &ips, nil
}
n, err := h.GetNamespace(s)
if err == nil {
nodes, err := h.ListMachinesInNamespace(n.Name)
if err != nil {
return nil, err
}
ips := []string{}
for _, n := range *nodes {
ips = append(ips, n.IPAddress)
}
return &ips, nil
}
if h, ok := h.aclPolicy.Hosts[s]; ok {
return &[]string{h.String()}, nil
}
ip, err := netaddr.ParseIP(s)
if err == nil {
return &[]string{ip.String()}, nil
}
cidr, err := netaddr.ParseIPPrefix(s)
if err == nil {
return &[]string{cidr.String()}, nil
}
return nil, errorInvalidUserSection
}
func (h *Headscale) expandPorts(s string) (*[]tailcfg.PortRange, error) {
if s == "*" {
return &[]tailcfg.PortRange{{First: 0, Last: 65535}}, nil
}
ports := []tailcfg.PortRange{}
for _, p := range strings.Split(s, ",") {
rang := strings.Split(p, "-")
if len(rang) == 1 {
pi, err := strconv.ParseUint(rang[0], 10, 16)
if err != nil {
return nil, err
}
ports = append(ports, tailcfg.PortRange{
First: uint16(pi),
Last: uint16(pi),
})
} else if len(rang) == 2 {
start, err := strconv.ParseUint(rang[0], 10, 16)
if err != nil {
return nil, err
}
last, err := strconv.ParseUint(rang[1], 10, 16)
if err != nil {
return nil, err
}
ports = append(ports, tailcfg.PortRange{
First: uint16(start),
Last: uint16(last),
})
} else {
return nil, errorInvalidPortFormat
}
}
return &ports, nil
}

View File

@@ -1,160 +0,0 @@
package headscale
import (
"gopkg.in/check.v1"
)
func (s *Suite) TestWrongPath(c *check.C) {
err := h.LoadACLPolicy("asdfg")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestBrokenHuJson(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/broken.hujson")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestInvalidPolicyHuson(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/invalid.hujson")
c.Assert(err, check.NotNil)
c.Assert(err, check.Equals, errorEmptyPolicy)
}
func (s *Suite) TestParseHosts(c *check.C) {
var hs Hosts
err := hs.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100","example-host-2": "100.100.101.100/24"}`))
c.Assert(hs, check.NotNil)
c.Assert(err, check.IsNil)
}
func (s *Suite) TestParseInvalidCIDR(c *check.C) {
var hs Hosts
err := hs.UnmarshalJSON([]byte(`{"example-host-1": "100.100.100.100/42"}`))
c.Assert(hs, check.IsNil)
c.Assert(err, check.NotNil)
}
func (s *Suite) TestRuleInvalidGeneration(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/acl_policy_invalid.hujson")
c.Assert(err, check.NotNil)
}
func (s *Suite) TestBasicRule(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_1.hujson")
c.Assert(err, check.IsNil)
rules, err := h.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
}
func (s *Suite) TestPortRange(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_range.hujson")
c.Assert(err, check.IsNil)
rules, err := h.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(*rules, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts[0].Ports.First, check.Equals, uint16(5400))
c.Assert((*rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(5500))
}
func (s *Suite) TestPortWildcard(c *check.C) {
err := h.LoadACLPolicy("./tests/acls/acl_policy_basic_wildcards.hujson")
c.Assert(err, check.IsNil)
rules, err := h.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(*rules, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert((*rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert((*rules)[0].SrcIPs, check.HasLen, 1)
c.Assert((*rules)[0].SrcIPs[0], check.Equals, "*")
}
func (s *Suite) TestPortNamespace(c *check.C) {
n, err := h.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil)
pak, err := h.CreatePreAuthKey(n.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine("testnamespace", "testmachine")
c.Assert(err, check.NotNil)
ip, _ := h.getAvailableIP()
m := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: n.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: ip.String(),
AuthKeyID: uint(pak.ID),
}
h.db.Save(&m)
err = h.LoadACLPolicy("./tests/acls/acl_policy_basic_namespace_as_user.hujson")
c.Assert(err, check.IsNil)
rules, err := h.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(*rules, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert((*rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert((*rules)[0].SrcIPs, check.HasLen, 1)
c.Assert((*rules)[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
c.Assert((*rules)[0].SrcIPs[0], check.Equals, ip.String())
}
func (s *Suite) TestPortGroup(c *check.C) {
n, err := h.CreateNamespace("testnamespace")
c.Assert(err, check.IsNil)
pak, err := h.CreatePreAuthKey(n.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine("testnamespace", "testmachine")
c.Assert(err, check.NotNil)
ip, _ := h.getAvailableIP()
m := Machine{
ID: 0,
MachineKey: "foo",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: n.ID,
Registered: true,
RegisterMethod: "authKey",
IPAddress: ip.String(),
AuthKeyID: uint(pak.ID),
}
h.db.Save(&m)
err = h.LoadACLPolicy("./tests/acls/acl_policy_basic_groups.hujson")
c.Assert(err, check.IsNil)
rules, err := h.generateACLRules()
c.Assert(err, check.IsNil)
c.Assert(rules, check.NotNil)
c.Assert(*rules, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts, check.HasLen, 1)
c.Assert((*rules)[0].DstPorts[0].Ports.First, check.Equals, uint16(0))
c.Assert((*rules)[0].DstPorts[0].Ports.Last, check.Equals, uint16(65535))
c.Assert((*rules)[0].SrcIPs, check.HasLen, 1)
c.Assert((*rules)[0].SrcIPs[0], check.Not(check.Equals), "not an ip")
c.Assert((*rules)[0].SrcIPs[0], check.Equals, ip.String())
}

View File

@@ -1,70 +0,0 @@
package headscale
import (
"strings"
"github.com/tailscale/hujson"
"inet.af/netaddr"
)
// ACLPolicy represents a Tailscale ACL Policy
type ACLPolicy struct {
Groups Groups `json:"Groups"`
Hosts Hosts `json:"Hosts"`
TagOwners TagOwners `json:"TagOwners"`
ACLs []ACL `json:"ACLs"`
Tests []ACLTest `json:"Tests"`
}
// ACL is a basic rule for the ACL Policy
type ACL struct {
Action string `json:"Action"`
Users []string `json:"Users"`
Ports []string `json:"Ports"`
}
// Groups references a series of alias in the ACL rules
type Groups map[string][]string
// Hosts are alias for IP addresses or subnets
type Hosts map[string]netaddr.IPPrefix
// TagOwners specify what users (namespaces?) are allow to use certain tags
type TagOwners map[string][]string
// ACLTest is not implemented, but should be use to check if a certain rule is allowed
type ACLTest struct {
User string `json:"User"`
Allow []string `json:"Allow"`
Deny []string `json:"Deny,omitempty"`
}
// UnmarshalJSON allows to parse the Hosts directly into netaddr objects
func (h *Hosts) UnmarshalJSON(data []byte) error {
hosts := Hosts{}
hs := make(map[string]string)
err := hujson.Unmarshal(data, &hs)
if err != nil {
return err
}
for k, v := range hs {
if !strings.Contains(v, "/") {
v = v + "/32"
}
prefix, err := netaddr.ParseIPPrefix(v)
if err != nil {
return err
}
hosts[k] = prefix
}
*h = hosts
return nil
}
// IsZero is perhaps a bit naive here
func (p ACLPolicy) IsZero() bool {
if len(p.Groups) == 0 && len(p.Hosts) == 0 && len(p.ACLs) == 0 {
return true
}
return false
}

416
api.go
View File

@@ -1,416 +0,0 @@
package headscale
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
"github.com/rs/zerolog/log"
"github.com/gin-gonic/gin"
"github.com/klauspost/compress/zstd"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"tailscale.com/types/wgkey"
)
// KeyHandler provides the Headscale pub key
// Listens in /key
func (h *Headscale) KeyHandler(c *gin.Context) {
c.Data(200, "text/plain; charset=utf-8", []byte(h.publicKey.HexString()))
}
// RegisterWebAPI shows a simple message in the browser to point to the CLI
// Listens in /register
func (h *Headscale) RegisterWebAPI(c *gin.Context) {
mKeyStr := c.Query("key")
if mKeyStr == "" {
c.String(http.StatusBadRequest, "Wrong params")
return
}
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
<html>
<body>
<h1>headscale</h1>
<p>
Run the command below in the headscale server to add this machine to your network:
</p>
<p>
<code>
<b>headscale -n NAMESPACE nodes register %s</b>
</code>
</p>
</body>
</html>
`, mKeyStr)))
}
// RegistrationHandler handles the actual registration process of a machine
// Endpoint /machine/:id
func (h *Headscale) RegistrationHandler(c *gin.Context) {
body, _ := io.ReadAll(c.Request.Body)
mKeyStr := c.Param("id")
mKey, err := wgkey.ParseHex(mKeyStr)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot parse machine key")
machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc()
c.String(http.StatusInternalServerError, "Sad!")
return
}
req := tailcfg.RegisterRequest{}
err = decode(body, &req, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot decode message")
machineRegistrations.WithLabelValues("unkown", "web", "error", "unknown").Inc()
c.String(http.StatusInternalServerError, "Very sad!")
return
}
now := time.Now().UTC()
var m Machine
if result := h.db.Preload("Namespace").First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) {
log.Info().Str("machine", req.Hostinfo.Hostname).Msg("New machine")
m = Machine{
Expiry: &req.Expiry,
MachineKey: mKey.HexString(),
Name: req.Hostinfo.Hostname,
NodeKey: wgkey.Key(req.NodeKey).HexString(),
LastSuccessfulUpdate: &now,
}
if err := h.db.Create(&m).Error; err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Could not create row")
machineRegistrations.WithLabelValues("unkown", "web", "error", m.Namespace.Name).Inc()
return
}
}
if !m.Registered && req.Auth.AuthKey != "" {
h.handleAuthKey(c, h.db, mKey, req, m)
return
}
resp := tailcfg.RegisterResponse{}
// We have the updated key!
if m.NodeKey == wgkey.Key(req.NodeKey).HexString() {
if m.Registered {
log.Debug().
Str("handler", "Registration").
Str("machine", m.Name).
Msg("Client is registered and we have the current NodeKey. All clear to /map")
resp.AuthURL = ""
resp.MachineAuthorized = true
resp.User = *m.Namespace.toUser()
respBody, err := encode(resp, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("update", "web", "error", m.Namespace.Name).Inc()
c.String(http.StatusInternalServerError, "")
return
}
machineRegistrations.WithLabelValues("update", "web", "success", m.Namespace.Name).Inc()
c.Data(200, "application/json; charset=utf-8", respBody)
return
}
log.Debug().
Str("handler", "Registration").
Str("machine", m.Name).
Msg("Not registered and not NodeKey rotation. Sending a authurl to register")
resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
h.cfg.ServerURL, mKey.HexString())
respBody, err := encode(resp, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", "web", "error", m.Namespace.Name).Inc()
c.String(http.StatusInternalServerError, "")
return
}
machineRegistrations.WithLabelValues("new", "web", "success", m.Namespace.Name).Inc()
c.Data(200, "application/json; charset=utf-8", respBody)
return
}
// The NodeKey we have matches OldNodeKey, which means this is a refresh after an key expiration
if m.NodeKey == wgkey.Key(req.OldNodeKey).HexString() {
log.Debug().
Str("handler", "Registration").
Str("machine", m.Name).
Msg("We have the OldNodeKey in the database. This is a key refresh")
m.NodeKey = wgkey.Key(req.NodeKey).HexString()
h.db.Save(&m)
resp.AuthURL = ""
resp.User = *m.Namespace.toUser()
respBody, err := encode(resp, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot encode message")
c.String(http.StatusInternalServerError, "Extremely sad!")
return
}
c.Data(200, "application/json; charset=utf-8", respBody)
return
}
// We arrive here after a client is restarted without finalizing the authentication flow or
// when headscale is stopped in the middle of the auth process.
if m.Registered {
log.Debug().
Str("handler", "Registration").
Str("machine", m.Name).
Msg("The node is sending us a new NodeKey, but machine is registered. All clear for /map")
resp.AuthURL = ""
resp.MachineAuthorized = true
resp.User = *m.Namespace.toUser()
respBody, err := encode(resp, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot encode message")
c.String(http.StatusInternalServerError, "")
return
}
c.Data(200, "application/json; charset=utf-8", respBody)
return
}
log.Debug().
Str("handler", "Registration").
Str("machine", m.Name).
Msg("The node is sending us a new NodeKey, sending auth url")
resp.AuthURL = fmt.Sprintf("%s/register?key=%s",
h.cfg.ServerURL, mKey.HexString())
respBody, err := encode(resp, &mKey, h.privateKey)
if err != nil {
log.Error().
Str("handler", "Registration").
Err(err).
Msg("Cannot encode message")
c.String(http.StatusInternalServerError, "")
return
}
c.Data(200, "application/json; charset=utf-8", respBody)
}
func (h *Headscale) getMapResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Machine) ([]byte, error) {
log.Trace().
Str("func", "getMapResponse").
Str("machine", req.Hostinfo.Hostname).
Msg("Creating Map response")
node, err := m.toNode(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Str("func", "getMapResponse").
Err(err).
Msg("Cannot convert to node")
return nil, err
}
peers, err := h.getPeers(m)
if err != nil {
log.Error().
Str("func", "getMapResponse").
Err(err).
Msg("Cannot fetch peers")
return nil, err
}
profiles := getMapResponseUserProfiles(*m, peers)
nodePeers, err := peers.toNodes(h.cfg.BaseDomain, h.cfg.DNSConfig, true)
if err != nil {
log.Error().
Str("func", "getMapResponse").
Err(err).
Msg("Failed to convert peers to Tailscale nodes")
return nil, err
}
dnsConfig, err := getMapResponseDNSConfig(h.cfg.DNSConfig, h.cfg.BaseDomain, *m, peers)
if err != nil {
log.Error().
Str("func", "getMapResponse").
Err(err).
Msg("Failed generate the DNSConfig")
return nil, err
}
resp := tailcfg.MapResponse{
KeepAlive: false,
Node: node,
Peers: nodePeers,
DNSConfig: dnsConfig,
Domain: h.cfg.BaseDomain,
PacketFilter: *h.aclRules,
DERPMap: h.cfg.DerpMap,
UserProfiles: profiles,
}
log.Trace().
Str("func", "getMapResponse").
Str("machine", req.Hostinfo.Hostname).
// Interface("payload", resp).
Msgf("Generated map response: %s", tailMapResponseToString(resp))
var respBody []byte
if req.Compress == "zstd" {
src, _ := json.Marshal(resp)
encoder, _ := zstd.NewWriter(nil)
srcCompressed := encoder.EncodeAll(src, nil)
respBody, err = encodeMsg(srcCompressed, &mKey, h.privateKey)
if err != nil {
return nil, err
}
} else {
respBody, err = encode(resp, &mKey, h.privateKey)
if err != nil {
return nil, err
}
}
// declare the incoming size on the first 4 bytes
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
data = append(data, respBody...)
return data, nil
}
func (h *Headscale) getMapKeepAliveResponse(mKey wgkey.Key, req tailcfg.MapRequest, m *Machine) ([]byte, error) {
resp := tailcfg.MapResponse{
KeepAlive: true,
}
var respBody []byte
var err error
if req.Compress == "zstd" {
src, _ := json.Marshal(resp)
encoder, _ := zstd.NewWriter(nil)
srcCompressed := encoder.EncodeAll(src, nil)
respBody, err = encodeMsg(srcCompressed, &mKey, h.privateKey)
if err != nil {
return nil, err
}
} else {
respBody, err = encode(resp, &mKey, h.privateKey)
if err != nil {
return nil, err
}
}
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, uint32(len(respBody)))
data = append(data, respBody...)
return data, nil
}
func (h *Headscale) handleAuthKey(c *gin.Context, db *gorm.DB, idKey wgkey.Key, req tailcfg.RegisterRequest, m Machine) {
log.Debug().
Str("func", "handleAuthKey").
Str("machine", req.Hostinfo.Hostname).
Msgf("Processing auth key for %s", req.Hostinfo.Hostname)
resp := tailcfg.RegisterResponse{}
pak, err := h.checkKeyValidity(req.Auth.AuthKey)
if err != nil {
log.Error().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Err(err).
Msg("Failed authentication via AuthKey")
resp.MachineAuthorized = false
respBody, err := encode(resp, &idKey, h.privateKey)
if err != nil {
log.Error().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Err(err).
Msg("Cannot encode message")
c.String(http.StatusInternalServerError, "")
machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc()
return
}
c.Data(401, "application/json; charset=utf-8", respBody)
log.Error().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Msg("Failed authentication via AuthKey")
machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc()
return
}
log.Debug().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Msg("Authentication key was valid, proceeding to acquire an IP address")
ip, err := h.getAvailableIP()
if err != nil {
log.Error().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Msg("Failed to find an available IP")
machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc()
return
}
log.Info().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Str("ip", ip.String()).
Msgf("Assigning %s to %s", ip, m.Name)
m.AuthKeyID = uint(pak.ID)
m.IPAddress = ip.String()
m.NamespaceID = pak.NamespaceID
m.NodeKey = wgkey.Key(req.NodeKey).HexString() // we update it just in case
m.Registered = true
m.RegisterMethod = "authKey"
db.Save(&m)
pak.Used = true
db.Save(&pak)
resp.MachineAuthorized = true
resp.User = *pak.Namespace.toUser()
respBody, err := encode(resp, &idKey, h.privateKey)
if err != nil {
log.Error().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Err(err).
Msg("Cannot encode message")
machineRegistrations.WithLabelValues("new", "authkey", "error", m.Namespace.Name).Inc()
c.String(http.StatusInternalServerError, "Extremely sad!")
return
}
machineRegistrations.WithLabelValues("new", "authkey", "success", m.Namespace.Name).Inc()
c.Data(200, "application/json; charset=utf-8", respBody)
log.Info().
Str("func", "handleAuthKey").
Str("machine", m.Name).
Str("ip", ip.String()).
Msg("Successfully authenticated via AuthKey")
}

288
app.go
View File

@@ -1,288 +0,0 @@
package headscale
import (
"errors"
"fmt"
"net/http"
"os"
"sort"
"strings"
"sync"
"time"
"github.com/rs/zerolog/log"
"github.com/gin-gonic/gin"
ginprometheus "github.com/zsais/go-gin-prometheus"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert"
"gorm.io/gorm"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"tailscale.com/types/wgkey"
)
// Config contains the initial Headscale configuration
type Config struct {
ServerURL string
Addr string
PrivateKeyPath string
DerpMap *tailcfg.DERPMap
EphemeralNodeInactivityTimeout time.Duration
IPPrefix netaddr.IPPrefix
BaseDomain string
DBtype string
DBpath string
DBhost string
DBport int
DBname string
DBuser string
DBpass string
TLSLetsEncryptListen string
TLSLetsEncryptHostname string
TLSLetsEncryptCacheDir string
TLSLetsEncryptChallengeType string
TLSCertPath string
TLSKeyPath string
ACMEURL string
ACMEEmail string
DNSConfig *tailcfg.DNSConfig
}
// Headscale represents the base app of the service
type Headscale struct {
cfg Config
db *gorm.DB
dbString string
dbType string
dbDebug bool
publicKey *wgkey.Key
privateKey *wgkey.Private
aclPolicy *ACLPolicy
aclRules *[]tailcfg.FilterRule
lastStateChange sync.Map
}
// NewHeadscale returns the Headscale app
func NewHeadscale(cfg Config) (*Headscale, error) {
content, err := os.ReadFile(cfg.PrivateKeyPath)
if err != nil {
return nil, err
}
privKey, err := wgkey.ParsePrivate(string(content))
if err != nil {
return nil, err
}
pubKey := privKey.Public()
var dbString string
switch cfg.DBtype {
case "postgres":
dbString = fmt.Sprintf("host=%s port=%d dbname=%s user=%s password=%s sslmode=disable", cfg.DBhost,
cfg.DBport, cfg.DBname, cfg.DBuser, cfg.DBpass)
case "sqlite3":
dbString = cfg.DBpath
default:
return nil, errors.New("unsupported DB")
}
h := Headscale{
cfg: cfg,
dbType: cfg.DBtype,
dbString: dbString,
privateKey: privKey,
publicKey: &pubKey,
aclRules: &tailcfg.FilterAllowAll, // default allowall
}
err = h.initDB()
if err != nil {
return nil, err
}
if h.cfg.DNSConfig != nil && h.cfg.DNSConfig.Proxied { // if MagicDNS
magicDNSDomains, err := generateMagicDNSRootDomains(h.cfg.IPPrefix, h.cfg.BaseDomain)
if err != nil {
return nil, err
}
h.cfg.DNSConfig.Routes = make(map[string][]dnstype.Resolver)
for _, d := range magicDNSDomains {
h.cfg.DNSConfig.Routes[d.WithoutTrailingDot()] = nil
}
}
return &h, nil
}
// Redirect to our TLS url
func (h *Headscale) redirect(w http.ResponseWriter, req *http.Request) {
target := h.cfg.ServerURL + req.URL.RequestURI()
http.Redirect(w, req, target, http.StatusFound)
}
// expireEphemeralNodes deletes ephemeral machine records that have not been
// seen for longer than h.cfg.EphemeralNodeInactivityTimeout
func (h *Headscale) expireEphemeralNodes(milliSeconds int64) {
ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond)
for range ticker.C {
h.expireEphemeralNodesWorker()
}
}
func (h *Headscale) expireEphemeralNodesWorker() {
namespaces, err := h.ListNamespaces()
if err != nil {
log.Error().Err(err).Msg("Error listing namespaces")
return
}
for _, ns := range *namespaces {
machines, err := h.ListMachinesInNamespace(ns.Name)
if err != nil {
log.Error().Err(err).Str("namespace", ns.Name).Msg("Error listing machines in namespace")
return
}
for _, m := range *machines {
if m.AuthKey != nil && m.LastSeen != nil && m.AuthKey.Ephemeral && time.Now().After(m.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
log.Info().Str("machine", m.Name).Msg("Ephemeral client removed from database")
err = h.db.Unscoped().Delete(m).Error
if err != nil {
log.Error().Err(err).Str("machine", m.Name).Msg("🤮 Cannot delete ephemeral machine from the database")
}
}
}
h.setLastStateChangeToNow(ns.Name)
}
}
// WatchForKVUpdates checks the KV DB table for requests to perform tailnet upgrades
// This is a way to communitate the CLI with the headscale server
func (h *Headscale) watchForKVUpdates(milliSeconds int64) {
ticker := time.NewTicker(time.Duration(milliSeconds) * time.Millisecond)
for range ticker.C {
h.watchForKVUpdatesWorker()
}
}
func (h *Headscale) watchForKVUpdatesWorker() {
h.checkForNamespacesPendingUpdates()
// more functions will come here in the future
}
// Serve launches a GIN server with the Headscale API
func (h *Headscale) Serve() error {
r := gin.Default()
p := ginprometheus.NewPrometheus("gin")
p.Use(r)
r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"healthy": "ok"}) })
r.GET("/key", h.KeyHandler)
r.GET("/register", h.RegisterWebAPI)
r.POST("/machine/:id/map", h.PollNetMapHandler)
r.POST("/machine/:id", h.RegistrationHandler)
r.GET("/apple", h.AppleMobileConfig)
r.GET("/apple/:platform", h.ApplePlatformConfig)
var err error
go h.watchForKVUpdates(5000)
go h.expireEphemeralNodes(5000)
s := &http.Server{
Addr: h.cfg.Addr,
Handler: r,
ReadTimeout: 30 * time.Second,
// Go does not handle timeouts in HTTP very well, and there is
// no good way to handle streaming timeouts, therefore we need to
// keep this at unlimited and be careful to clean up connections
// https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/#aboutstreaming
WriteTimeout: 0,
}
if h.cfg.TLSLetsEncryptHostname != "" {
if !strings.HasPrefix(h.cfg.ServerURL, "https://") {
log.Warn().Msg("Listening with TLS but ServerURL does not start with https://")
}
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(h.cfg.TLSLetsEncryptHostname),
Cache: autocert.DirCache(h.cfg.TLSLetsEncryptCacheDir),
Client: &acme.Client{
DirectoryURL: h.cfg.ACMEURL,
},
Email: h.cfg.ACMEEmail,
}
s.TLSConfig = m.TLSConfig()
if h.cfg.TLSLetsEncryptChallengeType == "TLS-ALPN-01" {
// Configuration via autocert with TLS-ALPN-01 (https://tools.ietf.org/html/rfc8737)
// The RFC requires that the validation is done on port 443; in other words, headscale
// must be reachable on port 443.
err = s.ListenAndServeTLS("", "")
} else if h.cfg.TLSLetsEncryptChallengeType == "HTTP-01" {
// Configuration via autocert with HTTP-01. This requires listening on
// port 80 for the certificate validation in addition to the headscale
// service, which can be configured to run on any other port.
go func() {
log.Fatal().
Err(http.ListenAndServe(h.cfg.TLSLetsEncryptListen, m.HTTPHandler(http.HandlerFunc(h.redirect)))).
Msg("failed to set up a HTTP server")
}()
err = s.ListenAndServeTLS("", "")
} else {
return errors.New("unknown value for TLSLetsEncryptChallengeType")
}
} else if h.cfg.TLSCertPath == "" {
if !strings.HasPrefix(h.cfg.ServerURL, "http://") {
log.Warn().Msg("Listening without TLS but ServerURL does not start with http://")
}
err = s.ListenAndServe()
} else {
if !strings.HasPrefix(h.cfg.ServerURL, "https://") {
log.Warn().Msg("Listening with TLS but ServerURL does not start with https://")
}
err = s.ListenAndServeTLS(h.cfg.TLSCertPath, h.cfg.TLSKeyPath)
}
return err
}
func (h *Headscale) setLastStateChangeToNow(namespace string) {
now := time.Now().UTC()
lastStateUpdate.WithLabelValues("", "headscale").Set(float64(now.Unix()))
h.lastStateChange.Store(namespace, now)
}
func (h *Headscale) getLastStateChange(namespaces ...string) time.Time {
times := []time.Time{}
for _, namespace := range namespaces {
if wrapped, ok := h.lastStateChange.Load(namespace); ok {
lastChange, _ := wrapped.(time.Time)
times = append(times, lastChange)
}
}
sort.Slice(times, func(i, j int) bool {
return times[i].After(times[j])
})
log.Trace().Msgf("Latest times %#v", times)
if len(times) == 0 {
return time.Now().UTC()
} else {
return times[0]
}
}

View File

@@ -1,226 +0,0 @@
package headscale
import (
"bytes"
"net/http"
"text/template"
"github.com/rs/zerolog/log"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
)
// AppleMobileConfig shows a simple message in the browser to point to the CLI
// Listens in /register
func (h *Headscale) AppleMobileConfig(c *gin.Context) {
t := template.Must(template.New("apple").Parse(`
<html>
<body>
<h1>Apple configuration profiles</h1>
<p>
This page provides <a href="https://support.apple.com/guide/mdm/mdm-overview-mdmbf9e668/web">configuration profiles</a> for the official Tailscale clients for <a href="https://apps.apple.com/us/app/tailscale/id1470499037?ls=1">iOS</a> and <a href="https://apps.apple.com/ca/app/tailscale/id1475387142?mt=12">macOS</a>.
</p>
<p>
The profiles will configure Tailscale.app to use {{.Url}} as its control server.
</p>
<h3>Caution</h3>
<p>You should always inspect the profile before installing it:</p>
<!--
<p><code>curl {{.Url}}/apple/ios</code></p>
-->
<p><code>curl {{.Url}}/apple/macos</code></p>
<h2>Profiles</h2>
<!--
<h3>iOS</h3>
<p>
<a href="/apple/ios" download="headscale_ios.mobileconfig">iOS profile</a>
</p>
-->
<h3>macOS</h3>
<p>Headscale can be set to the default server by installing a Headscale configuration profile:</p>
<p>
<a href="/apple/macos" download="headscale_macos.mobileconfig">macOS profile</a>
</p>
<ol>
<li>Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed</li>
<li>Open System Preferences and go to "Profiles"</li>
<li>Find and install the Headscale profile</li>
<li>Restart Tailscale.app and log in</li>
</ol>
<p>Or</p>
<p>Use your terminal to configure the default setting for Tailscale by issuing:</p>
<code>defaults write io.tailscale.ipn.macos ControlURL {{.Url}}</code>
<p>Restart Tailscale.app and log in.</p>
</body>
</html>`))
config := map[string]interface{}{
"Url": h.cfg.ServerURL,
}
var payload bytes.Buffer
if err := t.Execute(&payload, config); err != nil {
log.Error().
Str("handler", "AppleMobileConfig").
Err(err).
Msg("Could not render Apple index template")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple index template"))
return
}
c.Data(http.StatusOK, "text/html; charset=utf-8", payload.Bytes())
}
func (h *Headscale) ApplePlatformConfig(c *gin.Context) {
platform := c.Param("platform")
id, err := uuid.NewV4()
if err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Failed not create UUID")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID"))
return
}
contentId, err := uuid.NewV4()
if err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Failed not create UUID")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Failed to create UUID"))
return
}
platformConfig := AppleMobilePlatformConfig{
UUID: contentId,
Url: h.cfg.ServerURL,
}
var payload bytes.Buffer
switch platform {
case "macos":
if err := macosTemplate.Execute(&payload, platformConfig); err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Could not render Apple macOS template")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple macOS template"))
return
}
case "ios":
if err := iosTemplate.Execute(&payload, platformConfig); err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Could not render Apple iOS template")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple iOS template"))
return
}
default:
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte("Invalid platform, only ios and macos is supported"))
return
}
config := AppleMobileConfig{
UUID: id,
Url: h.cfg.ServerURL,
Payload: payload.String(),
}
var content bytes.Buffer
if err := commonTemplate.Execute(&content, config); err != nil {
log.Error().
Str("handler", "ApplePlatformConfig").
Err(err).
Msg("Could not render Apple platform template")
c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte("Could not render Apple platform template"))
return
}
c.Data(http.StatusOK, "application/x-apple-aspen-config; charset=utf-8", content.Bytes())
}
type AppleMobileConfig struct {
UUID uuid.UUID
Url string
Payload string
}
type AppleMobilePlatformConfig struct {
UUID uuid.UUID
Url string
}
var commonTemplate = template.Must(template.New("mobileconfig").Parse(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadUUID</key>
<string>{{.UUID}}</string>
<key>PayloadDisplayName</key>
<string>Headscale</string>
<key>PayloadDescription</key>
<string>Configure Tailscale login server to: {{.Url}}</string>
<key>PayloadIdentifier</key>
<string>com.github.juanfont.headscale</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadContent</key>
<array>
{{.Payload}}
</array>
</dict>
</plist>`))
var iosTemplate = template.Must(template.New("iosTemplate").Parse(`
<dict>
<key>PayloadType</key>
<string>io.tailscale.ipn.ios</string>
<key>PayloadUUID</key>
<string>{{.UUID}}</string>
<key>PayloadIdentifier</key>
<string>com.github.juanfont.headscale</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true/>
<key>ControlURL</key>
<string>{{.Url}}</string>
</dict>
`))
var macosTemplate = template.Must(template.New("macosTemplate").Parse(`
<dict>
<key>PayloadType</key>
<string>io.tailscale.ipn.macos</string>
<key>PayloadUUID</key>
<string>{{.UUID}}</string>
<key>PayloadIdentifier</key>
<string>com.github.juanfont.headscale</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>PayloadEnabled</key>
<true/>
<key>ControlURL</key>
<string>{{.Url}}</string>
</dict>
`))

21
buf.gen.yaml Normal file
View File

@@ -0,0 +1,21 @@
version: v1
plugins:
- name: go
out: gen/go
opt:
- paths=source_relative
- name: go-grpc
out: gen/go
opt:
- paths=source_relative
- name: grpc-gateway
out: gen/go
opt:
- paths=source_relative
- generate_unbound_methods=true
# - name: gorm
# out: gen/go
# opt:
# - paths=source_relative,enums=string,gateway=true
- name: openapiv2
out: gen/openapiv2

40
cli.go
View File

@@ -1,40 +0,0 @@
package headscale
import (
"errors"
"gorm.io/gorm"
"tailscale.com/types/wgkey"
)
// RegisterMachine is executed from the CLI to register a new Machine using its MachineKey
func (h *Headscale) RegisterMachine(key string, namespace string) (*Machine, error) {
ns, err := h.GetNamespace(namespace)
if err != nil {
return nil, err
}
mKey, err := wgkey.ParseHex(key)
if err != nil {
return nil, err
}
m := Machine{}
if result := h.db.First(&m, "machine_key = ?", mKey.HexString()); errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, errors.New("Machine not found")
}
if m.isAlreadyRegistered() {
return nil, errors.New("Machine already registered")
}
ip, err := h.getAvailableIP()
if err != nil {
return nil, err
}
m.IPAddress = ip.String()
m.NamespaceID = ns.ID
m.Registered = true
m.RegisterMethod = "cli"
h.db.Save(&m)
return &m, nil
}

View File

@@ -1,31 +0,0 @@
package headscale
import (
"gopkg.in/check.v1"
)
func (s *Suite) TestRegisterMachine(c *check.C) {
n, err := h.CreateNamespace("test")
c.Assert(err, check.IsNil)
m := Machine{
ID: 0,
MachineKey: "8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e",
NodeKey: "bar",
DiscoKey: "faa",
Name: "testmachine",
NamespaceID: n.ID,
IPAddress: "10.0.0.1",
}
h.db.Save(&m)
_, err = h.GetMachine("test", "testmachine")
c.Assert(err, check.IsNil)
m2, err := h.RegisterMachine("8ce002a935f8c394e55e78fbbb410576575ff8ec5cfa2e627e4b807f1be15b0e", n.Name)
c.Assert(err, check.IsNil)
c.Assert(m2.Registered, check.Equals, true)
_, err = m2.GetHostInfo()
c.Assert(err, check.IsNil)
}

View File

@@ -0,0 +1,222 @@
package cli
import (
"fmt"
"strconv"
"time"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/prometheus/common/model"
"github.com/pterm/pterm"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/timestamppb"
)
const (
// 90 days.
DefaultAPIKeyExpiry = "90d"
)
func init() {
rootCmd.AddCommand(apiKeysCmd)
apiKeysCmd.AddCommand(listAPIKeys)
createAPIKeyCmd.Flags().
StringP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
apiKeysCmd.AddCommand(createAPIKeyCmd)
expireAPIKeyCmd.Flags().StringP("prefix", "p", "", "ApiKey prefix")
if err := expireAPIKeyCmd.MarkFlagRequired("prefix"); err != nil {
log.Fatal().Err(err).Msg("")
}
apiKeysCmd.AddCommand(expireAPIKeyCmd)
deleteAPIKeyCmd.Flags().StringP("prefix", "p", "", "ApiKey prefix")
if err := deleteAPIKeyCmd.MarkFlagRequired("prefix"); err != nil {
log.Fatal().Err(err).Msg("")
}
apiKeysCmd.AddCommand(deleteAPIKeyCmd)
}
var apiKeysCmd = &cobra.Command{
Use: "apikeys",
Short: "Handle the Api keys in Headscale",
Aliases: []string{"apikey", "api"},
}
var listAPIKeys = &cobra.Command{
Use: "list",
Short: "List the Api keys for headscale",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ListApiKeysRequest{}
response, err := client.ListApiKeys(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting the list of keys: %s", err),
output,
)
}
if output != "" {
SuccessOutput(response.GetApiKeys(), "", output)
}
tableData := pterm.TableData{
{"ID", "Prefix", "Expiration", "Created"},
}
for _, key := range response.GetApiKeys() {
expiration := "-"
if key.GetExpiration() != nil {
expiration = ColourTime(key.GetExpiration().AsTime())
}
tableData = append(tableData, []string{
strconv.FormatUint(key.GetId(), util.Base10),
key.GetPrefix(),
expiration,
key.GetCreatedAt().AsTime().Format(HeadscaleDateTimeFormat),
})
}
err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to render pterm table: %s", err),
output,
)
}
},
}
var createAPIKeyCmd = &cobra.Command{
Use: "create",
Short: "Creates a new Api key",
Long: `
Creates a new Api key, the Api key is only visible on creation
and cannot be retrieved again.
If you loose a key, create a new one and revoke (expire) the old one.`,
Aliases: []string{"c", "new"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
request := &v1.CreateApiKeyRequest{}
durationStr, _ := cmd.Flags().GetString("expiration")
duration, err := model.ParseDuration(durationStr)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Could not parse duration: %s\n", err),
output,
)
}
expiration := time.Now().UTC().Add(time.Duration(duration))
request.Expiration = timestamppb.New(expiration)
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
response, err := client.CreateApiKey(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Cannot create Api Key: %s\n", err),
output,
)
}
SuccessOutput(response.GetApiKey(), response.GetApiKey(), output)
},
}
var expireAPIKeyCmd = &cobra.Command{
Use: "expire",
Short: "Expire an ApiKey",
Aliases: []string{"revoke", "exp", "e"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
prefix, err := cmd.Flags().GetString("prefix")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting prefix from CLI flag: %s", err),
output,
)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ExpireApiKeyRequest{
Prefix: prefix,
}
response, err := client.ExpireApiKey(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Cannot expire Api Key: %s\n", err),
output,
)
}
SuccessOutput(response, "Key expired", output)
},
}
var deleteAPIKeyCmd = &cobra.Command{
Use: "delete",
Short: "Delete an ApiKey",
Aliases: []string{"remove", "del"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
prefix, err := cmd.Flags().GetString("prefix")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting prefix from CLI flag: %s", err),
output,
)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.DeleteApiKeyRequest{
Prefix: prefix,
}
response, err := client.DeleteApiKey(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Cannot delete Api Key: %s\n", err),
output,
)
}
SuccessOutput(response, "Key deleted", output)
},
}

View File

@@ -0,0 +1,22 @@
package cli
import (
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(configTestCmd)
}
var configTestCmd = &cobra.Command{
Use: "configtest",
Short: "Test the configuration.",
Long: "Run a test of the configuration and exit.",
Run: func(cmd *cobra.Command, args []string) {
_, err := newHeadscaleServerWithConfig()
if err != nil {
log.Fatal().Caller().Err(err).Msg("Error initializing")
}
},
}

123
cmd/headscale/cli/debug.go Normal file
View File

@@ -0,0 +1,123 @@
package cli
import (
"fmt"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"google.golang.org/grpc/status"
)
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
type Error string
func (e Error) Error() string { return string(e) }
func init() {
rootCmd.AddCommand(debugCmd)
createNodeCmd.Flags().StringP("name", "", "", "Name")
err := createNodeCmd.MarkFlagRequired("name")
if err != nil {
log.Fatal().Err(err).Msg("")
}
createNodeCmd.Flags().StringP("user", "u", "", "User")
createNodeCmd.Flags().StringP("namespace", "n", "", "User")
createNodeNamespaceFlag := createNodeCmd.Flags().Lookup("namespace")
createNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage
createNodeNamespaceFlag.Hidden = true
err = createNodeCmd.MarkFlagRequired("user")
if err != nil {
log.Fatal().Err(err).Msg("")
}
createNodeCmd.Flags().StringP("key", "k", "", "Key")
err = createNodeCmd.MarkFlagRequired("key")
if err != nil {
log.Fatal().Err(err).Msg("")
}
createNodeCmd.Flags().
StringSliceP("route", "r", []string{}, "List (or repeated flags) of routes to advertise")
debugCmd.AddCommand(createNodeCmd)
}
var debugCmd = &cobra.Command{
Use: "debug",
Short: "debug and testing commands",
Long: "debug contains extra commands used for debugging and testing headscale",
}
var createNodeCmd = &cobra.Command{
Use: "create-node",
Short: "Create a node that can be registered with `nodes register <>` command",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
user, err := cmd.Flags().GetString("user")
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
name, err := cmd.Flags().GetString("name")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting node from flag: %s", err),
output,
)
}
registrationID, err := cmd.Flags().GetString("key")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting key from flag: %s", err),
output,
)
}
_, err = types.RegistrationIDFromString(registrationID)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to parse machine key from flag: %s", err),
output,
)
}
routes, err := cmd.Flags().GetStringSlice("route")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting routes from flag: %s", err),
output,
)
}
request := &v1.DebugCreateNodeRequest{
Key: registrationID,
Name: name,
User: user,
Routes: routes,
}
response, err := client.DebugCreateNode(ctx, request)
if err != nil {
ErrorOutput(
err,
"Cannot create node: "+status.Convert(err).Message(),
output,
)
}
SuccessOutput(response.GetNode(), "Node created", output)
},
}

View File

@@ -0,0 +1,28 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func init() {
rootCmd.AddCommand(dumpConfigCmd)
}
var dumpConfigCmd = &cobra.Command{
Use: "dumpConfig",
Short: "dump current config to /etc/headscale/config.dump.yaml, integration test only",
Hidden: true,
Args: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {
err := viper.WriteConfigAs("/etc/headscale/config.dump.yaml")
if err != nil {
//nolint
fmt.Println("Failed to dump config")
}
},
}

View File

@@ -0,0 +1,42 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"tailscale.com/types/key"
)
func init() {
rootCmd.AddCommand(generateCmd)
generateCmd.AddCommand(generatePrivateKeyCmd)
}
var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate commands",
Aliases: []string{"gen"},
}
var generatePrivateKeyCmd = &cobra.Command{
Use: "private-key",
Short: "Generate a private key for the headscale server",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
machineKey := key.NewMachine()
machineKeyStr, err := machineKey.MarshalText()
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting machine key from flag: %s", err),
output,
)
}
SuccessOutput(map[string]string{
"private_key": string(machineKeyStr),
},
string(machineKeyStr), output)
},
}

View File

@@ -0,0 +1,29 @@
package cli
import (
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(healthCmd)
}
var healthCmd = &cobra.Command{
Use: "health",
Short: "Check the health of the Headscale server",
Long: "Check the health of the Headscale server. This command will return an exit code of 0 if the server is healthy, or 1 if it is not.",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
response, err := client.Health(ctx, &v1.HealthRequest{})
if err != nil {
ErrorOutput(err, "Error checking health", output)
}
SuccessOutput(response, "", output)
},
}

View File

@@ -0,0 +1,147 @@
package cli
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"os"
"strconv"
"time"
"github.com/oauth2-proxy/mockoidc"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
const (
errMockOidcClientIDNotDefined = Error("MOCKOIDC_CLIENT_ID not defined")
errMockOidcClientSecretNotDefined = Error("MOCKOIDC_CLIENT_SECRET not defined")
errMockOidcPortNotDefined = Error("MOCKOIDC_PORT not defined")
refreshTTL = 60 * time.Minute
)
var accessTTL = 2 * time.Minute
func init() {
rootCmd.AddCommand(mockOidcCmd)
}
var mockOidcCmd = &cobra.Command{
Use: "mockoidc",
Short: "Runs a mock OIDC server for testing",
Long: "This internal command runs a OpenID Connect for testing purposes",
Run: func(cmd *cobra.Command, args []string) {
err := mockOIDC()
if err != nil {
log.Error().Err(err).Msgf("Error running mock OIDC server")
os.Exit(1)
}
},
}
func mockOIDC() error {
clientID := os.Getenv("MOCKOIDC_CLIENT_ID")
if clientID == "" {
return errMockOidcClientIDNotDefined
}
clientSecret := os.Getenv("MOCKOIDC_CLIENT_SECRET")
if clientSecret == "" {
return errMockOidcClientSecretNotDefined
}
addrStr := os.Getenv("MOCKOIDC_ADDR")
if addrStr == "" {
return errMockOidcPortNotDefined
}
portStr := os.Getenv("MOCKOIDC_PORT")
if portStr == "" {
return errMockOidcPortNotDefined
}
accessTTLOverride := os.Getenv("MOCKOIDC_ACCESS_TTL")
if accessTTLOverride != "" {
newTTL, err := time.ParseDuration(accessTTLOverride)
if err != nil {
return err
}
accessTTL = newTTL
}
userStr := os.Getenv("MOCKOIDC_USERS")
if userStr == "" {
return errors.New("MOCKOIDC_USERS not defined")
}
var users []mockoidc.MockUser
err := json.Unmarshal([]byte(userStr), &users)
if err != nil {
return fmt.Errorf("unmarshalling users: %w", err)
}
log.Info().Interface("users", users).Msg("loading users from JSON")
log.Info().Msgf("Access token TTL: %s", accessTTL)
port, err := strconv.Atoi(portStr)
if err != nil {
return err
}
mock, err := getMockOIDC(clientID, clientSecret, users)
if err != nil {
return err
}
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addrStr, port))
if err != nil {
return err
}
err = mock.Start(listener, nil)
if err != nil {
return err
}
log.Info().Msgf("Mock OIDC server listening on %s", listener.Addr().String())
log.Info().Msgf("Issuer: %s", mock.Issuer())
c := make(chan struct{})
<-c
return nil
}
func getMockOIDC(clientID string, clientSecret string, users []mockoidc.MockUser) (*mockoidc.MockOIDC, error) {
keypair, err := mockoidc.NewKeypair(nil)
if err != nil {
return nil, err
}
userQueue := mockoidc.UserQueue{}
for _, user := range users {
userQueue.Push(&user)
}
mock := mockoidc.MockOIDC{
ClientID: clientID,
ClientSecret: clientSecret,
AccessTTL: accessTTL,
RefreshTTL: refreshTTL,
CodeChallengeMethodsSupported: []string{"plain", "S256"},
Keypair: keypair,
SessionStore: mockoidc.NewSessionStore(),
UserQueue: &userQueue,
ErrorQueue: &mockoidc.ErrorQueue{},
}
mock.AddMiddleware(func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Info().Msgf("Request: %+v", r)
h.ServeHTTP(w, r)
if r.Response != nil {
log.Info().Msgf("Response: %+v", r.Response)
}
})
})
return &mock, nil
}

View File

@@ -1,138 +0,0 @@
package cli
import (
"fmt"
"log"
"strconv"
"strings"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(namespaceCmd)
namespaceCmd.AddCommand(createNamespaceCmd)
namespaceCmd.AddCommand(listNamespacesCmd)
namespaceCmd.AddCommand(destroyNamespaceCmd)
namespaceCmd.AddCommand(renameNamespaceCmd)
}
var namespaceCmd = &cobra.Command{
Use: "namespaces",
Short: "Manage the namespaces of Headscale",
}
var createNamespaceCmd = &cobra.Command{
Use: "create NAME",
Short: "Creates a new namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
namespace, err := h.CreateNamespace(args[0])
if strings.HasPrefix(o, "json") {
JsonOutput(namespace, err, o)
return
}
if err != nil {
fmt.Printf("Error creating namespace: %s\n", err)
return
}
fmt.Printf("Namespace created\n")
},
}
var destroyNamespaceCmd = &cobra.Command{
Use: "destroy NAME",
Short: "Destroys a namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
err = h.DestroyNamespace(args[0])
if strings.HasPrefix(o, "json") {
JsonOutput(map[string]string{"Result": "Namespace destroyed"}, err, o)
return
}
if err != nil {
fmt.Printf("Error destroying namespace: %s\n", err)
return
}
fmt.Printf("Namespace destroyed\n")
},
}
var listNamespacesCmd = &cobra.Command{
Use: "list",
Short: "List all the namespaces",
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
namespaces, err := h.ListNamespaces()
if strings.HasPrefix(o, "json") {
JsonOutput(namespaces, err, o)
return
}
if err != nil {
fmt.Println(err)
return
}
d := pterm.TableData{{"ID", "Name", "Created"}}
for _, n := range *namespaces {
d = append(d, []string{strconv.FormatUint(uint64(n.ID), 10), n.Name, n.CreatedAt.Format("2006-01-02 15:04:05")})
}
err = pterm.DefaultTable.WithHasHeader().WithData(d).Render()
if err != nil {
log.Fatal(err)
}
},
}
var renameNamespaceCmd = &cobra.Command{
Use: "rename OLD_NAME NEW_NAME",
Short: "Renames a namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
err = h.RenameNamespace(args[0], args[1])
if strings.HasPrefix(o, "json") {
JsonOutput(map[string]string{"Result": "Namespace renamed"}, err, o)
return
}
if err != nil {
fmt.Printf("Error renaming namespace: %s\n", err)
return
}
fmt.Printf("Namespace renamed\n")
},
}

View File

@@ -3,316 +3,758 @@ package cli
import (
"fmt"
"log"
"net/netip"
"slices"
"strconv"
"strings"
"time"
survey "github.com/AlecAivazis/survey/v2"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/pterm/pterm"
"github.com/samber/lo"
"github.com/spf13/cobra"
"tailscale.com/tailcfg"
"tailscale.com/types/wgkey"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"tailscale.com/types/key"
)
func init() {
rootCmd.AddCommand(nodeCmd)
nodeCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace")
err := nodeCmd.MarkPersistentFlagRequired("namespace")
if err != nil {
log.Fatalf(err.Error())
}
listNodesCmd.Flags().StringP("user", "u", "", "Filter by user")
listNodesCmd.Flags().BoolP("tags", "t", false, "Show tags")
listNodesCmd.Flags().StringP("namespace", "n", "", "User")
listNodesNamespaceFlag := listNodesCmd.Flags().Lookup("namespace")
listNodesNamespaceFlag.Deprecated = deprecateNamespaceMessage
listNodesNamespaceFlag.Hidden = true
nodeCmd.AddCommand(listNodesCmd)
listNodeRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
nodeCmd.AddCommand(listNodeRoutesCmd)
registerNodeCmd.Flags().StringP("user", "u", "", "User")
registerNodeCmd.Flags().StringP("namespace", "n", "", "User")
registerNodeNamespaceFlag := registerNodeCmd.Flags().Lookup("namespace")
registerNodeNamespaceFlag.Deprecated = deprecateNamespaceMessage
registerNodeNamespaceFlag.Hidden = true
err := registerNodeCmd.MarkFlagRequired("user")
if err != nil {
log.Fatal(err.Error())
}
registerNodeCmd.Flags().StringP("key", "k", "", "Key")
err = registerNodeCmd.MarkFlagRequired("key")
if err != nil {
log.Fatal(err.Error())
}
nodeCmd.AddCommand(registerNodeCmd)
expireNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
expireNodeCmd.Flags().StringP("expiry", "e", "", "Set expire to (RFC3339 format, e.g. 2025-08-27T10:00:00Z), or leave empty to expire immediately.")
err = expireNodeCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatal(err.Error())
}
nodeCmd.AddCommand(expireNodeCmd)
renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = renameNodeCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatal(err.Error())
}
nodeCmd.AddCommand(renameNodeCmd)
deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = deleteNodeCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatal(err.Error())
}
nodeCmd.AddCommand(deleteNodeCmd)
nodeCmd.AddCommand(shareMachineCmd)
nodeCmd.AddCommand(unshareMachineCmd)
tagCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
tagCmd.MarkFlagRequired("identifier")
tagCmd.Flags().StringSliceP("tags", "t", []string{}, "List of tags to add to the node")
nodeCmd.AddCommand(tagCmd)
approveRoutesCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
approveRoutesCmd.MarkFlagRequired("identifier")
approveRoutesCmd.Flags().StringSliceP("routes", "r", []string{}, `List of routes that will be approved (comma-separated, e.g. "10.0.0.0/8,192.168.0.0/24" or empty string to remove all approved routes)`)
nodeCmd.AddCommand(approveRoutesCmd)
nodeCmd.AddCommand(backfillNodeIPsCmd)
}
var nodeCmd = &cobra.Command{
Use: "nodes",
Short: "Manage the nodes of Headscale",
Use: "nodes",
Short: "Manage the nodes of Headscale",
Aliases: []string{"node", "machine", "machines"},
}
var registerNodeCmd = &cobra.Command{
Use: "register machineID",
Short: "Registers a machine to your network",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing parameters")
}
return nil
},
Use: "register",
Short: "Registers a node to your network",
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
output, _ := cmd.Flags().GetString("output")
user, err := cmd.Flags().GetString("user")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
registrationID, err := cmd.Flags().GetString("key")
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(
err,
fmt.Sprintf("Error getting node key from flag: %s", err),
output,
)
}
m, err := h.RegisterMachine(args[0], n)
if strings.HasPrefix(o, "json") {
JsonOutput(m, err, o)
return
request := &v1.RegisterNodeRequest{
Key: registrationID,
User: user,
}
response, err := client.RegisterNode(ctx, request)
if err != nil {
fmt.Printf("Cannot register machine: %s\n", err)
return
ErrorOutput(
err,
fmt.Sprintf(
"Cannot register node: %s\n",
status.Convert(err).Message(),
),
output,
)
}
fmt.Printf("Machine registered\n")
SuccessOutput(
response.GetNode(),
fmt.Sprintf("Node %s registered", response.GetNode().GetGivenName()), output)
},
}
var listNodesCmd = &cobra.Command{
Use: "list",
Short: "List the nodes in a given namespace",
Use: "list",
Short: "List nodes",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
output, _ := cmd.Flags().GetString("output")
user, err := cmd.Flags().GetString("user")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
showTags, err := cmd.Flags().GetBool("tags")
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting tags flag: %s", err), output)
}
namespace, err := h.GetNamespace(n)
if err != nil {
log.Fatalf("Error fetching namespace: %s", err)
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ListNodesRequest{
User: user,
}
machines, err := h.ListMachinesInNamespace(n)
response, err := client.ListNodes(ctx, request)
if err != nil {
log.Fatalf("Error fetching machines: %s", err)
ErrorOutput(
err,
"Cannot get nodes: "+status.Convert(err).Message(),
output,
)
}
sharedMachines, err := h.ListSharedMachinesInNamespace(n)
if err != nil {
log.Fatalf("Error fetching shared machines: %s", err)
if output != "" {
SuccessOutput(response.GetNodes(), "", output)
}
allMachines := append(*machines, *sharedMachines...)
if strings.HasPrefix(o, "json") {
JsonOutput(allMachines, err, o)
return
tableData, err := nodesToPtables(user, showTags, response.GetNodes())
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output)
}
err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
if err != nil {
log.Fatalf("Error getting nodes: %s", err)
}
d, err := nodesToPtables(*namespace, allMachines)
if err != nil {
log.Fatalf("Error converting to table: %s", err)
}
err = pterm.DefaultTable.WithHasHeader().WithData(d).Render()
if err != nil {
log.Fatal(err)
ErrorOutput(
err,
fmt.Sprintf("Failed to render pterm table: %s", err),
output,
)
}
},
}
var deleteNodeCmd = &cobra.Command{
Use: "delete ID",
Short: "Delete a node",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing parameters")
}
return nil
},
var listNodeRoutesCmd = &cobra.Command{
Use: "list-routes",
Short: "List routes available on nodes",
Aliases: []string{"lsr", "routes"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
id, err := strconv.Atoi(args[0])
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ListNodesRequest{}
response, err := client.ListNodes(ctx, request)
if err != nil {
log.Fatalf("Error converting ID to integer: %s", err)
ErrorOutput(
err,
"Cannot get nodes: "+status.Convert(err).Message(),
output,
)
}
m, err := h.GetMachineByID(uint64(id))
nodes := response.GetNodes()
if identifier != 0 {
for _, node := range response.GetNodes() {
if node.GetId() == identifier {
nodes = []*v1.Node{node}
break
}
}
}
nodes = lo.Filter(nodes, func(n *v1.Node, _ int) bool {
return (n.GetSubnetRoutes() != nil && len(n.GetSubnetRoutes()) > 0) || (n.GetApprovedRoutes() != nil && len(n.GetApprovedRoutes()) > 0) || (n.GetAvailableRoutes() != nil && len(n.GetAvailableRoutes()) > 0)
})
if output != "" {
SuccessOutput(nodes, "", output)
return
}
tableData, err := nodeRoutesToPtables(nodes)
if err != nil {
log.Fatalf("Error getting node: %s", err)
ErrorOutput(err, fmt.Sprintf("Error converting to table: %s", err), output)
}
err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to render pterm table: %s", err),
output,
)
}
},
}
var expireNodeCmd = &cobra.Command{
Use: "expire",
Short: "Expire (log out) a node in your network",
Long: "Expiring a node will keep the node in the database and force it to reauthenticate.",
Aliases: []string{"logout", "exp", "e"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
expiry, err := cmd.Flags().GetString("expiry")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting expiry to string: %s", err),
output,
)
return
}
expiryTime := time.Now()
if expiry != "" {
expiryTime, err = time.Parse(time.RFC3339, expiry)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting expiry to string: %s", err),
output,
)
return
}
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ExpireNodeRequest{
NodeId: identifier,
Expiry: timestamppb.New(expiryTime),
}
response, err := client.ExpireNode(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf(
"Cannot expire node: %s\n",
status.Convert(err).Message(),
),
output,
)
}
SuccessOutput(response.GetNode(), "Node expired", output)
},
}
var renameNodeCmd = &cobra.Command{
Use: "rename NEW_NAME",
Short: "Renames a node in your network",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
newName := ""
if len(args) > 0 {
newName = args[0]
}
request := &v1.RenameNodeRequest{
NodeId: identifier,
NewName: newName,
}
response, err := client.RenameNode(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf(
"Cannot rename node: %s\n",
status.Convert(err).Message(),
),
output,
)
}
SuccessOutput(response.GetNode(), "Node renamed", output)
},
}
var deleteNodeCmd = &cobra.Command{
Use: "delete",
Short: "Delete a node",
Aliases: []string{"del"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
getRequest := &v1.GetNodeRequest{
NodeId: identifier,
}
getResponse, err := client.GetNode(ctx, getRequest)
if err != nil {
ErrorOutput(
err,
"Error getting node node: "+status.Convert(err).Message(),
output,
)
}
deleteRequest := &v1.DeleteNodeRequest{
NodeId: identifier,
}
confirm := false
force, _ := cmd.Flags().GetBool("force")
if !force {
prompt := &survey.Confirm{
Message: fmt.Sprintf("Do you want to remove the node %s?", m.Name),
}
err = survey.AskOne(prompt, &confirm)
if err != nil {
return
}
confirm = util.YesNo(fmt.Sprintf(
"Do you want to remove the node %s?",
getResponse.GetNode().GetName(),
))
}
if confirm || force {
err = h.DeleteMachine(m)
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node deleted"}, err, output)
response, err := client.DeleteNode(ctx, deleteRequest)
if output != "" {
SuccessOutput(response, "", output)
return
}
if err != nil {
log.Fatalf("Error deleting node: %s", err)
ErrorOutput(
err,
"Error deleting node: "+status.Convert(err).Message(),
output,
)
}
fmt.Printf("Node deleted\n")
SuccessOutput(
map[string]string{"Result": "Node deleted"},
"Node deleted",
output,
)
} else {
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node not deleted"}, err, output)
return
SuccessOutput(map[string]string{"Result": "Node not deleted"}, "Node not deleted", output)
}
},
}
var backfillNodeIPsCmd = &cobra.Command{
Use: "backfillips",
Short: "Backfill IPs missing from nodes",
Long: `
Backfill IPs can be used to add/remove IPs from nodes
based on the current configuration of Headscale.
If there are nodes that does not have IPv4 or IPv6
even if prefixes for both are configured in the config,
this command can be used to assign IPs of the sort to
all nodes that are missing.
If you remove IPv4 or IPv6 prefixes from the config,
it can be run to remove the IPs that should no longer
be assigned to nodes.`,
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
confirm := false
force, _ := cmd.Flags().GetBool("force")
if !force {
confirm = util.YesNo("Are you sure that you want to assign/remove IPs to/from nodes?")
}
if confirm || force {
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
changes, err := client.BackfillNodeIPs(ctx, &v1.BackfillNodeIPsRequest{Confirmed: confirm || force})
if err != nil {
ErrorOutput(
err,
"Error backfilling IPs: "+status.Convert(err).Message(),
output,
)
}
fmt.Printf("Node not deleted\n")
SuccessOutput(changes, "Node IPs backfilled successfully", output)
}
},
}
var shareMachineCmd = &cobra.Command{
Use: "share ID namespace",
Short: "Shares a node from the current namespace to the specified one",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
output, _ := cmd.Flags().GetString("output")
func nodesToPtables(
currentUser string,
showTags bool,
nodes []*v1.Node,
) (pterm.TableData, error) {
tableHeader := []string{
"ID",
"Hostname",
"Name",
"MachineKey",
"NodeKey",
"User",
"IP addresses",
"Ephemeral",
"Last seen",
"Expiration",
"Connected",
"Expired",
}
if showTags {
tableHeader = append(tableHeader, []string{
"ForcedTags",
"InvalidTags",
"ValidTags",
}...)
}
tableData := pterm.TableData{tableHeader}
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
_, err = h.GetNamespace(namespace)
if err != nil {
log.Fatalf("Error fetching origin namespace: %s", err)
}
destinationNamespace, err := h.GetNamespace(args[1])
if err != nil {
log.Fatalf("Error fetching destination namespace: %s", err)
}
id, err := strconv.Atoi(args[0])
if err != nil {
log.Fatalf("Error converting ID to integer: %s", err)
}
machine, err := h.GetMachineByID(uint64(id))
if err != nil {
log.Fatalf("Error getting node: %s", err)
}
err = h.AddSharedMachineToNamespace(machine, destinationNamespace)
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node shared"}, err, output)
return
}
if err != nil {
fmt.Printf("Error sharing node: %s\n", err)
return
}
fmt.Println("Node shared!")
},
}
var unshareMachineCmd = &cobra.Command{
Use: "unshare ID",
Short: "Unshares a node from the specified namespace",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
namespace, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
n, err := h.GetNamespace(namespace)
if err != nil {
log.Fatalf("Error fetching namespace: %s", err)
}
id, err := strconv.Atoi(args[0])
if err != nil {
log.Fatalf("Error converting ID to integer: %s", err)
}
machine, err := h.GetMachineByID(uint64(id))
if err != nil {
log.Fatalf("Error getting node: %s", err)
}
err = h.RemoveSharedMachineFromNamespace(machine, n)
if strings.HasPrefix(output, "json") {
JsonOutput(map[string]string{"Result": "Node unshared"}, err, output)
return
}
if err != nil {
fmt.Printf("Error unsharing node: %s\n", err)
return
}
fmt.Println("Node unshared!")
},
}
func nodesToPtables(currentNamespace headscale.Namespace, machines []headscale.Machine) (pterm.TableData, error) {
d := pterm.TableData{{"ID", "Name", "NodeKey", "Namespace", "IP address", "Ephemeral", "Last seen", "Online"}}
for _, machine := range machines {
for _, node := range nodes {
var ephemeral bool
if machine.AuthKey != nil && machine.AuthKey.Ephemeral {
if node.GetPreAuthKey() != nil && node.GetPreAuthKey().GetEphemeral() {
ephemeral = true
}
var lastSeen time.Time
var lastSeenTime string
if machine.LastSeen != nil {
lastSeen = *machine.LastSeen
if node.GetLastSeen() != nil {
lastSeen = node.GetLastSeen().AsTime()
lastSeenTime = lastSeen.Format("2006-01-02 15:04:05")
}
nKey, err := wgkey.ParseHex(machine.NodeKey)
var expiry time.Time
var expiryTime string
if node.GetExpiry() != nil {
expiry = node.GetExpiry().AsTime()
expiryTime = expiry.Format("2006-01-02 15:04:05")
} else {
expiryTime = "N/A"
}
var machineKey key.MachinePublic
err := machineKey.UnmarshalText(
[]byte(node.GetMachineKey()),
)
if err != nil {
machineKey = key.MachinePublic{}
}
var nodeKey key.NodePublic
err = nodeKey.UnmarshalText(
[]byte(node.GetNodeKey()),
)
if err != nil {
return nil, err
}
nodeKey := tailcfg.NodeKey(nKey)
var online string
if lastSeen.After(time.Now().Add(-5 * time.Minute)) { // TODO: Find a better way to reliably show if online
online = pterm.LightGreen("true")
if node.GetOnline() {
online = pterm.LightGreen("online")
} else {
online = pterm.LightRed("false")
online = pterm.LightRed("offline")
}
var namespace string
if currentNamespace.ID == machine.NamespaceID {
namespace = pterm.LightMagenta(machine.Namespace.Name)
var expired string
if expiry.IsZero() || expiry.After(time.Now()) {
expired = pterm.LightGreen("no")
} else {
namespace = pterm.LightYellow(machine.Namespace.Name)
expired = pterm.LightRed("yes")
}
d = append(d, []string{strconv.FormatUint(machine.ID, 10), machine.Name, nodeKey.ShortString(), namespace, machine.IPAddress, strconv.FormatBool(ephemeral), lastSeenTime, online})
var forcedTags string
for _, tag := range node.GetForcedTags() {
forcedTags += "\n" + tag
}
forcedTags = strings.TrimLeft(forcedTags, "\n")
var invalidTags string
for _, tag := range node.GetInvalidTags() {
if !slices.Contains(node.GetForcedTags(), tag) {
invalidTags += "\n" + pterm.LightRed(tag)
}
}
invalidTags = strings.TrimLeft(invalidTags, "\n")
var validTags string
for _, tag := range node.GetValidTags() {
if !slices.Contains(node.GetForcedTags(), tag) {
validTags += "\n" + pterm.LightGreen(tag)
}
}
validTags = strings.TrimLeft(validTags, "\n")
var user string
if currentUser == "" || (currentUser == node.GetUser().GetName()) {
user = pterm.LightMagenta(node.GetUser().GetName())
} else {
// Shared into this user
user = pterm.LightYellow(node.GetUser().GetName())
}
var IPV4Address string
var IPV6Address string
for _, addr := range node.GetIpAddresses() {
if netip.MustParseAddr(addr).Is4() {
IPV4Address = addr
} else {
IPV6Address = addr
}
}
nodeData := []string{
strconv.FormatUint(node.GetId(), util.Base10),
node.GetName(),
node.GetGivenName(),
machineKey.ShortString(),
nodeKey.ShortString(),
user,
strings.Join([]string{IPV4Address, IPV6Address}, ", "),
strconv.FormatBool(ephemeral),
lastSeenTime,
expiryTime,
online,
expired,
}
if showTags {
nodeData = append(nodeData, []string{forcedTags, invalidTags, validTags}...)
}
tableData = append(
tableData,
nodeData,
)
}
return d, nil
return tableData, nil
}
func nodeRoutesToPtables(
nodes []*v1.Node,
) (pterm.TableData, error) {
tableHeader := []string{
"ID",
"Hostname",
"Approved",
"Available",
"Serving (Primary)",
}
tableData := pterm.TableData{tableHeader}
for _, node := range nodes {
nodeData := []string{
strconv.FormatUint(node.GetId(), util.Base10),
node.GetGivenName(),
strings.Join(node.GetApprovedRoutes(), "\n"),
strings.Join(node.GetAvailableRoutes(), "\n"),
strings.Join(node.GetSubnetRoutes(), "\n"),
}
tableData = append(
tableData,
nodeData,
)
}
return tableData, nil
}
var tagCmd = &cobra.Command{
Use: "tag",
Short: "Manage the tags of a node",
Aliases: []string{"tags", "t"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
// retrieve flags from CLI
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
tagsToSet, err := cmd.Flags().GetStringSlice("tags")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error retrieving list of tags to add to node, %v", err),
output,
)
}
// Sending tags to node
request := &v1.SetTagsRequest{
NodeId: identifier,
Tags: tagsToSet,
}
resp, err := client.SetTags(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error while sending tags to headscale: %s", err),
output,
)
}
if resp != nil {
SuccessOutput(
resp.GetNode(),
"Node updated",
output,
)
}
},
}
var approveRoutesCmd = &cobra.Command{
Use: "approve-routes",
Short: "Manage the approved routes of a node",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
// retrieve flags from CLI
identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error converting ID to integer: %s", err),
output,
)
}
routes, err := cmd.Flags().GetStringSlice("routes")
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error retrieving list of routes to add to node, %v", err),
output,
)
}
// Sending routes to node
request := &v1.SetApprovedRoutesRequest{
NodeId: identifier,
Routes: routes,
}
resp, err := client.SetApprovedRoutes(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error while sending routes to headscale: %s", err),
output,
)
}
if resp != nil {
SuccessOutput(
resp.GetNode(),
"Node updated",
output,
)
}
},
}

212
cmd/headscale/cli/policy.go Normal file
View File

@@ -0,0 +1,212 @@
package cli
import (
"fmt"
"io"
"os"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/db"
"github.com/juanfont/headscale/hscontrol/policy"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"tailscale.com/types/views"
)
const (
bypassFlag = "bypass-grpc-and-access-database-directly"
)
func init() {
rootCmd.AddCommand(policyCmd)
getPolicy.Flags().BoolP(bypassFlag, "", false, "Uses the headscale config to directly access the database, bypassing gRPC and does not require the server to be running")
policyCmd.AddCommand(getPolicy)
setPolicy.Flags().StringP("file", "f", "", "Path to a policy file in HuJSON format")
if err := setPolicy.MarkFlagRequired("file"); err != nil {
log.Fatal().Err(err).Msg("")
}
setPolicy.Flags().BoolP(bypassFlag, "", false, "Uses the headscale config to directly access the database, bypassing gRPC and does not require the server to be running")
policyCmd.AddCommand(setPolicy)
checkPolicy.Flags().StringP("file", "f", "", "Path to a policy file in HuJSON format")
if err := checkPolicy.MarkFlagRequired("file"); err != nil {
log.Fatal().Err(err).Msg("")
}
policyCmd.AddCommand(checkPolicy)
}
var policyCmd = &cobra.Command{
Use: "policy",
Short: "Manage the Headscale ACL Policy",
}
var getPolicy = &cobra.Command{
Use: "get",
Short: "Print the current ACL Policy",
Aliases: []string{"show", "view", "fetch"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
var policy string
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
confirm := false
force, _ := cmd.Flags().GetBool("force")
if !force {
confirm = util.YesNo("DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?")
}
if !confirm && !force {
ErrorOutput(nil, "Aborting command", output)
return
}
cfg, err := types.LoadServerConfig()
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed loading config: %s", err), output)
}
d, err := db.NewHeadscaleDatabase(
cfg.Database,
cfg.BaseDomain,
nil,
)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed to open database: %s", err), output)
}
pol, err := d.GetPolicy()
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed loading Policy from database: %s", err), output)
}
policy = pol.Data
} else {
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.GetPolicyRequest{}
response, err := client.GetPolicy(ctx, request)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed loading ACL Policy: %s", err), output)
}
policy = response.GetPolicy()
}
// TODO(pallabpain): Maybe print this better?
// This does not pass output as we dont support yaml, json or json-line
// output for this command. It is HuJSON already.
SuccessOutput("", policy, "")
},
}
var setPolicy = &cobra.Command{
Use: "set",
Short: "Updates the ACL Policy",
Long: `
Updates the existing ACL Policy with the provided policy. The policy must be a valid HuJSON object.
This command only works when the acl.policy_mode is set to "db", and the policy will be stored in the database.`,
Aliases: []string{"put", "update"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
policyPath, _ := cmd.Flags().GetString("file")
f, err := os.Open(policyPath)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error opening the policy file: %s", err), output)
}
defer f.Close()
policyBytes, err := io.ReadAll(f)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error reading the policy file: %s", err), output)
}
if bypass, _ := cmd.Flags().GetBool(bypassFlag); bypass {
confirm := false
force, _ := cmd.Flags().GetBool("force")
if !force {
confirm = util.YesNo("DO NOT run this command if an instance of headscale is running, are you sure headscale is not running?")
}
if !confirm && !force {
ErrorOutput(nil, "Aborting command", output)
return
}
cfg, err := types.LoadServerConfig()
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed loading config: %s", err), output)
}
d, err := db.NewHeadscaleDatabase(
cfg.Database,
cfg.BaseDomain,
nil,
)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed to open database: %s", err), output)
}
users, err := d.ListUsers()
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed to load users for policy validation: %s", err), output)
}
_, err = policy.NewPolicyManager(policyBytes, users, views.Slice[types.NodeView]{})
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error parsing the policy file: %s", err), output)
return
}
_, err = d.SetPolicy(string(policyBytes))
if err != nil {
ErrorOutput(err, fmt.Sprintf("Failed to set ACL Policy: %s", err), output)
}
} else {
request := &v1.SetPolicyRequest{Policy: string(policyBytes)}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
if _, err := client.SetPolicy(ctx, request); err != nil {
ErrorOutput(err, fmt.Sprintf("Failed to set ACL Policy: %s", err), output)
}
}
SuccessOutput(nil, "Policy updated.", "")
},
}
var checkPolicy = &cobra.Command{
Use: "check",
Short: "Check the Policy file for errors",
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
policyPath, _ := cmd.Flags().GetString("file")
f, err := os.Open(policyPath)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error opening the policy file: %s", err), output)
}
defer f.Close()
policyBytes, err := io.ReadAll(f)
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error reading the policy file: %s", err), output)
}
_, err = policy.NewPolicyManager(policyBytes, nil, views.Slice[types.NodeView]{})
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error parsing the policy file: %s", err), output)
}
SuccessOutput(nil, "Policy is valid", "")
},
}

View File

@@ -2,173 +2,274 @@ package cli
import (
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/hako/durafmt"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/prometheus/common/model"
"github.com/pterm/pterm"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/known/timestamppb"
)
const (
DefaultPreAuthKeyExpiry = "1h"
)
func init() {
rootCmd.AddCommand(preauthkeysCmd)
preauthkeysCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace")
err := preauthkeysCmd.MarkPersistentFlagRequired("namespace")
preauthkeysCmd.PersistentFlags().Uint64P("user", "u", 0, "User identifier (ID)")
preauthkeysCmd.PersistentFlags().StringP("namespace", "n", "", "User")
pakNamespaceFlag := preauthkeysCmd.PersistentFlags().Lookup("namespace")
pakNamespaceFlag.Deprecated = deprecateNamespaceMessage
pakNamespaceFlag.Hidden = true
err := preauthkeysCmd.MarkPersistentFlagRequired("user")
if err != nil {
log.Fatalf(err.Error())
log.Fatal().Err(err).Msg("")
}
preauthkeysCmd.AddCommand(listPreAuthKeys)
preauthkeysCmd.AddCommand(createPreAuthKeyCmd)
preauthkeysCmd.AddCommand(expirePreAuthKeyCmd)
createPreAuthKeyCmd.PersistentFlags().Bool("reusable", false, "Make the preauthkey reusable")
createPreAuthKeyCmd.PersistentFlags().Bool("ephemeral", false, "Preauthkey for ephemeral nodes")
createPreAuthKeyCmd.Flags().StringP("expiration", "e", "", "Human-readable expiration of the key (30m, 24h, 365d...)")
preauthkeysCmd.AddCommand(deletePreAuthKeyCmd)
createPreAuthKeyCmd.PersistentFlags().
Bool("reusable", false, "Make the preauthkey reusable")
createPreAuthKeyCmd.PersistentFlags().
Bool("ephemeral", false, "Preauthkey for ephemeral nodes")
createPreAuthKeyCmd.Flags().
StringP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
createPreAuthKeyCmd.Flags().
StringSlice("tags", []string{}, "Tags to automatically assign to node")
}
var preauthkeysCmd = &cobra.Command{
Use: "preauthkeys",
Short: "Handle the preauthkeys in Headscale",
Use: "preauthkeys",
Short: "Handle the preauthkeys in Headscale",
Aliases: []string{"preauthkey", "authkey", "pre"},
}
var listPreAuthKeys = &cobra.Command{
Use: "list",
Short: "List the preauthkeys for this namespace",
Use: "list",
Short: "List the preauthkeys for this user",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
o, _ := cmd.Flags().GetString("output")
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
user, err := cmd.Flags().GetUint64("user")
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
keys, err := h.GetPreAuthKeys(n)
if strings.HasPrefix(o, "json") {
JsonOutput(keys, err, o)
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ListPreAuthKeysRequest{
User: user,
}
response, err := client.ListPreAuthKeys(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting the list of keys: %s", err),
output,
)
return
}
if err != nil {
fmt.Printf("Error getting the list of keys: %s\n", err)
return
if output != "" {
SuccessOutput(response.GetPreAuthKeys(), "", output)
}
d := pterm.TableData{{"ID", "Key", "Reusable", "Ephemeral", "Used", "Expiration", "Created"}}
for _, k := range *keys {
tableData := pterm.TableData{
{
"ID",
"Key/Prefix",
"Reusable",
"Ephemeral",
"Used",
"Expiration",
"Created",
"Tags",
},
}
for _, key := range response.GetPreAuthKeys() {
expiration := "-"
if k.Expiration != nil {
expiration = k.Expiration.Format("2006-01-02 15:04:05")
if key.GetExpiration() != nil {
expiration = ColourTime(key.GetExpiration().AsTime())
}
var reusable string
if k.Ephemeral {
reusable = "N/A"
} else {
reusable = fmt.Sprintf("%v", k.Reusable)
aclTags := ""
for _, tag := range key.GetAclTags() {
aclTags += "\n" + tag
}
d = append(d, []string{
strconv.FormatUint(k.ID, 10),
k.Key,
reusable,
strconv.FormatBool(k.Ephemeral),
fmt.Sprintf("%v", k.Used),
aclTags = strings.TrimLeft(aclTags, "\n")
tableData = append(tableData, []string{
strconv.FormatUint(key.GetId(), 10),
key.GetKey(),
strconv.FormatBool(key.GetReusable()),
strconv.FormatBool(key.GetEphemeral()),
strconv.FormatBool(key.GetUsed()),
expiration,
k.CreatedAt.Format("2006-01-02 15:04:05"),
key.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"),
aclTags,
})
}
err = pterm.DefaultTable.WithHasHeader().WithData(d).Render()
err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
if err != nil {
log.Fatal(err)
ErrorOutput(
err,
fmt.Sprintf("Failed to render pterm table: %s", err),
output,
)
}
},
}
var createPreAuthKeyCmd = &cobra.Command{
Use: "create",
Short: "Creates a new preauthkey in the specified namespace",
Use: "create",
Short: "Creates a new preauthkey in the specified user",
Aliases: []string{"c", "new"},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
o, _ := cmd.Flags().GetString("output")
output, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
user, err := cmd.Flags().GetUint64("user")
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
reusable, _ := cmd.Flags().GetBool("reusable")
ephemeral, _ := cmd.Flags().GetBool("ephemeral")
tags, _ := cmd.Flags().GetStringSlice("tags")
e, _ := cmd.Flags().GetString("expiration")
var expiration *time.Time
if e != "" {
duration, err := durafmt.ParseStringShort(e)
if err != nil {
log.Fatalf("Error parsing expiration: %s", err)
}
exp := time.Now().UTC().Add(duration.Duration())
expiration = &exp
request := &v1.CreatePreAuthKeyRequest{
User: user,
Reusable: reusable,
Ephemeral: ephemeral,
AclTags: tags,
}
k, err := h.CreatePreAuthKey(n, reusable, ephemeral, expiration)
if strings.HasPrefix(o, "json") {
JsonOutput(k, err, o)
return
}
durationStr, _ := cmd.Flags().GetString("expiration")
duration, err := model.ParseDuration(durationStr)
if err != nil {
fmt.Println(err)
return
ErrorOutput(
err,
fmt.Sprintf("Could not parse duration: %s\n", err),
output,
)
}
fmt.Printf("%s\n", k.Key)
expiration := time.Now().UTC().Add(time.Duration(duration))
log.Trace().
Dur("expiration", time.Duration(duration)).
Msg("expiration has been set")
request.Expiration = timestamppb.New(expiration)
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
response, err := client.CreatePreAuthKey(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Cannot create Pre Auth Key: %s\n", err),
output,
)
}
SuccessOutput(response.GetPreAuthKey(), response.GetPreAuthKey().GetKey(), output)
},
}
var expirePreAuthKeyCmd = &cobra.Command{
Use: "expire KEY",
Short: "Expire a preauthkey",
Use: "expire KEY",
Short: "Expire a preauthkey",
Aliases: []string{"revoke", "exp", "e"},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("missing parameters")
return errMissingParameter
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
output, _ := cmd.Flags().GetString("output")
user, err := cmd.Flags().GetUint64("user")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
k, err := h.GetPreAuthKey(n, args[0])
if err != nil {
if strings.HasPrefix(o, "json") {
JsonOutput(k, err, o)
return
}
log.Fatalf("Error getting the key: %s", err)
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ExpirePreAuthKeyRequest{
User: user,
Key: args[0],
}
err = h.MarkExpirePreAuthKey(k)
if strings.HasPrefix(o, "json") {
JsonOutput(k, err, o)
return
}
response, err := client.ExpirePreAuthKey(ctx, request)
if err != nil {
fmt.Println(err)
return
ErrorOutput(
err,
fmt.Sprintf("Cannot expire Pre Auth Key: %s\n", err),
output,
)
}
fmt.Println("Expired")
SuccessOutput(response, "Key expired", output)
},
}
var deletePreAuthKeyCmd = &cobra.Command{
Use: "delete KEY",
Short: "Delete a preauthkey",
Aliases: []string{"del", "rm", "d"},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errMissingParameter
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
user, err := cmd.Flags().GetUint64("user")
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error getting user: %s", err), output)
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.DeletePreAuthKeyRequest{
User: user,
Key: args[0],
}
response, err := client.DeletePreAuthKey(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Cannot delete Pre Auth Key: %s\n", err),
output,
)
}
SuccessOutput(response, "Key deleted", output)
},
}

View File

@@ -0,0 +1,19 @@
package cli
import (
"time"
"github.com/pterm/pterm"
)
func ColourTime(date time.Time) string {
dateStr := date.Format("2006-01-02 15:04:05")
if date.After(time.Now()) {
dateStr = pterm.LightGreen(dateStr)
} else {
dateStr = pterm.LightRed(dateStr)
}
return dateStr
}

View File

@@ -3,13 +3,131 @@ package cli
import (
"fmt"
"os"
"runtime"
"slices"
"strings"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tcnksm/go-latest"
)
const (
deprecateNamespaceMessage = "use --user"
)
var cfgFile string = ""
func init() {
rootCmd.PersistentFlags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json' or 'json-line'")
rootCmd.PersistentFlags().Bool("force", false, "Disable prompts and forces the execution")
if len(os.Args) > 1 &&
(os.Args[1] == "version" || os.Args[1] == "mockoidc" || os.Args[1] == "completion") {
return
}
if slices.Contains(os.Args, "policy") && slices.Contains(os.Args, "check") {
zerolog.SetGlobalLevel(zerolog.Disabled)
return
}
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().
StringVarP(&cfgFile, "config", "c", "", "config file (default is /etc/headscale/config.yaml)")
rootCmd.PersistentFlags().
StringP("output", "o", "", "Output format. Empty for human-readable, 'json', 'json-line' or 'yaml'")
rootCmd.PersistentFlags().
Bool("force", false, "Disable prompts and forces the execution")
}
func initConfig() {
if cfgFile == "" {
cfgFile = os.Getenv("HEADSCALE_CONFIG")
}
if cfgFile != "" {
err := types.LoadConfig(cfgFile, true)
if err != nil {
log.Fatal().Caller().Err(err).Msgf("Error loading config file %s", cfgFile)
}
} else {
err := types.LoadConfig("", false)
if err != nil {
log.Fatal().Caller().Err(err).Msgf("Error loading config")
}
}
machineOutput := HasMachineOutputFlag()
// If the user has requested a "node" readable format,
// then disable login so the output remains valid.
if machineOutput {
zerolog.SetGlobalLevel(zerolog.Disabled)
}
logFormat := viper.GetString("log.format")
if logFormat == types.JSONLogFormat {
log.Logger = log.Output(os.Stdout)
}
disableUpdateCheck := viper.GetBool("disable_check_updates")
if !disableUpdateCheck && !machineOutput {
versionInfo := types.GetVersionInfo()
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") &&
!versionInfo.Dirty {
githubTag := &latest.GithubTag{
Owner: "juanfont",
Repository: "headscale",
TagFilterFunc: filterPreReleasesIfStable(func() string { return versionInfo.Version }),
}
res, err := latest.Check(githubTag, versionInfo.Version)
if err == nil && res.Outdated {
//nolint
log.Warn().Msgf(
"An updated version of Headscale has been found (%s vs. your current %s). Check it out https://github.com/juanfont/headscale/releases\n",
res.Current,
versionInfo.Version,
)
}
}
}
}
var prereleases = []string{"alpha", "beta", "rc", "dev"}
func isPreReleaseVersion(version string) bool {
for _, unstable := range prereleases {
if strings.Contains(version, unstable) {
return true
}
}
return false
}
// filterPreReleasesIfStable returns a function that filters out
// pre-release tags if the current version is stable.
// If the current version is a pre-release, it does not filter anything.
// versionFunc is a function that returns the current version string, it is
// a func for testability.
func filterPreReleasesIfStable(versionFunc func() string) func(string) bool {
return func(tag string) bool {
version := versionFunc()
// If we are on a pre-release version, then we do not filter anything
// as we want to recommend the user the latest pre-release.
if isPreReleaseVersion(version) {
return false
}
// If we are on a stable release, filter out pre-releases.
for _, ignore := range prereleases {
if strings.Contains(tag, ignore) {
return true
}
}
return false
}
}
var rootCmd = &cobra.Command{

View File

@@ -0,0 +1,293 @@
package cli
import (
"testing"
)
func TestFilterPreReleasesIfStable(t *testing.T) {
tests := []struct {
name string
currentVersion string
tag string
expectedFilter bool
description string
}{
{
name: "stable version filters alpha tag",
currentVersion: "0.23.0",
tag: "v0.24.0-alpha.1",
expectedFilter: true,
description: "When on stable release, alpha tags should be filtered",
},
{
name: "stable version filters beta tag",
currentVersion: "0.23.0",
tag: "v0.24.0-beta.2",
expectedFilter: true,
description: "When on stable release, beta tags should be filtered",
},
{
name: "stable version filters rc tag",
currentVersion: "0.23.0",
tag: "v0.24.0-rc.1",
expectedFilter: true,
description: "When on stable release, rc tags should be filtered",
},
{
name: "stable version allows stable tag",
currentVersion: "0.23.0",
tag: "v0.24.0",
expectedFilter: false,
description: "When on stable release, stable tags should not be filtered",
},
{
name: "alpha version allows alpha tag",
currentVersion: "0.23.0-alpha.1",
tag: "v0.24.0-alpha.2",
expectedFilter: false,
description: "When on alpha release, alpha tags should not be filtered",
},
{
name: "alpha version allows beta tag",
currentVersion: "0.23.0-alpha.1",
tag: "v0.24.0-beta.1",
expectedFilter: false,
description: "When on alpha release, beta tags should not be filtered",
},
{
name: "alpha version allows rc tag",
currentVersion: "0.23.0-alpha.1",
tag: "v0.24.0-rc.1",
expectedFilter: false,
description: "When on alpha release, rc tags should not be filtered",
},
{
name: "alpha version allows stable tag",
currentVersion: "0.23.0-alpha.1",
tag: "v0.24.0",
expectedFilter: false,
description: "When on alpha release, stable tags should not be filtered",
},
{
name: "beta version allows alpha tag",
currentVersion: "0.23.0-beta.1",
tag: "v0.24.0-alpha.1",
expectedFilter: false,
description: "When on beta release, alpha tags should not be filtered",
},
{
name: "beta version allows beta tag",
currentVersion: "0.23.0-beta.2",
tag: "v0.24.0-beta.3",
expectedFilter: false,
description: "When on beta release, beta tags should not be filtered",
},
{
name: "beta version allows rc tag",
currentVersion: "0.23.0-beta.1",
tag: "v0.24.0-rc.1",
expectedFilter: false,
description: "When on beta release, rc tags should not be filtered",
},
{
name: "beta version allows stable tag",
currentVersion: "0.23.0-beta.1",
tag: "v0.24.0",
expectedFilter: false,
description: "When on beta release, stable tags should not be filtered",
},
{
name: "rc version allows alpha tag",
currentVersion: "0.23.0-rc.1",
tag: "v0.24.0-alpha.1",
expectedFilter: false,
description: "When on rc release, alpha tags should not be filtered",
},
{
name: "rc version allows beta tag",
currentVersion: "0.23.0-rc.1",
tag: "v0.24.0-beta.1",
expectedFilter: false,
description: "When on rc release, beta tags should not be filtered",
},
{
name: "rc version allows rc tag",
currentVersion: "0.23.0-rc.2",
tag: "v0.24.0-rc.3",
expectedFilter: false,
description: "When on rc release, rc tags should not be filtered",
},
{
name: "rc version allows stable tag",
currentVersion: "0.23.0-rc.1",
tag: "v0.24.0",
expectedFilter: false,
description: "When on rc release, stable tags should not be filtered",
},
{
name: "stable version with patch filters alpha",
currentVersion: "0.23.1",
tag: "v0.24.0-alpha.1",
expectedFilter: true,
description: "Stable version with patch number should filter alpha tags",
},
{
name: "stable version with patch allows stable",
currentVersion: "0.23.1",
tag: "v0.24.0",
expectedFilter: false,
description: "Stable version with patch number should allow stable tags",
},
{
name: "tag with alpha substring in version number",
currentVersion: "0.23.0",
tag: "v1.0.0-alpha.1",
expectedFilter: true,
description: "Tags with alpha in version string should be filtered on stable",
},
{
name: "tag with beta substring in version number",
currentVersion: "0.23.0",
tag: "v1.0.0-beta.1",
expectedFilter: true,
description: "Tags with beta in version string should be filtered on stable",
},
{
name: "tag with rc substring in version number",
currentVersion: "0.23.0",
tag: "v1.0.0-rc.1",
expectedFilter: true,
description: "Tags with rc in version string should be filtered on stable",
},
{
name: "empty tag on stable version",
currentVersion: "0.23.0",
tag: "",
expectedFilter: false,
description: "Empty tags should not be filtered",
},
{
name: "dev version allows all tags",
currentVersion: "0.23.0-dev",
tag: "v0.24.0-alpha.1",
expectedFilter: false,
description: "Dev versions should not filter any tags (pre-release allows all)",
},
{
name: "stable version filters dev tag",
currentVersion: "0.23.0",
tag: "v0.24.0-dev",
expectedFilter: true,
description: "When on stable release, dev tags should be filtered",
},
{
name: "dev version allows dev tag",
currentVersion: "0.23.0-dev",
tag: "v0.24.0-dev.1",
expectedFilter: false,
description: "When on dev release, dev tags should not be filtered",
},
{
name: "dev version allows stable tag",
currentVersion: "0.23.0-dev",
tag: "v0.24.0",
expectedFilter: false,
description: "When on dev release, stable tags should not be filtered",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := filterPreReleasesIfStable(func() string { return tt.currentVersion })(tt.tag)
if result != tt.expectedFilter {
t.Errorf("%s: got %v, want %v\nDescription: %s\nCurrent version: %s, Tag: %s",
tt.name,
result,
tt.expectedFilter,
tt.description,
tt.currentVersion,
tt.tag,
)
}
})
}
}
func TestIsPreReleaseVersion(t *testing.T) {
tests := []struct {
name string
version string
expected bool
description string
}{
{
name: "stable version",
version: "0.23.0",
expected: false,
description: "Stable version should not be pre-release",
},
{
name: "alpha version",
version: "0.23.0-alpha.1",
expected: true,
description: "Alpha version should be pre-release",
},
{
name: "beta version",
version: "0.23.0-beta.1",
expected: true,
description: "Beta version should be pre-release",
},
{
name: "rc version",
version: "0.23.0-rc.1",
expected: true,
description: "RC version should be pre-release",
},
{
name: "version with alpha substring",
version: "0.23.0-alphabetical",
expected: true,
description: "Version containing 'alpha' should be pre-release",
},
{
name: "version with beta substring",
version: "0.23.0-betamax",
expected: true,
description: "Version containing 'beta' should be pre-release",
},
{
name: "dev version",
version: "0.23.0-dev",
expected: true,
description: "Dev version should be pre-release",
},
{
name: "empty version",
version: "",
expected: false,
description: "Empty version should not be pre-release",
},
{
name: "version with patch number",
version: "0.23.1",
expected: false,
description: "Stable version with patch should not be pre-release",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := isPreReleaseVersion(tt.version)
if result != tt.expected {
t.Errorf("%s: got %v, want %v\nDescription: %s\nVersion: %s",
tt.name,
result,
tt.expected,
tt.description,
tt.version,
)
}
})
}
}

View File

@@ -1,147 +0,0 @@
package cli
import (
"fmt"
"log"
"strings"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(routesCmd)
routesCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace")
err := routesCmd.MarkPersistentFlagRequired("namespace")
if err != nil {
log.Fatalf(err.Error())
}
enableRouteCmd.Flags().BoolP("all", "a", false, "Enable all routes advertised by the node")
routesCmd.AddCommand(listRoutesCmd)
routesCmd.AddCommand(enableRouteCmd)
}
var routesCmd = &cobra.Command{
Use: "routes",
Short: "Manage the routes of Headscale",
}
var listRoutesCmd = &cobra.Command{
Use: "list NODE",
Short: "List the routes exposed by this node",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Missing parameters")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
o, _ := cmd.Flags().GetString("output")
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
availableRoutes, err := h.GetAdvertisedNodeRoutes(n, args[0])
if err != nil {
fmt.Println(err)
return
}
if strings.HasPrefix(o, "json") {
// TODO: Add enable/disabled information to this interface
JsonOutput(availableRoutes, err, o)
return
}
d := h.RoutesToPtables(n, args[0], *availableRoutes)
err = pterm.DefaultTable.WithHasHeader().WithData(d).Render()
if err != nil {
log.Fatal(err)
}
},
}
var enableRouteCmd = &cobra.Command{
Use: "enable node-name route",
Short: "Allows exposing a route declared by this node to the rest of the nodes",
Args: func(cmd *cobra.Command, args []string) error {
all, err := cmd.Flags().GetBool("all")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
if all {
if len(args) < 1 {
return fmt.Errorf("Missing parameters")
}
return nil
} else {
if len(args) < 2 {
return fmt.Errorf("Missing parameters")
}
return nil
}
},
Run: func(cmd *cobra.Command, args []string) {
n, err := cmd.Flags().GetString("namespace")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
o, _ := cmd.Flags().GetString("output")
all, err := cmd.Flags().GetBool("all")
if err != nil {
log.Fatalf("Error getting namespace: %s", err)
}
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
if all {
availableRoutes, err := h.GetAdvertisedNodeRoutes(n, args[0])
if err != nil {
fmt.Println(err)
return
}
for _, availableRoute := range *availableRoutes {
err = h.EnableNodeRoute(n, args[0], availableRoute.String())
if err != nil {
fmt.Println(err)
return
}
if strings.HasPrefix(o, "json") {
JsonOutput(availableRoute, err, o)
} else {
fmt.Printf("Enabled route %s\n", availableRoute)
}
}
} else {
err = h.EnableNodeRoute(n, args[0], args[1])
if strings.HasPrefix(o, "json") {
JsonOutput(args[1], err, o)
return
}
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Enabled route %s\n", args[1])
}
},
}

View File

@@ -0,0 +1,40 @@
package cli
import (
"errors"
"fmt"
"net/http"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"github.com/tailscale/squibble"
)
func init() {
rootCmd.AddCommand(serveCmd)
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Launches the headscale server",
Args: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {
app, err := newHeadscaleServerWithConfig()
if err != nil {
var squibbleErr squibble.ValidationError
if errors.As(err, &squibbleErr) {
fmt.Printf("SQLite schema failed to validate:\n")
fmt.Println(squibbleErr.Diff)
}
log.Fatal().Caller().Err(err).Msg("Error initializing")
}
err = app.Serve()
if err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatal().Caller().Err(err).Msg("Headscale ran into an error and had to shut down.")
}
},
}

View File

@@ -1,30 +0,0 @@
package cli
import (
"log"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(serveCmd)
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Launches the headscale server",
Args: func(cmd *cobra.Command, args []string) error {
return nil
},
Run: func(cmd *cobra.Command, args []string) {
h, err := getHeadscaleApp()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
err = h.Serve()
if err != nil {
log.Fatalf("Error initializing: %s", err)
}
},
}

305
cmd/headscale/cli/users.go Normal file
View File

@@ -0,0 +1,305 @@
package cli
import (
"errors"
"fmt"
"net/url"
"strconv"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/pterm/pterm"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"google.golang.org/grpc/status"
)
func usernameAndIDFlag(cmd *cobra.Command) {
cmd.Flags().Int64P("identifier", "i", -1, "User identifier (ID)")
cmd.Flags().StringP("name", "n", "", "Username")
}
// usernameAndIDFromFlag returns the username and ID from the flags of the command.
// If both are empty, it will exit the program with an error.
func usernameAndIDFromFlag(cmd *cobra.Command) (uint64, string) {
username, _ := cmd.Flags().GetString("name")
identifier, _ := cmd.Flags().GetInt64("identifier")
if username == "" && identifier < 0 {
err := errors.New("--name or --identifier flag is required")
ErrorOutput(
err,
"Cannot rename user: "+status.Convert(err).Message(),
"",
)
}
return uint64(identifier), username
}
func init() {
rootCmd.AddCommand(userCmd)
userCmd.AddCommand(createUserCmd)
createUserCmd.Flags().StringP("display-name", "d", "", "Display name")
createUserCmd.Flags().StringP("email", "e", "", "Email")
createUserCmd.Flags().StringP("picture-url", "p", "", "Profile picture URL")
userCmd.AddCommand(listUsersCmd)
usernameAndIDFlag(listUsersCmd)
listUsersCmd.Flags().StringP("email", "e", "", "Email")
userCmd.AddCommand(destroyUserCmd)
usernameAndIDFlag(destroyUserCmd)
userCmd.AddCommand(renameUserCmd)
usernameAndIDFlag(renameUserCmd)
renameUserCmd.Flags().StringP("new-name", "r", "", "New username")
renameNodeCmd.MarkFlagRequired("new-name")
}
var errMissingParameter = errors.New("missing parameters")
var userCmd = &cobra.Command{
Use: "users",
Short: "Manage the users of Headscale",
Aliases: []string{"user", "namespace", "namespaces", "ns"},
}
var createUserCmd = &cobra.Command{
Use: "create NAME",
Short: "Creates a new user",
Aliases: []string{"c", "new"},
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errMissingParameter
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
userName := args[0]
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
log.Trace().Interface("client", client).Msg("Obtained gRPC client")
request := &v1.CreateUserRequest{Name: userName}
if displayName, _ := cmd.Flags().GetString("display-name"); displayName != "" {
request.DisplayName = displayName
}
if email, _ := cmd.Flags().GetString("email"); email != "" {
request.Email = email
}
if pictureURL, _ := cmd.Flags().GetString("picture-url"); pictureURL != "" {
if _, err := url.Parse(pictureURL); err != nil {
ErrorOutput(
err,
fmt.Sprintf(
"Invalid Picture URL: %s",
err,
),
output,
)
}
request.PictureUrl = pictureURL
}
log.Trace().Interface("request", request).Msg("Sending CreateUser request")
response, err := client.CreateUser(ctx, request)
if err != nil {
ErrorOutput(
err,
"Cannot create user: "+status.Convert(err).Message(),
output,
)
}
SuccessOutput(response.GetUser(), "User created", output)
},
}
var destroyUserCmd = &cobra.Command{
Use: "destroy --identifier ID or --name NAME",
Short: "Destroys a user",
Aliases: []string{"delete"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
id, username := usernameAndIDFromFlag(cmd)
request := &v1.ListUsersRequest{
Name: username,
Id: id,
}
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
users, err := client.ListUsers(ctx, request)
if err != nil {
ErrorOutput(
err,
"Error: "+status.Convert(err).Message(),
output,
)
}
if len(users.GetUsers()) != 1 {
err := errors.New("Unable to determine user to delete, query returned multiple users, use ID")
ErrorOutput(
err,
"Error: "+status.Convert(err).Message(),
output,
)
}
user := users.GetUsers()[0]
confirm := false
force, _ := cmd.Flags().GetBool("force")
if !force {
confirm = util.YesNo(fmt.Sprintf(
"Do you want to remove the user %q (%d) and any associated preauthkeys?",
user.GetName(), user.GetId(),
))
}
if confirm || force {
request := &v1.DeleteUserRequest{Id: user.GetId()}
response, err := client.DeleteUser(ctx, request)
if err != nil {
ErrorOutput(
err,
"Cannot destroy user: "+status.Convert(err).Message(),
output,
)
}
SuccessOutput(response, "User destroyed", output)
} else {
SuccessOutput(map[string]string{"Result": "User not destroyed"}, "User not destroyed", output)
}
},
}
var listUsersCmd = &cobra.Command{
Use: "list",
Short: "List all the users",
Aliases: []string{"ls", "show"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
request := &v1.ListUsersRequest{}
id, _ := cmd.Flags().GetInt64("identifier")
username, _ := cmd.Flags().GetString("name")
email, _ := cmd.Flags().GetString("email")
// filter by one param at most
switch {
case id > 0:
request.Id = uint64(id)
case username != "":
request.Name = username
case email != "":
request.Email = email
}
response, err := client.ListUsers(ctx, request)
if err != nil {
ErrorOutput(
err,
"Cannot get users: "+status.Convert(err).Message(),
output,
)
}
if output != "" {
SuccessOutput(response.GetUsers(), "", output)
}
tableData := pterm.TableData{{"ID", "Name", "Username", "Email", "Created"}}
for _, user := range response.GetUsers() {
tableData = append(
tableData,
[]string{
strconv.FormatUint(user.GetId(), 10),
user.GetDisplayName(),
user.GetName(),
user.GetEmail(),
user.GetCreatedAt().AsTime().Format("2006-01-02 15:04:05"),
},
)
}
err = pterm.DefaultTable.WithHasHeader().WithData(tableData).Render()
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to render pterm table: %s", err),
output,
)
}
},
}
var renameUserCmd = &cobra.Command{
Use: "rename",
Short: "Renames a user",
Aliases: []string{"mv"},
Run: func(cmd *cobra.Command, args []string) {
output, _ := cmd.Flags().GetString("output")
ctx, client, conn, cancel := newHeadscaleCLIWithConfig()
defer cancel()
defer conn.Close()
id, username := usernameAndIDFromFlag(cmd)
listReq := &v1.ListUsersRequest{
Name: username,
Id: id,
}
users, err := client.ListUsers(ctx, listReq)
if err != nil {
ErrorOutput(
err,
"Error: "+status.Convert(err).Message(),
output,
)
}
if len(users.GetUsers()) != 1 {
err := errors.New("Unable to determine user to delete, query returned multiple users, use ID")
ErrorOutput(
err,
"Error: "+status.Convert(err).Message(),
output,
)
}
newName, _ := cmd.Flags().GetString("new-name")
renameReq := &v1.RenameUserRequest{
OldId: id,
NewName: newName,
}
response, err := client.RenameUser(ctx, renameReq)
if err != nil {
ErrorOutput(
err,
"Cannot rename user: "+status.Convert(err).Message(),
output,
)
}
SuccessOutput(response.GetUser(), "User renamed", output)
},
}

View File

@@ -1,273 +1,209 @@
package cli
import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"time"
"github.com/juanfont/headscale"
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
"github.com/juanfont/headscale/hscontrol"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"gopkg.in/yaml.v3"
)
type ErrorOutput struct {
Error string
}
const (
HeadscaleDateTimeFormat = "2006-01-02 15:04:05"
SocketWritePermissions = 0o666
)
func LoadConfig(path string) error {
viper.SetConfigName("config")
if path == "" {
viper.AddConfigPath("/etc/headscale/")
viper.AddConfigPath("$HOME/.headscale")
viper.AddConfigPath(".")
} else {
// For testing
viper.AddConfigPath(path)
}
viper.AutomaticEnv()
viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache")
viper.SetDefault("tls_letsencrypt_challenge_type", "HTTP-01")
viper.SetDefault("ip_prefix", "100.64.0.0/10")
viper.SetDefault("log_level", "info")
viper.SetDefault("dns_config", nil)
err := viper.ReadInConfig()
func newHeadscaleServerWithConfig() (*hscontrol.Headscale, error) {
cfg, err := types.LoadServerConfig()
if err != nil {
return fmt.Errorf("Fatal error reading config file: %s \n", err)
return nil, fmt.Errorf(
"loading configuration: %w",
err,
)
}
// Collect any validation errors and return them all at once
var errorText string
if (viper.GetString("tls_letsencrypt_hostname") != "") && ((viper.GetString("tls_cert_path") != "") || (viper.GetString("tls_key_path") != "")) {
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
}
if (viper.GetString("tls_letsencrypt_hostname") != "") && (viper.GetString("tls_letsencrypt_challenge_type") == "TLS-ALPN-01") && (!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
log.Warn().
Msg("Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
}
if (viper.GetString("tls_letsencrypt_challenge_type") != "HTTP-01") && (viper.GetString("tls_letsencrypt_challenge_type") != "TLS-ALPN-01") {
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
}
if !strings.HasPrefix(viper.GetString("server_url"), "http://") && !strings.HasPrefix(viper.GetString("server_url"), "https://") {
errorText += "Fatal config error: server_url must start with https:// or http://\n"
}
if errorText != "" {
return errors.New(strings.TrimSuffix(errorText, "\n"))
} else {
return nil
}
}
func GetDNSConfig() (*tailcfg.DNSConfig, string) {
if viper.IsSet("dns_config") {
dnsConfig := &tailcfg.DNSConfig{}
if viper.IsSet("dns_config.nameservers") {
nameserversStr := viper.GetStringSlice("dns_config.nameservers")
nameservers := make([]netaddr.IP, len(nameserversStr))
resolvers := make([]dnstype.Resolver, len(nameserversStr))
for index, nameserverStr := range nameserversStr {
nameserver, err := netaddr.ParseIP(nameserverStr)
if err != nil {
log.Error().
Str("func", "getDNSConfig").
Err(err).
Msgf("Could not parse nameserver IP: %s", nameserverStr)
}
nameservers[index] = nameserver
resolvers[index] = dnstype.Resolver{
Addr: nameserver.String(),
}
}
dnsConfig.Nameservers = nameservers
dnsConfig.Resolvers = resolvers
}
if viper.IsSet("dns_config.domains") {
dnsConfig.Domains = viper.GetStringSlice("dns_config.domains")
}
if viper.IsSet("dns_config.magic_dns") {
magicDNS := viper.GetBool("dns_config.magic_dns")
if len(dnsConfig.Nameservers) > 0 {
dnsConfig.Proxied = magicDNS
} else if magicDNS {
log.Warn().
Msg("Warning: dns_config.magic_dns is set, but no nameservers are configured. Ignoring magic_dns.")
}
}
var baseDomain string
if viper.IsSet("dns_config.base_domain") {
baseDomain = viper.GetString("dns_config.base_domain")
} else {
baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled
}
return dnsConfig, baseDomain
}
return nil, ""
}
func absPath(path string) string {
// If a relative path is provided, prefix it with the the directory where
// the config file was found.
if (path != "") && !strings.HasPrefix(path, string(os.PathSeparator)) {
dir, _ := filepath.Split(viper.ConfigFileUsed())
if dir != "" {
path = filepath.Join(dir, path)
}
}
return path
}
func getHeadscaleApp() (*headscale.Headscale, error) {
derpPath := absPath(viper.GetString("derp_map_path"))
derpMap, err := loadDerpMap(derpPath)
app, err := hscontrol.NewHeadscale(cfg)
if err != nil {
log.Error().
Str("path", derpPath).
return nil, fmt.Errorf("creating new headscale: %w", err)
}
return app, nil
}
func newHeadscaleCLIWithConfig() (context.Context, v1.HeadscaleServiceClient, *grpc.ClientConn, context.CancelFunc) {
cfg, err := types.LoadCLIConfig()
if err != nil {
log.Fatal().
Err(err).
Msg("Could not load DERP servers map file")
Caller().
Msgf("Failed to load configuration")
os.Exit(-1) // we get here if logging is suppressed (i.e., json output)
}
// Minimum inactivity time out is keepalive timeout (60s) plus a few seconds
// to avoid races
minInactivityTimeout, _ := time.ParseDuration("65s")
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
err = fmt.Errorf("ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s\n", viper.GetString("ephemeral_node_inactivity_timeout"), minInactivityTimeout)
return nil, err
log.Debug().
Dur("timeout", cfg.CLI.Timeout).
Msgf("Setting timeout")
ctx, cancel := context.WithTimeout(context.Background(), cfg.CLI.Timeout)
grpcOptions := []grpc.DialOption{
grpc.WithBlock(),
}
dnsConfig, baseDomain := GetDNSConfig()
address := cfg.CLI.Address
cfg := headscale.Config{
ServerURL: viper.GetString("server_url"),
Addr: viper.GetString("listen_addr"),
PrivateKeyPath: absPath(viper.GetString("private_key_path")),
DerpMap: derpMap,
IPPrefix: netaddr.MustParseIPPrefix(viper.GetString("ip_prefix")),
BaseDomain: baseDomain,
// If the address is not set, we assume that we are on the server hosting hscontrol.
if address == "" {
log.Debug().
Str("socket", cfg.UnixSocket).
Msgf("HEADSCALE_CLI_ADDRESS environment is not set, connecting to unix socket.")
EphemeralNodeInactivityTimeout: viper.GetDuration("ephemeral_node_inactivity_timeout"),
address = cfg.UnixSocket
DBtype: viper.GetString("db_type"),
DBpath: absPath(viper.GetString("db_path")),
DBhost: viper.GetString("db_host"),
DBport: viper.GetInt("db_port"),
DBname: viper.GetString("db_name"),
DBuser: viper.GetString("db_user"),
DBpass: viper.GetString("db_pass"),
TLSLetsEncryptHostname: viper.GetString("tls_letsencrypt_hostname"),
TLSLetsEncryptListen: viper.GetString("tls_letsencrypt_listen"),
TLSLetsEncryptCacheDir: absPath(viper.GetString("tls_letsencrypt_cache_dir")),
TLSLetsEncryptChallengeType: viper.GetString("tls_letsencrypt_challenge_type"),
TLSCertPath: absPath(viper.GetString("tls_cert_path")),
TLSKeyPath: absPath(viper.GetString("tls_key_path")),
DNSConfig: dnsConfig,
ACMEEmail: viper.GetString("acme_email"),
ACMEURL: viper.GetString("acme_url"),
}
h, err := headscale.NewHeadscale(cfg)
if err != nil {
return nil, err
}
// We are doing this here, as in the future could be cool to have it also hot-reload
if viper.GetString("acl_policy_path") != "" {
aclPath := absPath(viper.GetString("acl_policy_path"))
err = h.LoadACLPolicy(aclPath)
// Try to give the user better feedback if we cannot write to the headscale
// socket.
socket, err := os.OpenFile(cfg.UnixSocket, os.O_WRONLY, SocketWritePermissions) // nolint
if err != nil {
log.Error().
Str("path", aclPath).
Err(err).
Msg("Could not load the ACL policy")
if os.IsPermission(err) {
log.Fatal().
Err(err).
Str("socket", cfg.UnixSocket).
Msgf("Unable to read/write to headscale socket, do you have the correct permissions?")
}
}
socket.Close()
grpcOptions = append(
grpcOptions,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(util.GrpcSocketDialer),
)
} else {
// If we are not connecting to a local server, require an API key for authentication
apiKey := cfg.CLI.APIKey
if apiKey == "" {
log.Fatal().Caller().Msgf("HEADSCALE_CLI_API_KEY environment variable needs to be set.")
}
grpcOptions = append(grpcOptions,
grpc.WithPerRPCCredentials(tokenAuth{
token: apiKey,
}),
)
if cfg.CLI.Insecure {
tlsConfig := &tls.Config{
// turn of gosec as we are intentionally setting
// insecure.
//nolint:gosec
InsecureSkipVerify: true,
}
grpcOptions = append(grpcOptions,
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
)
} else {
grpcOptions = append(grpcOptions,
grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")),
)
}
}
return h, nil
log.Trace().Caller().Str("address", address).Msg("Connecting via gRPC")
conn, err := grpc.DialContext(ctx, address, grpcOptions...)
if err != nil {
log.Fatal().Caller().Err(err).Msgf("Could not connect: %v", err)
os.Exit(-1) // we get here if logging is suppressed (i.e., json output)
}
client := v1.NewHeadscaleServiceClient(conn)
return ctx, client, conn, cancel
}
func loadDerpMap(path string) (*tailcfg.DERPMap, error) {
derpFile, err := os.Open(path)
if err != nil {
return nil, err
}
defer derpFile.Close()
var derpMap tailcfg.DERPMap
b, err := io.ReadAll(derpFile)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(b, &derpMap)
return &derpMap, err
}
func JsonOutput(result interface{}, errResult error, outputFormat string) {
var j []byte
func output(result any, override string, outputFormat string) string {
var jsonBytes []byte
var err error
switch outputFormat {
case "json":
if errResult != nil {
j, err = json.MarshalIndent(ErrorOutput{errResult.Error()}, "", "\t")
if err != nil {
log.Fatal().Err(err)
}
} else {
j, err = json.MarshalIndent(result, "", "\t")
if err != nil {
log.Fatal().Err(err)
}
jsonBytes, err = json.MarshalIndent(result, "", "\t")
if err != nil {
log.Fatal().Err(err).Msg("failed to unmarshal output")
}
case "json-line":
if errResult != nil {
j, err = json.Marshal(ErrorOutput{errResult.Error()})
if err != nil {
log.Fatal().Err(err)
}
} else {
j, err = json.Marshal(result)
if err != nil {
log.Fatal().Err(err)
}
jsonBytes, err = json.Marshal(result)
if err != nil {
log.Fatal().Err(err).Msg("failed to unmarshal output")
}
case "yaml":
jsonBytes, err = yaml.Marshal(result)
if err != nil {
log.Fatal().Err(err).Msg("failed to unmarshal output")
}
default:
// nolint
return override
}
fmt.Println(string(j))
return string(jsonBytes)
}
func HasJsonOutputFlag() bool {
// SuccessOutput prints the result to stdout and exits with status code 0.
func SuccessOutput(result any, override string, outputFormat string) {
fmt.Println(output(result, override, outputFormat))
os.Exit(0)
}
// ErrorOutput prints an error message to stderr and exits with status code 1.
func ErrorOutput(errResult error, override string, outputFormat string) {
type errOutput struct {
Error string `json:"error"`
}
var errorMessage string
if errResult != nil {
errorMessage = errResult.Error()
} else {
errorMessage = override
}
fmt.Fprintf(os.Stderr, "%s\n", output(errOutput{errorMessage}, override, outputFormat))
os.Exit(1)
}
func HasMachineOutputFlag() bool {
for _, arg := range os.Args {
if arg == "json" || arg == "json-line" {
if arg == "json" || arg == "json-line" || arg == "yaml" {
return true
}
}
return false
}
type tokenAuth struct {
token string
}
// Return value is mapped to request headers.
func (t tokenAuth) GetRequestMetadata(
ctx context.Context,
in ...string,
) (map[string]string, error) {
return map[string]string{
"authorization": "Bearer " + t.token,
}, nil
}
func (tokenAuth) RequireTransportSecurity() bool {
return true
}

View File

@@ -1,16 +1,13 @@
package cli
import (
"fmt"
"strings"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/spf13/cobra"
)
var Version = "dev"
func init() {
rootCmd.AddCommand(versionCmd)
versionCmd.Flags().StringP("output", "o", "", "Output format. Empty for human-readable, 'json', 'json-line' or 'yaml'")
}
var versionCmd = &cobra.Command{
@@ -18,11 +15,10 @@ var versionCmd = &cobra.Command{
Short: "Print the version.",
Long: "The version of headscale.",
Run: func(cmd *cobra.Command, args []string) {
o, _ := cmd.Flags().GetString("output")
if strings.HasPrefix(o, "json") {
JsonOutput(map[string]string{"version": Version}, nil, o)
return
}
fmt.Println(Version)
output, _ := cmd.Flags().GetString("output")
info := types.GetVersionInfo()
SuccessOutput(info, info.String(), output)
},
}

View File

@@ -1,17 +1,13 @@
package main
import (
"fmt"
"os"
"runtime"
"time"
"github.com/efekarakus/termcolor"
"github.com/jagottsicher/termcolor"
"github.com/juanfont/headscale/cmd/headscale/cli"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
"github.com/tcnksm/go-latest"
)
func main() {
@@ -23,6 +19,8 @@ func main() {
colors = true
case termcolor.LevelBasic:
colors = true
case termcolor.LevelNone:
colors = false
default:
// no color, return text as is.
colors = false
@@ -36,46 +34,10 @@ func main() {
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stdout,
Out: os.Stderr,
TimeFormat: time.RFC3339,
NoColor: !colors,
})
err := cli.LoadConfig("")
if err != nil {
log.Fatal().Err(err)
}
logLevel := viper.GetString("log_level")
switch logLevel {
case "trace":
zerolog.SetGlobalLevel(zerolog.TraceLevel)
case "debug":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
case "info":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "warn":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "error":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
default:
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
jsonOutput := cli.HasJsonOutputFlag()
if !viper.GetBool("disable_check_updates") && !jsonOutput {
if (runtime.GOOS == "linux" || runtime.GOOS == "darwin") && cli.Version != "dev" {
githubTag := &latest.GithubTag{
Owner: "juanfont",
Repository: "headscale",
}
res, err := latest.Check(githubTag, cli.Version)
if err == nil && res.Outdated {
fmt.Printf("An updated version of Headscale has been found (%s vs. your current %s). Check it out https://github.com/juanfont/headscale/releases\n",
res.Current, cli.Version)
}
}
}
cli.Execute()
}

View File

@@ -1,14 +1,13 @@
package main
import (
"fmt"
"io/ioutil"
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
"github.com/juanfont/headscale/cmd/headscale/cli"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/juanfont/headscale/hscontrol/util"
"github.com/spf13/viper"
"gopkg.in/check.v1"
)
@@ -25,11 +24,10 @@ func (s *Suite) SetUpSuite(c *check.C) {
}
func (s *Suite) TearDownSuite(c *check.C) {
}
func (*Suite) TestPostgresConfigLoading(c *check.C) {
tmpDir, err := ioutil.TempDir("", "headscale")
func (*Suite) TestConfigFileLoading(c *check.C) {
tmpDir, err := os.MkdirTemp("", "headscale")
if err != nil {
c.Fatal(err)
}
@@ -40,63 +38,40 @@ func (*Suite) TestPostgresConfigLoading(c *check.C) {
c.Fatal(err)
}
cfgFile := filepath.Join(tmpDir, "config.yaml")
// Symlink the example config file
err = os.Symlink(filepath.Clean(path+"/../../config.json.postgres.example"), filepath.Join(tmpDir, "config.json"))
err = os.Symlink(
filepath.Clean(path+"/../../config-example.yaml"),
cfgFile,
)
if err != nil {
c.Fatal(err)
}
// Load example config, it should load without validation errors
err = cli.LoadConfig(tmpDir)
err = types.LoadConfig(cfgFile, true)
c.Assert(err, check.IsNil)
// Test that config file was interpreted correctly
c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080")
c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080")
c.Assert(viper.GetString("derp_map_path"), check.Equals, "derp.yaml")
c.Assert(viper.GetString("db_type"), check.Equals, "postgres")
c.Assert(viper.GetString("db_port"), check.Equals, "5432")
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http")
c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1")
}
func (*Suite) TestSqliteConfigLoading(c *check.C) {
tmpDir, err := ioutil.TempDir("", "headscale")
if err != nil {
c.Fatal(err)
}
defer os.RemoveAll(tmpDir)
path, err := os.Getwd()
if err != nil {
c.Fatal(err)
}
// Symlink the example config file
err = os.Symlink(filepath.Clean(path+"/../../config.json.sqlite.example"), filepath.Join(tmpDir, "config.json"))
if err != nil {
c.Fatal(err)
}
// Load example config, it should load without validation errors
err = cli.LoadConfig(tmpDir)
c.Assert(err, check.IsNil)
// Test that config file was interpreted correctly
c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080")
c.Assert(viper.GetString("listen_addr"), check.Equals, "0.0.0.0:8080")
c.Assert(viper.GetString("derp_map_path"), check.Equals, "derp.yaml")
c.Assert(viper.GetString("db_type"), check.Equals, "sqlite3")
c.Assert(viper.GetString("db_path"), check.Equals, "db.sqlite")
c.Assert(viper.GetString("listen_addr"), check.Equals, "127.0.0.1:8080")
c.Assert(viper.GetString("metrics_listen_addr"), check.Equals, "127.0.0.1:9090")
c.Assert(viper.GetString("database.type"), check.Equals, "sqlite")
c.Assert(viper.GetString("database.sqlite.path"), check.Equals, "/var/lib/headscale/db.sqlite")
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http")
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(
util.GetFileMode("unix_socket_permission"),
check.Equals,
fs.FileMode(0o770),
)
c.Assert(viper.GetBool("logtail.enabled"), check.Equals, false)
}
func (*Suite) TestDNSConfigLoading(c *check.C) {
tmpDir, err := ioutil.TempDir("", "headscale")
func (*Suite) TestConfigLoading(c *check.C) {
tmpDir, err := os.MkdirTemp("", "headscale")
if err != nil {
c.Fatal(err)
}
@@ -108,56 +83,32 @@ func (*Suite) TestDNSConfigLoading(c *check.C) {
}
// Symlink the example config file
err = os.Symlink(filepath.Clean(path+"/../../config.json.sqlite.example"), filepath.Join(tmpDir, "config.json"))
err = os.Symlink(
filepath.Clean(path+"/../../config-example.yaml"),
filepath.Join(tmpDir, "config.yaml"),
)
if err != nil {
c.Fatal(err)
}
// Load example config, it should load without validation errors
err = cli.LoadConfig(tmpDir)
err = types.LoadConfig(tmpDir, false)
c.Assert(err, check.IsNil)
dnsConfig, baseDomain := cli.GetDNSConfig()
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.Proxied, check.Equals, true)
c.Assert(baseDomain, check.Equals, "example.com")
}
func writeConfig(c *check.C, tmpDir string, configYaml []byte) {
// Populate a custom config file
configFile := filepath.Join(tmpDir, "config.yaml")
err := ioutil.WriteFile(configFile, configYaml, 0644)
if err != nil {
c.Fatalf("Couldn't write file %s", configFile)
}
}
func (*Suite) TestTLSConfigValidation(c *check.C) {
tmpDir, err := ioutil.TempDir("", "headscale")
if err != nil {
c.Fatal(err)
}
//defer os.RemoveAll(tmpDir)
fmt.Println(tmpDir)
configYaml := []byte("---\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"\"\ntls_cert_path: \"abc.pem\"")
writeConfig(c, tmpDir, configYaml)
// Check configuration validation errors (1)
err = cli.LoadConfig(tmpDir)
c.Assert(err, check.NotNil)
// check.Matches can not handle multiline strings
tmp := strings.ReplaceAll(err.Error(), "\n", "***")
c.Assert(tmp, check.Matches, ".*Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both.*")
c.Assert(tmp, check.Matches, ".*Fatal config error: the only supported values for tls_letsencrypt_challenge_type are.*")
c.Assert(tmp, check.Matches, ".*Fatal config error: server_url must start with https:// or http://.*")
fmt.Println(tmp)
// Check configuration validation errors (2)
configYaml = []byte("---\nserver_url: \"http://127.0.0.1:8080\"\ntls_letsencrypt_hostname: \"example.com\"\ntls_letsencrypt_challenge_type: \"TLS-ALPN-01\"")
writeConfig(c, tmpDir, configYaml)
err = cli.LoadConfig(tmpDir)
c.Assert(err, check.IsNil)
// Test that config file was interpreted correctly
c.Assert(viper.GetString("server_url"), check.Equals, "http://127.0.0.1:8080")
c.Assert(viper.GetString("listen_addr"), check.Equals, "127.0.0.1:8080")
c.Assert(viper.GetString("metrics_listen_addr"), check.Equals, "127.0.0.1:9090")
c.Assert(viper.GetString("database.type"), check.Equals, "sqlite")
c.Assert(viper.GetString("database.sqlite.path"), check.Equals, "/var/lib/headscale/db.sqlite")
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http")
c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01")
c.Assert(
util.GetFileMode("unix_socket_permission"),
check.Equals,
fs.FileMode(0o770),
)
c.Assert(viper.GetBool("logtail.enabled"), check.Equals, false)
c.Assert(viper.GetBool("randomize_client_port"), check.Equals, false)
}

6
cmd/hi/README.md Normal file
View File

@@ -0,0 +1,6 @@
# hi
hi (headscale integration runner) is an entirely "vibe coded" wrapper around our
[integration test suite](../integration). It essentially runs the docker
commands for you with some added benefits of extracting resources like logs and
databases.

426
cmd/hi/cleanup.go Normal file
View File

@@ -0,0 +1,426 @@
package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/cenkalti/backoff/v5"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
)
// cleanupBeforeTest performs cleanup operations before running tests.
// Only removes stale (stopped/exited) test containers to avoid interfering with concurrent test runs.
func cleanupBeforeTest(ctx context.Context) error {
err := cleanupStaleTestContainers(ctx)
if err != nil {
return fmt.Errorf("failed to clean stale test containers: %w", err)
}
if err := pruneDockerNetworks(ctx); err != nil {
return fmt.Errorf("failed to prune networks: %w", err)
}
return nil
}
// cleanupAfterTest removes the test container and all associated integration test containers for the run.
func cleanupAfterTest(ctx context.Context, cli *client.Client, containerID, runID string) error {
// Remove the main test container
err := cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
Force: true,
})
if err != nil {
return fmt.Errorf("failed to remove test container: %w", err)
}
// Clean up integration test containers for this run only
if runID != "" {
err := killTestContainersByRunID(ctx, runID)
if err != nil {
return fmt.Errorf("failed to clean up containers for run %s: %w", runID, err)
}
}
return nil
}
// killTestContainers terminates and removes all test containers.
func killTestContainers(ctx context.Context) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
containers, err := cli.ContainerList(ctx, container.ListOptions{
All: true,
})
if err != nil {
return fmt.Errorf("failed to list containers: %w", err)
}
removed := 0
for _, cont := range containers {
shouldRemove := false
for _, name := range cont.Names {
if strings.Contains(name, "headscale-test-suite") ||
strings.Contains(name, "hs-") ||
strings.Contains(name, "ts-") ||
strings.Contains(name, "derp-") {
shouldRemove = true
break
}
}
if shouldRemove {
// First kill the container if it's running
if cont.State == "running" {
_ = cli.ContainerKill(ctx, cont.ID, "KILL")
}
// Then remove the container with retry logic
if removeContainerWithRetry(ctx, cli, cont.ID) {
removed++
}
}
}
if removed > 0 {
fmt.Printf("Removed %d test containers\n", removed)
} else {
fmt.Println("No test containers found to remove")
}
return nil
}
// killTestContainersByRunID terminates and removes all test containers for a specific run ID.
// This function filters containers by the hi.run-id label to only affect containers
// belonging to the specified test run, leaving other concurrent test runs untouched.
func killTestContainersByRunID(ctx context.Context, runID string) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
// Filter containers by hi.run-id label
containers, err := cli.ContainerList(ctx, container.ListOptions{
All: true,
Filters: filters.NewArgs(
filters.Arg("label", "hi.run-id="+runID),
),
})
if err != nil {
return fmt.Errorf("failed to list containers for run %s: %w", runID, err)
}
removed := 0
for _, cont := range containers {
// Kill the container if it's running
if cont.State == "running" {
_ = cli.ContainerKill(ctx, cont.ID, "KILL")
}
// Remove the container with retry logic
if removeContainerWithRetry(ctx, cli, cont.ID) {
removed++
}
}
if removed > 0 {
fmt.Printf("Removed %d containers for run ID %s\n", removed, runID)
}
return nil
}
// cleanupStaleTestContainers removes stopped/exited test containers without affecting running tests.
// This is useful for cleaning up leftover containers from previous crashed or interrupted test runs
// without interfering with currently running concurrent tests.
func cleanupStaleTestContainers(ctx context.Context) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
// Only get stopped/exited containers
containers, err := cli.ContainerList(ctx, container.ListOptions{
All: true,
Filters: filters.NewArgs(
filters.Arg("status", "exited"),
filters.Arg("status", "dead"),
),
})
if err != nil {
return fmt.Errorf("failed to list stopped containers: %w", err)
}
removed := 0
for _, cont := range containers {
// Only remove containers that look like test containers
shouldRemove := false
for _, name := range cont.Names {
if strings.Contains(name, "headscale-test-suite") ||
strings.Contains(name, "hs-") ||
strings.Contains(name, "ts-") ||
strings.Contains(name, "derp-") {
shouldRemove = true
break
}
}
if shouldRemove {
if removeContainerWithRetry(ctx, cli, cont.ID) {
removed++
}
}
}
if removed > 0 {
fmt.Printf("Removed %d stale test containers\n", removed)
}
return nil
}
const (
containerRemoveInitialInterval = 100 * time.Millisecond
containerRemoveMaxElapsedTime = 2 * time.Second
)
// removeContainerWithRetry attempts to remove a container with exponential backoff retry logic.
func removeContainerWithRetry(ctx context.Context, cli *client.Client, containerID string) bool {
expBackoff := backoff.NewExponentialBackOff()
expBackoff.InitialInterval = containerRemoveInitialInterval
_, err := backoff.Retry(ctx, func() (struct{}, error) {
err := cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
Force: true,
})
if err != nil {
return struct{}{}, err
}
return struct{}{}, nil
}, backoff.WithBackOff(expBackoff), backoff.WithMaxElapsedTime(containerRemoveMaxElapsedTime))
return err == nil
}
// pruneDockerNetworks removes unused Docker networks.
func pruneDockerNetworks(ctx context.Context) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
report, err := cli.NetworksPrune(ctx, filters.Args{})
if err != nil {
return fmt.Errorf("failed to prune networks: %w", err)
}
if len(report.NetworksDeleted) > 0 {
fmt.Printf("Removed %d unused networks\n", len(report.NetworksDeleted))
} else {
fmt.Println("No unused networks found to remove")
}
return nil
}
// cleanOldImages removes test-related and old dangling Docker images.
func cleanOldImages(ctx context.Context) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
images, err := cli.ImageList(ctx, image.ListOptions{
All: true,
})
if err != nil {
return fmt.Errorf("failed to list images: %w", err)
}
removed := 0
for _, img := range images {
shouldRemove := false
for _, tag := range img.RepoTags {
if strings.Contains(tag, "hs-") ||
strings.Contains(tag, "headscale-integration") ||
strings.Contains(tag, "tailscale") {
shouldRemove = true
break
}
}
if len(img.RepoTags) == 0 && time.Unix(img.Created, 0).Before(time.Now().Add(-7*24*time.Hour)) {
shouldRemove = true
}
if shouldRemove {
_, err := cli.ImageRemove(ctx, img.ID, image.RemoveOptions{
Force: true,
})
if err == nil {
removed++
}
}
}
if removed > 0 {
fmt.Printf("Removed %d test images\n", removed)
} else {
fmt.Println("No test images found to remove")
}
return nil
}
// cleanCacheVolume removes the Docker volume used for Go module cache.
func cleanCacheVolume(ctx context.Context) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
volumeName := "hs-integration-go-cache"
err = cli.VolumeRemove(ctx, volumeName, true)
if err != nil {
if errdefs.IsNotFound(err) {
fmt.Printf("Go module cache volume not found: %s\n", volumeName)
} else if errdefs.IsConflict(err) {
fmt.Printf("Go module cache volume is in use and cannot be removed: %s\n", volumeName)
} else {
fmt.Printf("Failed to remove Go module cache volume %s: %v\n", volumeName, err)
}
} else {
fmt.Printf("Removed Go module cache volume: %s\n", volumeName)
}
return nil
}
// cleanupSuccessfulTestArtifacts removes artifacts from successful test runs to save disk space.
// This function removes large artifacts that are mainly useful for debugging failures:
// - Database dumps (.db files)
// - Profile data (pprof directories)
// - MapResponse data (mapresponses directories)
// - Prometheus metrics files
//
// It preserves:
// - Log files (.log) which are small and useful for verification.
func cleanupSuccessfulTestArtifacts(logsDir string, verbose bool) error {
entries, err := os.ReadDir(logsDir)
if err != nil {
return fmt.Errorf("failed to read logs directory: %w", err)
}
var (
removedFiles, removedDirs int
totalSize int64
)
for _, entry := range entries {
name := entry.Name()
fullPath := filepath.Join(logsDir, name)
if entry.IsDir() {
// Remove pprof and mapresponses directories (typically large)
// These directories contain artifacts from all containers in the test run
if name == "pprof" || name == "mapresponses" {
size, sizeErr := getDirSize(fullPath)
if sizeErr == nil {
totalSize += size
}
err := os.RemoveAll(fullPath)
if err != nil {
if verbose {
log.Printf("Warning: failed to remove directory %s: %v", name, err)
}
} else {
removedDirs++
if verbose {
log.Printf("Removed directory: %s/", name)
}
}
}
} else {
// Only process test-related files (headscale and tailscale)
if !strings.HasPrefix(name, "hs-") && !strings.HasPrefix(name, "ts-") {
continue
}
// Remove database, metrics, and status files, but keep logs
shouldRemove := strings.HasSuffix(name, ".db") ||
strings.HasSuffix(name, "_metrics.txt") ||
strings.HasSuffix(name, "_status.json")
if shouldRemove {
info, infoErr := entry.Info()
if infoErr == nil {
totalSize += info.Size()
}
err := os.Remove(fullPath)
if err != nil {
if verbose {
log.Printf("Warning: failed to remove file %s: %v", name, err)
}
} else {
removedFiles++
if verbose {
log.Printf("Removed file: %s", name)
}
}
}
}
}
if removedFiles > 0 || removedDirs > 0 {
const bytesPerMB = 1024 * 1024
log.Printf("Cleaned up %d files and %d directories (freed ~%.2f MB)",
removedFiles, removedDirs, float64(totalSize)/bytesPerMB)
}
return nil
}
// getDirSize calculates the total size of a directory.
func getDirSize(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
size += info.Size()
}
return nil
})
return size, err
}

767
cmd/hi/docker.go Normal file
View File

@@ -0,0 +1,767 @@
package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/juanfont/headscale/integration/dockertestutil"
)
var (
ErrTestFailed = errors.New("test failed")
ErrUnexpectedContainerWait = errors.New("unexpected end of container wait")
ErrNoDockerContext = errors.New("no docker context found")
)
// runTestContainer executes integration tests in a Docker container.
func runTestContainer(ctx context.Context, config *RunConfig) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
runID := dockertestutil.GenerateRunID()
containerName := "headscale-test-suite-" + runID
logsDir := filepath.Join(config.LogsDir, runID)
if config.Verbose {
log.Printf("Run ID: %s", runID)
log.Printf("Container name: %s", containerName)
log.Printf("Logs directory: %s", logsDir)
}
absLogsDir, err := filepath.Abs(logsDir)
if err != nil {
return fmt.Errorf("failed to get absolute path for logs directory: %w", err)
}
const dirPerm = 0o755
if err := os.MkdirAll(absLogsDir, dirPerm); err != nil {
return fmt.Errorf("failed to create logs directory: %w", err)
}
if config.CleanBefore {
if config.Verbose {
log.Printf("Running pre-test cleanup...")
}
if err := cleanupBeforeTest(ctx); err != nil && config.Verbose {
log.Printf("Warning: pre-test cleanup failed: %v", err)
}
}
goTestCmd := buildGoTestCommand(config)
if config.Verbose {
log.Printf("Command: %s", strings.Join(goTestCmd, " "))
}
imageName := "golang:" + config.GoVersion
if err := ensureImageAvailable(ctx, cli, imageName, config.Verbose); err != nil {
return fmt.Errorf("failed to ensure image availability: %w", err)
}
resp, err := createGoTestContainer(ctx, cli, config, containerName, absLogsDir, goTestCmd)
if err != nil {
return fmt.Errorf("failed to create container: %w", err)
}
if config.Verbose {
log.Printf("Created container: %s", resp.ID)
}
if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
return fmt.Errorf("failed to start container: %w", err)
}
log.Printf("Starting test: %s", config.TestPattern)
log.Printf("Run ID: %s", runID)
log.Printf("Monitor with: docker logs -f %s", containerName)
log.Printf("Logs directory: %s", logsDir)
// Start stats collection for container resource monitoring (if enabled)
var statsCollector *StatsCollector
if config.Stats {
var err error
statsCollector, err = NewStatsCollector()
if err != nil {
if config.Verbose {
log.Printf("Warning: failed to create stats collector: %v", err)
}
statsCollector = nil
}
if statsCollector != nil {
defer statsCollector.Close()
// Start stats collection immediately - no need for complex retry logic
// The new implementation monitors Docker events and will catch containers as they start
if err := statsCollector.StartCollection(ctx, runID, config.Verbose); err != nil {
if config.Verbose {
log.Printf("Warning: failed to start stats collection: %v", err)
}
}
defer statsCollector.StopCollection()
}
}
exitCode, err := streamAndWait(ctx, cli, resp.ID)
// Ensure all containers have finished and logs are flushed before extracting artifacts
if waitErr := waitForContainerFinalization(ctx, cli, resp.ID, config.Verbose); waitErr != nil && config.Verbose {
log.Printf("Warning: failed to wait for container finalization: %v", waitErr)
}
// Extract artifacts from test containers before cleanup
if err := extractArtifactsFromContainers(ctx, resp.ID, logsDir, config.Verbose); err != nil && config.Verbose {
log.Printf("Warning: failed to extract artifacts from containers: %v", err)
}
// Always list control files regardless of test outcome
listControlFiles(logsDir)
// Print stats summary and check memory limits if enabled
if config.Stats && statsCollector != nil {
violations := statsCollector.PrintSummaryAndCheckLimits(config.HSMemoryLimit, config.TSMemoryLimit)
if len(violations) > 0 {
log.Printf("MEMORY LIMIT VIOLATIONS DETECTED:")
log.Printf("=================================")
for _, violation := range violations {
log.Printf("Container %s exceeded memory limit: %.1f MB > %.1f MB",
violation.ContainerName, violation.MaxMemoryMB, violation.LimitMB)
}
return fmt.Errorf("test failed: %d container(s) exceeded memory limits", len(violations))
}
}
shouldCleanup := config.CleanAfter && (!config.KeepOnFailure || exitCode == 0)
if shouldCleanup {
if config.Verbose {
log.Printf("Running post-test cleanup for run %s...", runID)
}
cleanErr := cleanupAfterTest(ctx, cli, resp.ID, runID)
if cleanErr != nil && config.Verbose {
log.Printf("Warning: post-test cleanup failed: %v", cleanErr)
}
// Clean up artifacts from successful tests to save disk space in CI
if exitCode == 0 {
if config.Verbose {
log.Printf("Test succeeded, cleaning up artifacts to save disk space...")
}
cleanErr := cleanupSuccessfulTestArtifacts(logsDir, config.Verbose)
if cleanErr != nil && config.Verbose {
log.Printf("Warning: artifact cleanup failed: %v", cleanErr)
}
}
}
if err != nil {
return fmt.Errorf("test execution failed: %w", err)
}
if exitCode != 0 {
return fmt.Errorf("%w: exit code %d", ErrTestFailed, exitCode)
}
log.Printf("Test completed successfully!")
return nil
}
// buildGoTestCommand constructs the go test command arguments.
func buildGoTestCommand(config *RunConfig) []string {
cmd := []string{"go", "test", "./..."}
if config.TestPattern != "" {
cmd = append(cmd, "-run", config.TestPattern)
}
if config.FailFast {
cmd = append(cmd, "-failfast")
}
cmd = append(cmd, "-timeout", config.Timeout.String())
cmd = append(cmd, "-v")
return cmd
}
// createGoTestContainer creates a Docker container configured for running integration tests.
func createGoTestContainer(ctx context.Context, cli *client.Client, config *RunConfig, containerName, logsDir string, goTestCmd []string) (container.CreateResponse, error) {
pwd, err := os.Getwd()
if err != nil {
return container.CreateResponse{}, fmt.Errorf("failed to get working directory: %w", err)
}
projectRoot := findProjectRoot(pwd)
runID := dockertestutil.ExtractRunIDFromContainerName(containerName)
env := []string{
fmt.Sprintf("HEADSCALE_INTEGRATION_POSTGRES=%d", boolToInt(config.UsePostgres)),
"HEADSCALE_INTEGRATION_RUN_ID=" + runID,
}
// Pass through CI environment variable for CI detection
if ci := os.Getenv("CI"); ci != "" {
env = append(env, "CI="+ci)
}
// Pass through all HEADSCALE_INTEGRATION_* environment variables
for _, e := range os.Environ() {
if strings.HasPrefix(e, "HEADSCALE_INTEGRATION_") {
// Skip the ones we already set explicitly
if strings.HasPrefix(e, "HEADSCALE_INTEGRATION_POSTGRES=") ||
strings.HasPrefix(e, "HEADSCALE_INTEGRATION_RUN_ID=") {
continue
}
env = append(env, e)
}
}
// Set GOCACHE to a known location (used by both bind mount and volume cases)
env = append(env, "GOCACHE=/cache/go-build")
containerConfig := &container.Config{
Image: "golang:" + config.GoVersion,
Cmd: goTestCmd,
Env: env,
WorkingDir: projectRoot + "/integration",
Tty: true,
Labels: map[string]string{
"hi.run-id": runID,
"hi.test-type": "test-runner",
},
}
// Get the correct Docker socket path from the current context
dockerSocketPath := getDockerSocketPath()
if config.Verbose {
log.Printf("Using Docker socket: %s", dockerSocketPath)
}
binds := []string{
fmt.Sprintf("%s:%s", projectRoot, projectRoot),
dockerSocketPath + ":/var/run/docker.sock",
logsDir + ":/tmp/control",
}
// Use bind mounts for Go cache if provided via environment variables,
// otherwise fall back to Docker volumes for local development
var mounts []mount.Mount
goCache := os.Getenv("HEADSCALE_INTEGRATION_GO_CACHE")
goBuildCache := os.Getenv("HEADSCALE_INTEGRATION_GO_BUILD_CACHE")
if goCache != "" {
binds = append(binds, goCache+":/go")
} else {
mounts = append(mounts, mount.Mount{
Type: mount.TypeVolume,
Source: "hs-integration-go-cache",
Target: "/go",
})
}
if goBuildCache != "" {
binds = append(binds, goBuildCache+":/cache/go-build")
} else {
mounts = append(mounts, mount.Mount{
Type: mount.TypeVolume,
Source: "hs-integration-go-build-cache",
Target: "/cache/go-build",
})
}
hostConfig := &container.HostConfig{
AutoRemove: false, // We'll remove manually for better control
Binds: binds,
Mounts: mounts,
}
return cli.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, containerName)
}
// streamAndWait streams container output and waits for completion.
func streamAndWait(ctx context.Context, cli *client.Client, containerID string) (int, error) {
out, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true,
})
if err != nil {
return -1, fmt.Errorf("failed to get container logs: %w", err)
}
defer out.Close()
go func() {
_, _ = io.Copy(os.Stdout, out)
}()
statusCh, errCh := cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
return -1, fmt.Errorf("error waiting for container: %w", err)
}
case status := <-statusCh:
return int(status.StatusCode), nil
}
return -1, ErrUnexpectedContainerWait
}
// waitForContainerFinalization ensures all test containers have properly finished and flushed their output.
func waitForContainerFinalization(ctx context.Context, cli *client.Client, testContainerID string, verbose bool) error {
// First, get all related test containers
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
return fmt.Errorf("failed to list containers: %w", err)
}
testContainers := getCurrentTestContainers(containers, testContainerID, verbose)
// Wait for all test containers to reach a final state
maxWaitTime := 10 * time.Second
checkInterval := 500 * time.Millisecond
timeout := time.After(maxWaitTime)
ticker := time.NewTicker(checkInterval)
defer ticker.Stop()
for {
select {
case <-timeout:
if verbose {
log.Printf("Timeout waiting for container finalization, proceeding with artifact extraction")
}
return nil
case <-ticker.C:
allFinalized := true
for _, testCont := range testContainers {
inspect, err := cli.ContainerInspect(ctx, testCont.ID)
if err != nil {
if verbose {
log.Printf("Warning: failed to inspect container %s: %v", testCont.name, err)
}
continue
}
// Check if container is in a final state
if !isContainerFinalized(inspect.State) {
allFinalized = false
if verbose {
log.Printf("Container %s still finalizing (state: %s)", testCont.name, inspect.State.Status)
}
break
}
}
if allFinalized {
if verbose {
log.Printf("All test containers finalized, ready for artifact extraction")
}
return nil
}
}
}
}
// isContainerFinalized checks if a container has reached a final state where logs are flushed.
func isContainerFinalized(state *container.State) bool {
// Container is finalized if it's not running and has a finish time
return !state.Running && state.FinishedAt != ""
}
// findProjectRoot locates the project root by finding the directory containing go.mod.
func findProjectRoot(startPath string) string {
current := startPath
for {
if _, err := os.Stat(filepath.Join(current, "go.mod")); err == nil {
return current
}
parent := filepath.Dir(current)
if parent == current {
return startPath
}
current = parent
}
}
// boolToInt converts a boolean to an integer for environment variables.
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
// DockerContext represents Docker context information.
type DockerContext struct {
Name string `json:"Name"`
Metadata map[string]any `json:"Metadata"`
Endpoints map[string]any `json:"Endpoints"`
Current bool `json:"Current"`
}
// createDockerClient creates a Docker client with context detection.
func createDockerClient() (*client.Client, error) {
contextInfo, err := getCurrentDockerContext()
if err != nil {
return client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
}
var clientOpts []client.Opt
clientOpts = append(clientOpts, client.WithAPIVersionNegotiation())
if contextInfo != nil {
if endpoints, ok := contextInfo.Endpoints["docker"]; ok {
if endpointMap, ok := endpoints.(map[string]any); ok {
if host, ok := endpointMap["Host"].(string); ok {
if runConfig.Verbose {
log.Printf("Using Docker host from context '%s': %s", contextInfo.Name, host)
}
clientOpts = append(clientOpts, client.WithHost(host))
}
}
}
}
if len(clientOpts) == 1 {
clientOpts = append(clientOpts, client.FromEnv)
}
return client.NewClientWithOpts(clientOpts...)
}
// getCurrentDockerContext retrieves the current Docker context information.
func getCurrentDockerContext() (*DockerContext, error) {
cmd := exec.Command("docker", "context", "inspect")
output, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to get docker context: %w", err)
}
var contexts []DockerContext
if err := json.Unmarshal(output, &contexts); err != nil {
return nil, fmt.Errorf("failed to parse docker context: %w", err)
}
if len(contexts) > 0 {
return &contexts[0], nil
}
return nil, ErrNoDockerContext
}
// getDockerSocketPath returns the correct Docker socket path for the current context.
func getDockerSocketPath() string {
// Always use the default socket path for mounting since Docker handles
// the translation to the actual socket (e.g., colima socket) internally
return "/var/run/docker.sock"
}
// checkImageAvailableLocally checks if the specified Docker image is available locally.
func checkImageAvailableLocally(ctx context.Context, cli *client.Client, imageName string) (bool, error) {
_, _, err := cli.ImageInspectWithRaw(ctx, imageName)
if err != nil {
if client.IsErrNotFound(err) {
return false, nil
}
return false, fmt.Errorf("failed to inspect image %s: %w", imageName, err)
}
return true, nil
}
// ensureImageAvailable checks if the image is available locally first, then pulls if needed.
func ensureImageAvailable(ctx context.Context, cli *client.Client, imageName string, verbose bool) error {
// First check if image is available locally
available, err := checkImageAvailableLocally(ctx, cli, imageName)
if err != nil {
return fmt.Errorf("failed to check local image availability: %w", err)
}
if available {
if verbose {
log.Printf("Image %s is available locally", imageName)
}
return nil
}
// Image not available locally, try to pull it
if verbose {
log.Printf("Image %s not found locally, pulling...", imageName)
}
reader, err := cli.ImagePull(ctx, imageName, image.PullOptions{})
if err != nil {
return fmt.Errorf("failed to pull image %s: %w", imageName, err)
}
defer reader.Close()
if verbose {
_, err = io.Copy(os.Stdout, reader)
if err != nil {
return fmt.Errorf("failed to read pull output: %w", err)
}
} else {
_, err = io.Copy(io.Discard, reader)
if err != nil {
return fmt.Errorf("failed to read pull output: %w", err)
}
log.Printf("Image %s pulled successfully", imageName)
}
return nil
}
// listControlFiles displays the headscale test artifacts created in the control logs directory.
func listControlFiles(logsDir string) {
entries, err := os.ReadDir(logsDir)
if err != nil {
log.Printf("Logs directory: %s", logsDir)
return
}
var logFiles []string
var dataFiles []string
var dataDirs []string
for _, entry := range entries {
name := entry.Name()
// Only show headscale (hs-*) files and directories
if !strings.HasPrefix(name, "hs-") {
continue
}
if entry.IsDir() {
// Include directories (pprof, mapresponses)
if strings.Contains(name, "-pprof") || strings.Contains(name, "-mapresponses") {
dataDirs = append(dataDirs, name)
}
} else {
// Include files
switch {
case strings.HasSuffix(name, ".stderr.log") || strings.HasSuffix(name, ".stdout.log"):
logFiles = append(logFiles, name)
case strings.HasSuffix(name, ".db"):
dataFiles = append(dataFiles, name)
}
}
}
log.Printf("Test artifacts saved to: %s", logsDir)
if len(logFiles) > 0 {
log.Printf("Headscale logs:")
for _, file := range logFiles {
log.Printf(" %s", file)
}
}
if len(dataFiles) > 0 || len(dataDirs) > 0 {
log.Printf("Headscale data:")
for _, file := range dataFiles {
log.Printf(" %s", file)
}
for _, dir := range dataDirs {
log.Printf(" %s/", dir)
}
}
}
// extractArtifactsFromContainers collects container logs and files from the specific test run.
func extractArtifactsFromContainers(ctx context.Context, testContainerID, logsDir string, verbose bool) error {
cli, err := createDockerClient()
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
defer cli.Close()
// List all containers
containers, err := cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
return fmt.Errorf("failed to list containers: %w", err)
}
// Get containers from the specific test run
currentTestContainers := getCurrentTestContainers(containers, testContainerID, verbose)
extractedCount := 0
for _, cont := range currentTestContainers {
// Extract container logs and tar files
if err := extractContainerArtifacts(ctx, cli, cont.ID, cont.name, logsDir, verbose); err != nil {
if verbose {
log.Printf("Warning: failed to extract artifacts from container %s (%s): %v", cont.name, cont.ID[:12], err)
}
} else {
if verbose {
log.Printf("Extracted artifacts from container %s (%s)", cont.name, cont.ID[:12])
}
extractedCount++
}
}
if verbose && extractedCount > 0 {
log.Printf("Extracted artifacts from %d containers", extractedCount)
}
return nil
}
// testContainer represents a container from the current test run.
type testContainer struct {
ID string
name string
}
// getCurrentTestContainers filters containers to only include those from the current test run.
func getCurrentTestContainers(containers []container.Summary, testContainerID string, verbose bool) []testContainer {
var testRunContainers []testContainer
// Find the test container to get its run ID label
var runID string
for _, cont := range containers {
if cont.ID == testContainerID {
if cont.Labels != nil {
runID = cont.Labels["hi.run-id"]
}
break
}
}
if runID == "" {
log.Printf("Error: test container %s missing required hi.run-id label", testContainerID[:12])
return testRunContainers
}
if verbose {
log.Printf("Looking for containers with run ID: %s", runID)
}
// Find all containers with the same run ID
for _, cont := range containers {
for _, name := range cont.Names {
containerName := strings.TrimPrefix(name, "/")
if strings.HasPrefix(containerName, "hs-") || strings.HasPrefix(containerName, "ts-") {
// Check if container has matching run ID label
if cont.Labels != nil && cont.Labels["hi.run-id"] == runID {
testRunContainers = append(testRunContainers, testContainer{
ID: cont.ID,
name: containerName,
})
if verbose {
log.Printf("Including container %s (run ID: %s)", containerName, runID)
}
}
break
}
}
}
return testRunContainers
}
// extractContainerArtifacts saves logs and tar files from a container.
func extractContainerArtifacts(ctx context.Context, cli *client.Client, containerID, containerName, logsDir string, verbose bool) error {
// Ensure the logs directory exists
if err := os.MkdirAll(logsDir, 0o755); err != nil {
return fmt.Errorf("failed to create logs directory: %w", err)
}
// Extract container logs
if err := extractContainerLogs(ctx, cli, containerID, containerName, logsDir, verbose); err != nil {
return fmt.Errorf("failed to extract logs: %w", err)
}
// Extract tar files for headscale containers only
if strings.HasPrefix(containerName, "hs-") {
if err := extractContainerFiles(ctx, cli, containerID, containerName, logsDir, verbose); err != nil {
if verbose {
log.Printf("Warning: failed to extract files from %s: %v", containerName, err)
}
// Don't fail the whole extraction if files are missing
}
}
return nil
}
// extractContainerLogs saves the stdout and stderr logs from a container to files.
func extractContainerLogs(ctx context.Context, cli *client.Client, containerID, containerName, logsDir string, verbose bool) error {
// Get container logs
logReader, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Timestamps: false,
Follow: false,
Tail: "all",
})
if err != nil {
return fmt.Errorf("failed to get container logs: %w", err)
}
defer logReader.Close()
// Create log files following the headscale naming convention
stdoutPath := filepath.Join(logsDir, containerName+".stdout.log")
stderrPath := filepath.Join(logsDir, containerName+".stderr.log")
// Create buffers to capture stdout and stderr separately
var stdoutBuf, stderrBuf bytes.Buffer
// Demultiplex the Docker logs stream to separate stdout and stderr
_, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, logReader)
if err != nil {
return fmt.Errorf("failed to demultiplex container logs: %w", err)
}
// Write stdout logs
if err := os.WriteFile(stdoutPath, stdoutBuf.Bytes(), 0o644); err != nil {
return fmt.Errorf("failed to write stdout log: %w", err)
}
// Write stderr logs
if err := os.WriteFile(stderrPath, stderrBuf.Bytes(), 0o644); err != nil {
return fmt.Errorf("failed to write stderr log: %w", err)
}
if verbose {
log.Printf("Saved logs for %s: %s, %s", containerName, stdoutPath, stderrPath)
}
return nil
}
// extractContainerFiles extracts database file and directories from headscale containers.
// Note: The actual file extraction is now handled by the integration tests themselves
// via SaveProfile, SaveMapResponses, and SaveDatabase functions in hsic.go.
func extractContainerFiles(ctx context.Context, cli *client.Client, containerID, containerName, logsDir string, verbose bool) error {
// Files are now extracted directly by the integration tests
// This function is kept for potential future use or other file types
return nil
}

374
cmd/hi/doctor.go Normal file
View File

@@ -0,0 +1,374 @@
package main
import (
"context"
"errors"
"fmt"
"log"
"os/exec"
"strings"
)
var ErrSystemChecksFailed = errors.New("system checks failed")
// DoctorResult represents the result of a single health check.
type DoctorResult struct {
Name string
Status string // "PASS", "FAIL", "WARN"
Message string
Suggestions []string
}
// runDoctorCheck performs comprehensive pre-flight checks for integration testing.
func runDoctorCheck(ctx context.Context) error {
results := []DoctorResult{}
// Check 1: Docker binary availability
results = append(results, checkDockerBinary())
// Check 2: Docker daemon connectivity
dockerResult := checkDockerDaemon(ctx)
results = append(results, dockerResult)
// If Docker is available, run additional checks
if dockerResult.Status == "PASS" {
results = append(results, checkDockerContext(ctx))
results = append(results, checkDockerSocket(ctx))
results = append(results, checkGolangImage(ctx))
}
// Check 3: Go installation
results = append(results, checkGoInstallation())
// Check 4: Git repository
results = append(results, checkGitRepository())
// Check 5: Required files
results = append(results, checkRequiredFiles())
// Display results
displayDoctorResults(results)
// Return error if any critical checks failed
for _, result := range results {
if result.Status == "FAIL" {
return fmt.Errorf("%w - see details above", ErrSystemChecksFailed)
}
}
log.Printf("✅ All system checks passed - ready to run integration tests!")
return nil
}
// checkDockerBinary verifies Docker binary is available.
func checkDockerBinary() DoctorResult {
_, err := exec.LookPath("docker")
if err != nil {
return DoctorResult{
Name: "Docker Binary",
Status: "FAIL",
Message: "Docker binary not found in PATH",
Suggestions: []string{
"Install Docker: https://docs.docker.com/get-docker/",
"For macOS: consider using colima or Docker Desktop",
"Ensure docker is in your PATH",
},
}
}
return DoctorResult{
Name: "Docker Binary",
Status: "PASS",
Message: "Docker binary found",
}
}
// checkDockerDaemon verifies Docker daemon is running and accessible.
func checkDockerDaemon(ctx context.Context) DoctorResult {
cli, err := createDockerClient()
if err != nil {
return DoctorResult{
Name: "Docker Daemon",
Status: "FAIL",
Message: fmt.Sprintf("Cannot create Docker client: %v", err),
Suggestions: []string{
"Start Docker daemon/service",
"Check Docker Desktop is running (if using Docker Desktop)",
"For colima: run 'colima start'",
"Verify DOCKER_HOST environment variable if set",
},
}
}
defer cli.Close()
_, err = cli.Ping(ctx)
if err != nil {
return DoctorResult{
Name: "Docker Daemon",
Status: "FAIL",
Message: fmt.Sprintf("Cannot ping Docker daemon: %v", err),
Suggestions: []string{
"Ensure Docker daemon is running",
"Check Docker socket permissions",
"Try: docker info",
},
}
}
return DoctorResult{
Name: "Docker Daemon",
Status: "PASS",
Message: "Docker daemon is running and accessible",
}
}
// checkDockerContext verifies Docker context configuration.
func checkDockerContext(_ context.Context) DoctorResult {
contextInfo, err := getCurrentDockerContext()
if err != nil {
return DoctorResult{
Name: "Docker Context",
Status: "WARN",
Message: "Could not detect Docker context, using default settings",
Suggestions: []string{
"Check: docker context ls",
"Consider setting up a specific context if needed",
},
}
}
if contextInfo == nil {
return DoctorResult{
Name: "Docker Context",
Status: "PASS",
Message: "Using default Docker context",
}
}
return DoctorResult{
Name: "Docker Context",
Status: "PASS",
Message: "Using Docker context: " + contextInfo.Name,
}
}
// checkDockerSocket verifies Docker socket accessibility.
func checkDockerSocket(ctx context.Context) DoctorResult {
cli, err := createDockerClient()
if err != nil {
return DoctorResult{
Name: "Docker Socket",
Status: "FAIL",
Message: fmt.Sprintf("Cannot access Docker socket: %v", err),
Suggestions: []string{
"Check Docker socket permissions",
"Add user to docker group: sudo usermod -aG docker $USER",
"For colima: ensure socket is accessible",
},
}
}
defer cli.Close()
info, err := cli.Info(ctx)
if err != nil {
return DoctorResult{
Name: "Docker Socket",
Status: "FAIL",
Message: fmt.Sprintf("Cannot get Docker info: %v", err),
Suggestions: []string{
"Check Docker daemon status",
"Verify socket permissions",
},
}
}
return DoctorResult{
Name: "Docker Socket",
Status: "PASS",
Message: fmt.Sprintf("Docker socket accessible (Server: %s)", info.ServerVersion),
}
}
// checkGolangImage verifies the golang Docker image is available locally or can be pulled.
func checkGolangImage(ctx context.Context) DoctorResult {
cli, err := createDockerClient()
if err != nil {
return DoctorResult{
Name: "Golang Image",
Status: "FAIL",
Message: "Cannot create Docker client for image check",
}
}
defer cli.Close()
goVersion := detectGoVersion()
imageName := "golang:" + goVersion
// First check if image is available locally
available, err := checkImageAvailableLocally(ctx, cli, imageName)
if err != nil {
return DoctorResult{
Name: "Golang Image",
Status: "FAIL",
Message: fmt.Sprintf("Cannot check golang image %s: %v", imageName, err),
Suggestions: []string{
"Check Docker daemon status",
"Try: docker images | grep golang",
},
}
}
if available {
return DoctorResult{
Name: "Golang Image",
Status: "PASS",
Message: fmt.Sprintf("Golang image %s is available locally", imageName),
}
}
// Image not available locally, try to pull it
err = ensureImageAvailable(ctx, cli, imageName, false)
if err != nil {
return DoctorResult{
Name: "Golang Image",
Status: "FAIL",
Message: fmt.Sprintf("Golang image %s not available locally and cannot pull: %v", imageName, err),
Suggestions: []string{
"Check internet connectivity",
"Verify Docker Hub access",
"Try: docker pull " + imageName,
"Or run tests offline if image was pulled previously",
},
}
}
return DoctorResult{
Name: "Golang Image",
Status: "PASS",
Message: fmt.Sprintf("Golang image %s is now available", imageName),
}
}
// checkGoInstallation verifies Go is installed and working.
func checkGoInstallation() DoctorResult {
_, err := exec.LookPath("go")
if err != nil {
return DoctorResult{
Name: "Go Installation",
Status: "FAIL",
Message: "Go binary not found in PATH",
Suggestions: []string{
"Install Go: https://golang.org/dl/",
"Ensure go is in your PATH",
},
}
}
cmd := exec.Command("go", "version")
output, err := cmd.Output()
if err != nil {
return DoctorResult{
Name: "Go Installation",
Status: "FAIL",
Message: fmt.Sprintf("Cannot get Go version: %v", err),
}
}
version := strings.TrimSpace(string(output))
return DoctorResult{
Name: "Go Installation",
Status: "PASS",
Message: version,
}
}
// checkGitRepository verifies we're in a git repository.
func checkGitRepository() DoctorResult {
cmd := exec.Command("git", "rev-parse", "--git-dir")
err := cmd.Run()
if err != nil {
return DoctorResult{
Name: "Git Repository",
Status: "FAIL",
Message: "Not in a Git repository",
Suggestions: []string{
"Run from within the headscale git repository",
"Clone the repository: git clone https://github.com/juanfont/headscale.git",
},
}
}
return DoctorResult{
Name: "Git Repository",
Status: "PASS",
Message: "Running in Git repository",
}
}
// checkRequiredFiles verifies required files exist.
func checkRequiredFiles() DoctorResult {
requiredFiles := []string{
"go.mod",
"integration/",
"cmd/hi/",
}
var missingFiles []string
for _, file := range requiredFiles {
cmd := exec.Command("test", "-e", file)
if err := cmd.Run(); err != nil {
missingFiles = append(missingFiles, file)
}
}
if len(missingFiles) > 0 {
return DoctorResult{
Name: "Required Files",
Status: "FAIL",
Message: "Missing required files: " + strings.Join(missingFiles, ", "),
Suggestions: []string{
"Ensure you're in the headscale project root directory",
"Check that integration/ directory exists",
"Verify this is a complete headscale repository",
},
}
}
return DoctorResult{
Name: "Required Files",
Status: "PASS",
Message: "All required files found",
}
}
// displayDoctorResults shows the results in a formatted way.
func displayDoctorResults(results []DoctorResult) {
log.Printf("🔍 System Health Check Results")
log.Printf("================================")
for _, result := range results {
var icon string
switch result.Status {
case "PASS":
icon = "✅"
case "WARN":
icon = "⚠️"
case "FAIL":
icon = "❌"
default:
icon = "❓"
}
log.Printf("%s %s: %s", icon, result.Name, result.Message)
if len(result.Suggestions) > 0 {
for _, suggestion := range result.Suggestions {
log.Printf(" 💡 %s", suggestion)
}
}
}
log.Printf("================================")
}

93
cmd/hi/main.go Normal file
View File

@@ -0,0 +1,93 @@
package main
import (
"context"
"os"
"github.com/creachadair/command"
"github.com/creachadair/flax"
)
var runConfig RunConfig
func main() {
root := command.C{
Name: "hi",
Help: "Headscale Integration test runner",
Commands: []*command.C{
{
Name: "run",
Help: "Run integration tests",
Usage: "run [test-pattern] [flags]",
SetFlags: command.Flags(flax.MustBind, &runConfig),
Run: runIntegrationTest,
},
{
Name: "doctor",
Help: "Check system requirements for running integration tests",
Run: func(env *command.Env) error {
return runDoctorCheck(env.Context())
},
},
{
Name: "clean",
Help: "Clean Docker resources",
Commands: []*command.C{
{
Name: "networks",
Help: "Prune unused Docker networks",
Run: func(env *command.Env) error {
return pruneDockerNetworks(env.Context())
},
},
{
Name: "images",
Help: "Clean old test images",
Run: func(env *command.Env) error {
return cleanOldImages(env.Context())
},
},
{
Name: "containers",
Help: "Kill all test containers",
Run: func(env *command.Env) error {
return killTestContainers(env.Context())
},
},
{
Name: "cache",
Help: "Clean Go module cache volume",
Run: func(env *command.Env) error {
return cleanCacheVolume(env.Context())
},
},
{
Name: "all",
Help: "Run all cleanup operations",
Run: func(env *command.Env) error {
return cleanAll(env.Context())
},
},
},
},
command.HelpCommand(nil),
},
}
env := root.NewEnv(nil).MergeFlags(true)
command.RunOrFail(env, os.Args[1:])
}
func cleanAll(ctx context.Context) error {
if err := killTestContainers(ctx); err != nil {
return err
}
if err := pruneDockerNetworks(ctx); err != nil {
return err
}
if err := cleanOldImages(ctx); err != nil {
return err
}
return cleanCacheVolume(ctx)
}

125
cmd/hi/run.go Normal file
View File

@@ -0,0 +1,125 @@
package main
import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"time"
"github.com/creachadair/command"
)
var ErrTestPatternRequired = errors.New("test pattern is required as first argument or use --test flag")
type RunConfig struct {
TestPattern string `flag:"test,Test pattern to run"`
Timeout time.Duration `flag:"timeout,default=120m,Test timeout"`
FailFast bool `flag:"failfast,default=true,Stop on first test failure"`
UsePostgres bool `flag:"postgres,default=false,Use PostgreSQL instead of SQLite"`
GoVersion string `flag:"go-version,Go version to use (auto-detected from go.mod)"`
CleanBefore bool `flag:"clean-before,default=true,Clean stale resources before test"`
CleanAfter bool `flag:"clean-after,default=true,Clean resources after test"`
KeepOnFailure bool `flag:"keep-on-failure,default=false,Keep containers on test failure"`
LogsDir string `flag:"logs-dir,default=control_logs,Control logs directory"`
Verbose bool `flag:"verbose,default=false,Verbose output"`
Stats bool `flag:"stats,default=false,Collect and display container resource usage statistics"`
HSMemoryLimit float64 `flag:"hs-memory-limit,default=0,Fail test if any Headscale container exceeds this memory limit in MB (0 = disabled)"`
TSMemoryLimit float64 `flag:"ts-memory-limit,default=0,Fail test if any Tailscale container exceeds this memory limit in MB (0 = disabled)"`
}
// runIntegrationTest executes the integration test workflow.
func runIntegrationTest(env *command.Env) error {
args := env.Args
if len(args) > 0 && runConfig.TestPattern == "" {
runConfig.TestPattern = args[0]
}
if runConfig.TestPattern == "" {
return ErrTestPatternRequired
}
if runConfig.GoVersion == "" {
runConfig.GoVersion = detectGoVersion()
}
// Run pre-flight checks
if runConfig.Verbose {
log.Printf("Running pre-flight system checks...")
}
if err := runDoctorCheck(env.Context()); err != nil {
return fmt.Errorf("pre-flight checks failed: %w", err)
}
if runConfig.Verbose {
log.Printf("Running test: %s", runConfig.TestPattern)
log.Printf("Go version: %s", runConfig.GoVersion)
log.Printf("Timeout: %s", runConfig.Timeout)
log.Printf("Use PostgreSQL: %t", runConfig.UsePostgres)
}
return runTestContainer(env.Context(), &runConfig)
}
// detectGoVersion reads the Go version from go.mod file.
func detectGoVersion() string {
goModPath := filepath.Join("..", "..", "go.mod")
if _, err := os.Stat("go.mod"); err == nil {
goModPath = "go.mod"
} else if _, err := os.Stat("../../go.mod"); err == nil {
goModPath = "../../go.mod"
}
content, err := os.ReadFile(goModPath)
if err != nil {
return "1.25"
}
lines := splitLines(string(content))
for _, line := range lines {
if len(line) > 3 && line[:3] == "go " {
version := line[3:]
if idx := indexOf(version, " "); idx != -1 {
version = version[:idx]
}
return version
}
}
return "1.25"
}
// splitLines splits a string into lines without using strings.Split.
func splitLines(s string) []string {
var lines []string
var current string
for _, char := range s {
if char == '\n' {
lines = append(lines, current)
current = ""
} else {
current += string(char)
}
}
if current != "" {
lines = append(lines, current)
}
return lines
}
// indexOf finds the first occurrence of substr in s.
func indexOf(s, substr string) int {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return i
}
}
return -1
}

471
cmd/hi/stats.go Normal file
View File

@@ -0,0 +1,471 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log"
"sort"
"strings"
"sync"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
)
// ContainerStats represents statistics for a single container.
type ContainerStats struct {
ContainerID string
ContainerName string
Stats []StatsSample
mutex sync.RWMutex
}
// StatsSample represents a single stats measurement.
type StatsSample struct {
Timestamp time.Time
CPUUsage float64 // CPU usage percentage
MemoryMB float64 // Memory usage in MB
}
// StatsCollector manages collection of container statistics.
type StatsCollector struct {
client *client.Client
containers map[string]*ContainerStats
stopChan chan struct{}
wg sync.WaitGroup
mutex sync.RWMutex
collectionStarted bool
}
// NewStatsCollector creates a new stats collector instance.
func NewStatsCollector() (*StatsCollector, error) {
cli, err := createDockerClient()
if err != nil {
return nil, fmt.Errorf("failed to create Docker client: %w", err)
}
return &StatsCollector{
client: cli,
containers: make(map[string]*ContainerStats),
stopChan: make(chan struct{}),
}, nil
}
// StartCollection begins monitoring all containers and collecting stats for hs- and ts- containers with matching run ID.
func (sc *StatsCollector) StartCollection(ctx context.Context, runID string, verbose bool) error {
sc.mutex.Lock()
defer sc.mutex.Unlock()
if sc.collectionStarted {
return errors.New("stats collection already started")
}
sc.collectionStarted = true
// Start monitoring existing containers
sc.wg.Add(1)
go sc.monitorExistingContainers(ctx, runID, verbose)
// Start Docker events monitoring for new containers
sc.wg.Add(1)
go sc.monitorDockerEvents(ctx, runID, verbose)
if verbose {
log.Printf("Started container monitoring for run ID %s", runID)
}
return nil
}
// StopCollection stops all stats collection.
func (sc *StatsCollector) StopCollection() {
// Check if already stopped without holding lock
sc.mutex.RLock()
if !sc.collectionStarted {
sc.mutex.RUnlock()
return
}
sc.mutex.RUnlock()
// Signal stop to all goroutines
close(sc.stopChan)
// Wait for all goroutines to finish
sc.wg.Wait()
// Mark as stopped
sc.mutex.Lock()
sc.collectionStarted = false
sc.mutex.Unlock()
}
// monitorExistingContainers checks for existing containers that match our criteria.
func (sc *StatsCollector) monitorExistingContainers(ctx context.Context, runID string, verbose bool) {
defer sc.wg.Done()
containers, err := sc.client.ContainerList(ctx, container.ListOptions{})
if err != nil {
if verbose {
log.Printf("Failed to list existing containers: %v", err)
}
return
}
for _, cont := range containers {
if sc.shouldMonitorContainer(cont, runID) {
sc.startStatsForContainer(ctx, cont.ID, cont.Names[0], verbose)
}
}
}
// monitorDockerEvents listens for container start events and begins monitoring relevant containers.
func (sc *StatsCollector) monitorDockerEvents(ctx context.Context, runID string, verbose bool) {
defer sc.wg.Done()
filter := filters.NewArgs()
filter.Add("type", "container")
filter.Add("event", "start")
eventOptions := events.ListOptions{
Filters: filter,
}
events, errs := sc.client.Events(ctx, eventOptions)
for {
select {
case <-sc.stopChan:
return
case <-ctx.Done():
return
case event := <-events:
if event.Type == "container" && event.Action == "start" {
// Get container details
containerInfo, err := sc.client.ContainerInspect(ctx, event.ID)
if err != nil {
continue
}
// Convert to types.Container format for consistency
cont := types.Container{
ID: containerInfo.ID,
Names: []string{containerInfo.Name},
Labels: containerInfo.Config.Labels,
}
if sc.shouldMonitorContainer(cont, runID) {
sc.startStatsForContainer(ctx, cont.ID, cont.Names[0], verbose)
}
}
case err := <-errs:
if verbose {
log.Printf("Error in Docker events stream: %v", err)
}
return
}
}
}
// shouldMonitorContainer determines if a container should be monitored.
func (sc *StatsCollector) shouldMonitorContainer(cont types.Container, runID string) bool {
// Check if it has the correct run ID label
if cont.Labels == nil || cont.Labels["hi.run-id"] != runID {
return false
}
// Check if it's an hs- or ts- container
for _, name := range cont.Names {
containerName := strings.TrimPrefix(name, "/")
if strings.HasPrefix(containerName, "hs-") || strings.HasPrefix(containerName, "ts-") {
return true
}
}
return false
}
// startStatsForContainer begins stats collection for a specific container.
func (sc *StatsCollector) startStatsForContainer(ctx context.Context, containerID, containerName string, verbose bool) {
containerName = strings.TrimPrefix(containerName, "/")
sc.mutex.Lock()
// Check if we're already monitoring this container
if _, exists := sc.containers[containerID]; exists {
sc.mutex.Unlock()
return
}
sc.containers[containerID] = &ContainerStats{
ContainerID: containerID,
ContainerName: containerName,
Stats: make([]StatsSample, 0),
}
sc.mutex.Unlock()
if verbose {
log.Printf("Starting stats collection for container %s (%s)", containerName, containerID[:12])
}
sc.wg.Add(1)
go sc.collectStatsForContainer(ctx, containerID, verbose)
}
// collectStatsForContainer collects stats for a specific container using Docker API streaming.
func (sc *StatsCollector) collectStatsForContainer(ctx context.Context, containerID string, verbose bool) {
defer sc.wg.Done()
// Use Docker API streaming stats - much more efficient than CLI
statsResponse, err := sc.client.ContainerStats(ctx, containerID, true)
if err != nil {
if verbose {
log.Printf("Failed to get stats stream for container %s: %v", containerID[:12], err)
}
return
}
defer statsResponse.Body.Close()
decoder := json.NewDecoder(statsResponse.Body)
var prevStats *container.Stats
for {
select {
case <-sc.stopChan:
return
case <-ctx.Done():
return
default:
var stats container.Stats
if err := decoder.Decode(&stats); err != nil {
// EOF is expected when container stops or stream ends
if err.Error() != "EOF" && verbose {
log.Printf("Failed to decode stats for container %s: %v", containerID[:12], err)
}
return
}
// Calculate CPU percentage (only if we have previous stats)
var cpuPercent float64
if prevStats != nil {
cpuPercent = calculateCPUPercent(prevStats, &stats)
}
// Calculate memory usage in MB
memoryMB := float64(stats.MemoryStats.Usage) / (1024 * 1024)
// Store the sample (skip first sample since CPU calculation needs previous stats)
if prevStats != nil {
// Get container stats reference without holding the main mutex
var containerStats *ContainerStats
var exists bool
sc.mutex.RLock()
containerStats, exists = sc.containers[containerID]
sc.mutex.RUnlock()
if exists && containerStats != nil {
containerStats.mutex.Lock()
containerStats.Stats = append(containerStats.Stats, StatsSample{
Timestamp: time.Now(),
CPUUsage: cpuPercent,
MemoryMB: memoryMB,
})
containerStats.mutex.Unlock()
}
}
// Save current stats for next iteration
prevStats = &stats
}
}
}
// calculateCPUPercent calculates CPU usage percentage from Docker stats.
func calculateCPUPercent(prevStats, stats *container.Stats) float64 {
// CPU calculation based on Docker's implementation
cpuDelta := float64(stats.CPUStats.CPUUsage.TotalUsage) - float64(prevStats.CPUStats.CPUUsage.TotalUsage)
systemDelta := float64(stats.CPUStats.SystemUsage) - float64(prevStats.CPUStats.SystemUsage)
if systemDelta > 0 && cpuDelta >= 0 {
// Calculate CPU percentage: (container CPU delta / system CPU delta) * number of CPUs * 100
numCPUs := float64(len(stats.CPUStats.CPUUsage.PercpuUsage))
if numCPUs == 0 {
// Fallback: if PercpuUsage is not available, assume 1 CPU
numCPUs = 1.0
}
return (cpuDelta / systemDelta) * numCPUs * 100.0
}
return 0.0
}
// ContainerStatsSummary represents summary statistics for a container.
type ContainerStatsSummary struct {
ContainerName string
SampleCount int
CPU StatsSummary
Memory StatsSummary
}
// MemoryViolation represents a container that exceeded the memory limit.
type MemoryViolation struct {
ContainerName string
MaxMemoryMB float64
LimitMB float64
}
// StatsSummary represents min, max, and average for a metric.
type StatsSummary struct {
Min float64
Max float64
Average float64
}
// GetSummary returns a summary of collected statistics.
func (sc *StatsCollector) GetSummary() []ContainerStatsSummary {
// Take snapshot of container references without holding main lock long
sc.mutex.RLock()
containerRefs := make([]*ContainerStats, 0, len(sc.containers))
for _, containerStats := range sc.containers {
containerRefs = append(containerRefs, containerStats)
}
sc.mutex.RUnlock()
summaries := make([]ContainerStatsSummary, 0, len(containerRefs))
for _, containerStats := range containerRefs {
containerStats.mutex.RLock()
stats := make([]StatsSample, len(containerStats.Stats))
copy(stats, containerStats.Stats)
containerName := containerStats.ContainerName
containerStats.mutex.RUnlock()
if len(stats) == 0 {
continue
}
summary := ContainerStatsSummary{
ContainerName: containerName,
SampleCount: len(stats),
}
// Calculate CPU stats
cpuValues := make([]float64, len(stats))
memoryValues := make([]float64, len(stats))
for i, sample := range stats {
cpuValues[i] = sample.CPUUsage
memoryValues[i] = sample.MemoryMB
}
summary.CPU = calculateStatsSummary(cpuValues)
summary.Memory = calculateStatsSummary(memoryValues)
summaries = append(summaries, summary)
}
// Sort by container name for consistent output
sort.Slice(summaries, func(i, j int) bool {
return summaries[i].ContainerName < summaries[j].ContainerName
})
return summaries
}
// calculateStatsSummary calculates min, max, and average for a slice of values.
func calculateStatsSummary(values []float64) StatsSummary {
if len(values) == 0 {
return StatsSummary{}
}
min := values[0]
max := values[0]
sum := 0.0
for _, value := range values {
if value < min {
min = value
}
if value > max {
max = value
}
sum += value
}
return StatsSummary{
Min: min,
Max: max,
Average: sum / float64(len(values)),
}
}
// PrintSummary prints the statistics summary to the console.
func (sc *StatsCollector) PrintSummary() {
summaries := sc.GetSummary()
if len(summaries) == 0 {
log.Printf("No container statistics collected")
return
}
log.Printf("Container Resource Usage Summary:")
log.Printf("================================")
for _, summary := range summaries {
log.Printf("Container: %s (%d samples)", summary.ContainerName, summary.SampleCount)
log.Printf(" CPU Usage: Min: %6.2f%% Max: %6.2f%% Avg: %6.2f%%",
summary.CPU.Min, summary.CPU.Max, summary.CPU.Average)
log.Printf(" Memory Usage: Min: %6.1f MB Max: %6.1f MB Avg: %6.1f MB",
summary.Memory.Min, summary.Memory.Max, summary.Memory.Average)
log.Printf("")
}
}
// CheckMemoryLimits checks if any containers exceeded their memory limits.
func (sc *StatsCollector) CheckMemoryLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation {
if hsLimitMB <= 0 && tsLimitMB <= 0 {
return nil
}
summaries := sc.GetSummary()
var violations []MemoryViolation
for _, summary := range summaries {
var limitMB float64
if strings.HasPrefix(summary.ContainerName, "hs-") {
limitMB = hsLimitMB
} else if strings.HasPrefix(summary.ContainerName, "ts-") {
limitMB = tsLimitMB
} else {
continue // Skip containers that don't match our patterns
}
if limitMB > 0 && summary.Memory.Max > limitMB {
violations = append(violations, MemoryViolation{
ContainerName: summary.ContainerName,
MaxMemoryMB: summary.Memory.Max,
LimitMB: limitMB,
})
}
}
return violations
}
// PrintSummaryAndCheckLimits prints the statistics summary and returns memory violations if any.
func (sc *StatsCollector) PrintSummaryAndCheckLimits(hsLimitMB, tsLimitMB float64) []MemoryViolation {
sc.PrintSummary()
return sc.CheckMemoryLimits(hsLimitMB, tsLimitMB)
}
// Close closes the stats collector and cleans up resources.
func (sc *StatsCollector) Close() error {
sc.StopCollection()
return sc.client.Close()
}

61
cmd/mapresponses/main.go Normal file
View File

@@ -0,0 +1,61 @@
package main
import (
"encoding/json"
"fmt"
"os"
"github.com/creachadair/command"
"github.com/creachadair/flax"
"github.com/juanfont/headscale/hscontrol/mapper"
"github.com/juanfont/headscale/integration/integrationutil"
)
type MapConfig struct {
Directory string `flag:"directory,Directory to read map responses from"`
}
var mapConfig MapConfig
func main() {
root := command.C{
Name: "mapresponses",
Help: "MapResponses is a tool to map and compare map responses from a directory",
Commands: []*command.C{
{
Name: "online",
Help: "",
Usage: "run [test-pattern] [flags]",
SetFlags: command.Flags(flax.MustBind, &mapConfig),
Run: runOnline,
},
command.HelpCommand(nil),
},
}
env := root.NewEnv(nil).MergeFlags(true)
command.RunOrFail(env, os.Args[1:])
}
// runIntegrationTest executes the integration test workflow.
func runOnline(env *command.Env) error {
if mapConfig.Directory == "" {
return fmt.Errorf("directory is required")
}
resps, err := mapper.ReadMapResponsesFromDirectory(mapConfig.Directory)
if err != nil {
return fmt.Errorf("reading map responses from directory: %w", err)
}
expected := integrationutil.BuildExpectedOnlineMap(resps)
out, err := json.MarshalIndent(expected, "", " ")
if err != nil {
return fmt.Errorf("marshaling expected online map: %w", err)
}
os.Stderr.Write(out)
os.Stderr.Write([]byte("\n"))
return nil
}

429
config-example.yaml Normal file
View File

@@ -0,0 +1,429 @@
---
# headscale will look for a configuration file named `config.yaml` (or `config.json`) in the following order:
#
# - `/etc/headscale`
# - `~/.headscale`
# - current working directory
# The url clients will connect to.
# Typically this will be a domain like:
#
# https://myheadscale.example.com:443
#
server_url: http://127.0.0.1:8080
# Address to listen to / bind to on the server
#
# For production:
# listen_addr: 0.0.0.0:8080
listen_addr: 127.0.0.1:8080
# Address to listen to /metrics and /debug, you may want
# to keep this endpoint private to your internal network
metrics_listen_addr: 127.0.0.1:9090
# Address to listen for gRPC.
# gRPC is used for controlling a headscale server
# remotely with the CLI
# Note: Remote access _only_ works if you have
# valid certificates.
#
# For production:
# grpc_listen_addr: 0.0.0.0:50443
grpc_listen_addr: 127.0.0.1:50443
# Allow the gRPC admin interface to run in INSECURE
# mode. This is not recommended as the traffic will
# be unencrypted. Only enable if you know what you
# are doing.
grpc_allow_insecure: false
# The Noise section includes specific configuration for the
# TS2021 Noise protocol
noise:
# The Noise private key is used to encrypt the traffic between headscale and
# Tailscale clients when using the new Noise-based protocol. A missing key
# will be automatically generated.
private_key_path: /var/lib/headscale/noise_private.key
# List of IP prefixes to allocate tailaddresses from.
# Each prefix consists of either an IPv4 or IPv6 address,
# and the associated prefix length, delimited by a slash.
# It must be within IP ranges supported by the Tailscale
# client - i.e., subnets of 100.64.0.0/10 and fd7a:115c:a1e0::/48.
# See below:
# 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
# Any other range is NOT supported, and it will cause unexpected issues.
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
# Strategy used for allocation of IPs to nodes, available options:
# - sequential (default): assigns the next free IP from the previous given
# IP. A best-effort approach is used and Headscale might leave holes in the
# IP range or fill up existing holes in the IP range.
# - random: assigns the next free IP from a pseudo-random IP generator (crypto/rand).
allocation: sequential
# DERP is a relay system that Tailscale uses when a direct
# connection cannot be established.
# https://tailscale.com/blog/how-tailscale-works/#encrypted-tcp-relays-derp
#
# headscale needs a list of DERP servers that can be presented
# to the clients.
derp:
server:
# If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
# The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
enabled: false
# Region ID to use for the embedded DERP server.
# The local DERP prevails if the region ID collides with other region ID coming from
# the regular DERP config.
region_id: 999
# Region code and name are displayed in the Tailscale UI to identify a DERP region
region_code: "headscale"
region_name: "Headscale Embedded DERP"
# Only allow clients associated with this server access
verify_clients: true
# Listens over UDP at the configured address for STUN connections - to help with NAT traversal.
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
#
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
stun_listen_addr: "0.0.0.0:3478"
# Private key used to encrypt the traffic between headscale DERP and
# Tailscale clients. A missing key will be automatically generated.
private_key_path: /var/lib/headscale/derp_server_private.key
# This flag can be used, so the DERP map entry for the embedded DERP server is not written automatically,
# it enables the creation of your very own DERP map entry using a locally available file with the parameter DERP.paths
# If you enable the DERP server and set this to false, it is required to add the DERP server to the DERP map using DERP.paths
automatically_add_embedded_derp_region: true
# For better connection stability (especially when using an Exit-Node and DNS is not working),
# it is possible to optionally add the public IPv4 and IPv6 address to the Derp-Map using:
ipv4: 198.51.100.1
ipv6: 2001:db8::1
# List of externally available DERP maps encoded in JSON
urls:
- https://controlplane.tailscale.com/derpmap/default
# Locally available DERP map files encoded in YAML
#
# This option is mostly interesting for people hosting
# their own DERP servers:
# https://tailscale.com/kb/1118/custom-derp-servers/
#
# paths:
# - /etc/headscale/derp-example.yaml
paths: []
# If enabled, a worker will be set up to periodically
# refresh the given sources and update the derpmap
# will be set up.
auto_update_enabled: true
# How often should we check for DERP updates?
update_frequency: 3h
# Disables the automatic check for headscale updates on startup
disable_check_updates: false
# Time before an inactive ephemeral node is deleted?
ephemeral_node_inactivity_timeout: 30m
database:
# Database type. Available options: sqlite, postgres
# Please note that using Postgres is highly discouraged as it is only supported for legacy reasons.
# All new development, testing and optimisations are done with SQLite in mind.
type: sqlite
# Enable debug mode. This setting requires the log.level to be set to "debug" or "trace".
debug: false
# GORM configuration settings.
gorm:
# Enable prepared statements.
prepare_stmt: true
# Enable parameterized queries.
parameterized_queries: true
# Skip logging "record not found" errors.
skip_err_record_not_found: true
# Threshold for slow queries in milliseconds.
slow_threshold: 1000
# SQLite config
sqlite:
path: /var/lib/headscale/db.sqlite
# Enable WAL mode for SQLite. This is recommended for production environments.
# https://www.sqlite.org/wal.html
write_ahead_log: true
# Maximum number of WAL file frames before the WAL file is automatically checkpointed.
# https://www.sqlite.org/c3ref/wal_autocheckpoint.html
# Set to 0 to disable automatic checkpointing.
wal_autocheckpoint: 1000
# # Postgres config
# Please note that using Postgres is highly discouraged as it is only supported for legacy reasons.
# See database.type for more information.
# postgres:
# # If using a Unix socket to connect to Postgres, set the socket path in the 'host' field and leave 'port' blank.
# host: localhost
# port: 5432
# name: headscale
# user: foo
# pass: bar
# max_open_conns: 10
# max_idle_conns: 10
# conn_max_idle_time_secs: 3600
# # If other 'sslmode' is required instead of 'require(true)' and 'disabled(false)', set the 'sslmode' you need
# # in the 'ssl' field. Refers to https://www.postgresql.org/docs/current/libpq-ssl.html Table 34.1.
# ssl: false
### TLS configuration
#
## Let's encrypt / ACME
#
# headscale supports automatically requesting and setting up
# TLS for a domain with Let's Encrypt.
#
# URL to ACME directory
acme_url: https://acme-v02.api.letsencrypt.org/directory
# Email to register with ACME provider
acme_email: ""
# Domain name to request a TLS certificate for:
tls_letsencrypt_hostname: ""
# Path to store certificates and metadata needed by
# letsencrypt
# For production:
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
# Type of ACME challenge to use, currently supported types:
# HTTP-01 or TLS-ALPN-01
# See: docs/ref/tls.md for more information
tls_letsencrypt_challenge_type: HTTP-01
# When HTTP-01 challenge is chosen, letsencrypt must set up a
# verification endpoint, and it will be listening on:
# :http = port 80
tls_letsencrypt_listen: ":http"
## Use already defined certificates:
tls_cert_path: ""
tls_key_path: ""
log:
# Valid log levels: panic, fatal, error, warn, info, debug, trace
level: info
# Output formatting for logs: text or json
format: text
## Policy
# headscale supports Tailscale's ACL policies.
# Please have a look to their KB to better
# understand the concepts: https://tailscale.com/kb/1018/acls/
policy:
# The mode can be "file" or "database" that defines
# where the ACL policies are stored and read from.
mode: file
# If the mode is set to "file", the path to a
# HuJSON file containing ACL policies.
path: ""
## DNS
#
# headscale supports Tailscale's DNS configuration and MagicDNS.
# Please have a look to their KB to better understand the concepts:
#
# - https://tailscale.com/kb/1054/dns/
# - https://tailscale.com/kb/1081/magicdns/
# - https://tailscale.com/blog/2021-09-private-dns-with-magicdns/
#
# Please note that for the DNS configuration to have any effect,
# clients must have the `--accept-dns=true` option enabled. This is the
# default for the Tailscale client. This option is enabled by default
# in the Tailscale client.
#
# Setting _any_ of the configuration and `--accept-dns=true` on the
# clients will integrate with the DNS manager on the client or
# overwrite /etc/resolv.conf.
# https://tailscale.com/kb/1235/resolv-conf
#
# If you want stop Headscale from managing the DNS configuration
# all the fields under `dns` should be set to empty values.
dns:
# Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/).
magic_dns: true
# Defines the base domain to create the hostnames for MagicDNS.
# This domain _must_ be different from the server_url domain.
# `base_domain` must be a FQDN, without the trailing dot.
# The FQDN of the hosts will be
# `hostname.base_domain` (e.g., _myhost.example.com_).
base_domain: example.com
# Whether to use the local DNS settings of a node or override the local DNS
# settings (default) and force the use of Headscale's DNS configuration.
override_local_dns: true
# List of DNS servers to expose to clients.
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
# NextDNS (see https://tailscale.com/kb/1218/nextdns/).
# "abc123" is example NextDNS ID, replace with yours.
# - https://dns.nextdns.io/abc123
# Split DNS (see https://tailscale.com/kb/1054/dns/),
# a map of domains and which DNS server to use for each.
split: {}
# foo.bar.com:
# - 1.1.1.1
# darp.headscale.net:
# - 1.1.1.1
# - 8.8.8.8
# Set custom DNS search domains. With MagicDNS enabled,
# your tailnet base_domain is always the first search domain.
search_domains: []
# Extra DNS records
# so far only A and AAAA records are supported (on the tailscale side)
# See: docs/ref/dns.md
extra_records: []
# - name: "grafana.myvpn.example.com"
# type: "A"
# value: "100.64.0.3"
#
# # you can also put it in one line
# - { name: "prometheus.myvpn.example.com", type: "A", value: "100.64.0.3" }
#
# Alternatively, extra DNS records can be loaded from a JSON file.
# Headscale processes this file on each change.
# extra_records_path: /var/lib/headscale/extra-records.json
# Unix socket used for the CLI to connect without authentication
# Note: for production you will want to set this to something like:
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
# OpenID Connect
# oidc:
# # Block startup until the identity provider is available and healthy.
# only_start_if_oidc_is_available: true
#
# # OpenID Connect Issuer URL from the identity provider
# issuer: "https://your-oidc.issuer.com/path"
#
# # Client ID from the identity provider
# client_id: "your-oidc-client-id"
#
# # Client secret generated by the identity provider
# # Note: client_secret and client_secret_path are mutually exclusive.
# client_secret: "your-oidc-client-secret"
# # Alternatively, set `client_secret_path` to read the secret from the file.
# # It resolves environment variables, making integration to systemd's
# # `LoadCredential` straightforward:
# client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"
#
# # The amount of time a node is authenticated with OpenID until it expires
# # and needs to reauthenticate.
# # Setting the value to "0" will mean no expiry.
# expiry: 180d
#
# # Use the expiry from the token received from OpenID when the user logged
# # in. This will typically lead to frequent need to reauthenticate and should
# # only be enabled if you know what you are doing.
# # Note: enabling this will cause `oidc.expiry` to be ignored.
# use_expiry_from_token: false
#
# # The OIDC scopes to use, defaults to "openid", "profile" and "email".
# # Custom scopes can be configured as needed, be sure to always include the
# # required "openid" scope.
# scope: ["openid", "profile", "email"]
#
# # Provide custom key/value pairs which get sent to the identity provider's
# # authorization endpoint.
# extra_params:
# domain_hint: example.com
#
# # Only accept users whose email domain is part of the allowed_domains list.
# allowed_domains:
# - example.com
#
# # Only accept users whose email address is part of the allowed_users list.
# allowed_users:
# - alice@example.com
#
# # Only accept users which are members of at least one group in the
# # allowed_groups list.
# allowed_groups:
# - /headscale
#
# # Optional: PKCE (Proof Key for Code Exchange) configuration
# # PKCE adds an additional layer of security to the OAuth 2.0 authorization code flow
# # by preventing authorization code interception attacks
# # See https://datatracker.ietf.org/doc/html/rfc7636
# pkce:
# # Enable or disable PKCE support (default: false)
# enabled: false
#
# # PKCE method to use:
# # - plain: Use plain code verifier
# # - S256: Use SHA256 hashed code verifier (default, recommended)
# method: S256
# Logtail configuration
# Logtail is Tailscales logging and auditing infrastructure, it allows the
# control panel to instruct tailscale nodes to log their activity to a remote
# server. To disable logging on the client side, please refer to:
# https://tailscale.com/kb/1011/log-mesh-traffic#opting-out-of-client-logging
logtail:
# Enable logtail for tailscale nodes of this Headscale instance.
# As there is currently no support for overriding the log server in Headscale, this is
# disabled by default. Enabling this will make your clients send logs to Tailscale Inc.
enabled: false
# Enabling this option makes devices prefer a random port for WireGuard traffic over the
# default static port 41641. This option is intended as a workaround for some buggy
# firewall devices. See https://tailscale.com/kb/1181/firewalls/ for more information.
randomize_client_port: false
# Taildrop configuration
# Taildrop is the file sharing feature of Tailscale, allowing nodes to send files to each other.
# https://tailscale.com/kb/1106/taildrop/
taildrop:
# Enable or disable Taildrop for all nodes.
# When enabled, nodes can send files to other nodes owned by the same user.
# Tagged devices and cross-user transfers are not permitted by Tailscale clients.
enabled: true
# Advanced performance tuning parameters.
# The defaults are carefully chosen and should rarely need adjustment.
# Only modify these if you have identified a specific performance issue.
#
# tuning:
# # NodeStore write batching configuration.
# # The NodeStore batches write operations before rebuilding peer relationships,
# # which is computationally expensive. Batching reduces rebuild frequency.
# #
# # node_store_batch_size: 100
# # node_store_batch_timeout: 500ms

View File

@@ -1,30 +0,0 @@
{
"server_url": "http://127.0.0.1:8080",
"listen_addr": "0.0.0.0:8080",
"private_key_path": "private.key",
"derp_map_path": "derp.yaml",
"ephemeral_node_inactivity_timeout": "30m",
"db_type": "postgres",
"db_host": "localhost",
"db_port": 5432,
"db_name": "headscale",
"db_user": "foo",
"db_pass": "bar",
"acme_url": "https://acme-v02.api.letsencrypt.org/directory",
"acme_email": "",
"tls_letsencrypt_hostname": "",
"tls_letsencrypt_listen": ":http",
"tls_letsencrypt_cache_dir": ".cache",
"tls_letsencrypt_challenge_type": "HTTP-01",
"tls_cert_path": "",
"tls_key_path": "",
"acl_policy_path": "",
"dns_config": {
"nameservers": [
"1.1.1.1"
],
"domains": [],
"magic_dns": true,
"base_domain": "example.com"
}
}

View File

@@ -1,26 +0,0 @@
{
"server_url": "http://127.0.0.1:8080",
"listen_addr": "0.0.0.0:8080",
"private_key_path": "private.key",
"derp_map_path": "derp.yaml",
"ephemeral_node_inactivity_timeout": "30m",
"db_type": "sqlite3",
"db_path": "db.sqlite",
"acme_url": "https://acme-v02.api.letsencrypt.org/directory",
"acme_email": "",
"tls_letsencrypt_hostname": "",
"tls_letsencrypt_listen": ":http",
"tls_letsencrypt_cache_dir": ".cache",
"tls_letsencrypt_challenge_type": "HTTP-01",
"tls_cert_path": "",
"tls_key_path": "",
"acl_policy_path": "",
"dns_config": {
"nameservers": [
"1.1.1.1"
],
"domains": [],
"magic_dns": true,
"base_domain": "example.com"
}
}

111
db.go
View File

@@ -1,111 +0,0 @@
package headscale
import (
"errors"
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
const dbVersion = "1"
// KV is a key-value store in a psql table. For future use...
type KV struct {
Key string
Value string
}
func (h *Headscale) initDB() error {
db, err := h.openDB()
if err != nil {
return err
}
h.db = db
if h.dbType == "postgres" {
db.Exec("create extension if not exists \"uuid-ossp\";")
}
err = db.AutoMigrate(&Machine{})
if err != nil {
return err
}
err = db.AutoMigrate(&KV{})
if err != nil {
return err
}
err = db.AutoMigrate(&Namespace{})
if err != nil {
return err
}
err = db.AutoMigrate(&PreAuthKey{})
if err != nil {
return err
}
err = db.AutoMigrate(&SharedMachine{})
if err != nil {
return err
}
err = h.setValue("db_version", dbVersion)
return err
}
func (h *Headscale) openDB() (*gorm.DB, error) {
var db *gorm.DB
var err error
var log logger.Interface
if h.dbDebug {
log = logger.Default
} else {
log = logger.Default.LogMode(logger.Silent)
}
switch h.dbType {
case "sqlite3":
db, err = gorm.Open(sqlite.Open(h.dbString), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: log,
})
case "postgres":
db, err = gorm.Open(postgres.Open(h.dbString), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
Logger: log,
})
}
if err != nil {
return nil, err
}
return db, nil
}
// getValue returns the value for the given key in KV
func (h *Headscale) getValue(key string) (string, error) {
var row KV
if result := h.db.First(&row, "key = ?", key); errors.Is(result.Error, gorm.ErrRecordNotFound) {
return "", errors.New("not found")
}
return row.Value, nil
}
// setValue sets value for the given key in KV
func (h *Headscale) setValue(key string, value string) error {
kv := KV{
Key: key,
Value: value,
}
_, err := h.getValue(key)
if err == nil {
h.db.Model(&kv).Where("key = ?", key).Update("value", value)
return nil
}
h.db.Create(kv)
return nil
}

16
derp-example.yaml Normal file
View File

@@ -0,0 +1,16 @@
# If you plan to somehow use headscale, please deploy your own DERP infra: https://tailscale.com/kb/1118/custom-derp-servers/
regions:
1: null # Disable DERP region with ID 1
900:
regionid: 900
regioncode: custom
regionname: My Region
nodes:
- name: 900a
regionid: 900
hostname: myderp.example.com
ipv4: 198.51.100.1
ipv6: 2001:db8::1
stunport: 0
stunonly: false
derpport: 0

146
derp.yaml
View File

@@ -1,146 +0,0 @@
# This file contains some of the official Tailscale DERP servers,
# shamelessly taken from https://github.com/tailscale/tailscale/blob/main/net/dnsfallback/dns-fallback-servers.json
#
# If you plan to somehow use headscale, please deploy your own DERP infra: https://tailscale.com/kb/1118/custom-derp-servers/
regions:
1:
regionid: 1
regioncode: nyc
regionname: New York City
nodes:
- name: 1a
regionid: 1
hostname: derp1.tailscale.com
ipv4: 159.89.225.99
ipv6: "2604:a880:400:d1::828:b001"
stunport: 0
stunonly: false
derptestport: 0
- name: 1b
regionid: 1
hostname: derp1b.tailscale.com
ipv4: 45.55.35.93
ipv6: "2604:a880:800:a1::f:2001"
stunport: 0
stunonly: false
derptestport: 0
2:
regionid: 2
regioncode: sfo
regionname: San Francisco
nodes:
- name: 2a
regionid: 2
hostname: derp2.tailscale.com
ipv4: 167.172.206.31
ipv6: "2604:a880:2:d1::c5:7001"
stunport: 0
stunonly: false
derptestport: 0
- name: 2b
regionid: 2
hostname: derp2b.tailscale.com
ipv4: 64.227.106.23
ipv6: "2604:a880:4:1d0::29:9000"
stunport: 0
stunonly: false
derptestport: 0
3:
regionid: 3
regioncode: sin
regionname: Singapore
nodes:
- name: 3a
regionid: 3
hostname: derp3.tailscale.com
ipv4: 68.183.179.66
ipv6: "2400:6180:0:d1::67d:8001"
stunport: 0
stunonly: false
derptestport: 0
4:
regionid: 4
regioncode: fra
regionname: Frankfurt
nodes:
- name: 4a
regionid: 4
hostname: derp4.tailscale.com
ipv4: 167.172.182.26
ipv6: "2a03:b0c0:3:e0::36e:900"
stunport: 0
stunonly: false
derptestport: 0
- name: 4b
regionid: 4
hostname: derp4b.tailscale.com
ipv4: 157.230.25.0
ipv6: "2a03:b0c0:3:e0::58f:3001"
stunport: 0
stunonly: false
derptestport: 0
5:
regionid: 5
regioncode: syd
regionname: Sydney
nodes:
- name: 5a
regionid: 5
hostname: derp5.tailscale.com
ipv4: 103.43.75.49
ipv6: "2001:19f0:5801:10b7:5400:2ff:feaa:284c"
stunport: 0
stunonly: false
derptestport: 0
6:
regionid: 6
regioncode: blr
regionname: Bangalore
nodes:
- name: 6a
regionid: 6
hostname: derp6.tailscale.com
ipv4: 68.183.90.120
ipv6: "2400:6180:100:d0::982:d001"
stunport: 0
stunonly: false
derptestport: 0
7:
regionid: 7
regioncode: tok
regionname: Tokyo
nodes:
- name: 7a
regionid: 7
hostname: derp7.tailscale.com
ipv4: 167.179.89.145
ipv6: "2401:c080:1000:467f:5400:2ff:feee:22aa"
stunport: 0
stunonly: false
derptestport: 0
8:
regionid: 8
regioncode: lhr
regionname: London
nodes:
- name: 8a
regionid: 8
hostname: derp8.tailscale.com
ipv4: 167.71.139.179
ipv6: "2a03:b0c0:1:e0::3cc:e001"
stunport: 0
stunonly: false
derptestport: 0
9:
regionid: 9
regioncode: sao
regionname: São Paulo
nodes:
- name: 9a
regionid: 9
hostname: derp9.tailscale.com
ipv4: 207.148.3.137
ipv6: "2001:19f0:6401:1d9c:5400:2ff:feef:bb82"
stunport: 0
stunonly: false
derptestport: 0

92
dns.go
View File

@@ -1,92 +0,0 @@
package headscale
import (
"fmt"
"strings"
"github.com/fatih/set"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
)
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
// server (listening in 100.100.100.100 udp/53) should be used for.
//
// Tailscale.com includes in the list:
// - the `BaseDomain` of the user
// - the reverse DNS entry for IPv6 (0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa., see below more on IPv6)
// - the reverse DNS entries for the IPv4 subnets covered by the user's `IPPrefix`.
// In the public SaaS this is [64-127].100.in-addr.arpa.
//
// The main purpose of this function is then generating the list of IPv4 entries. For the 100.64.0.0/10, this
// is clear, and could be hardcoded. But we are allowing any range as `IPPrefix`, so we need to find out the
// subnets when we have 172.16.0.0/16 (i.e., [0-255].16.172.in-addr.arpa.), or any other subnet.
//
// How IN-ADDR.ARPA domains work is defined in RFC1035 (section 3.5). Tailscale.com seems to adhere to this,
// and do not make use of RFC2317 ("Classless IN-ADDR.ARPA delegation") - hence generating the entries for the next
// class block only.
// From the netmask we can find out the wildcard bits (the bits that are not set in the netmask).
// This allows us to then calculate the subnets included in the subsequent class block and generate the entries.
func generateMagicDNSRootDomains(ipPrefix netaddr.IPPrefix, baseDomain string) ([]dnsname.FQDN, error) {
// TODO(juanfont): we are not handing out IPv6 addresses yet
// and in fact this is Tailscale.com's range (note the fd7a:115c:a1e0: range in the fc00::/7 network)
ipv6base := dnsname.FQDN("0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.")
fqdns := []dnsname.FQDN{ipv6base}
// Conversion to the std lib net.IPnet, a bit easier to operate
netRange := ipPrefix.IPNet()
maskBits, _ := netRange.Mask.Size()
// lastOctet is the last IP byte covered by the mask
lastOctet := maskBits / 8
// wildcardBits is the number of bits not under the mask in the lastOctet
wildcardBits := 8 - maskBits%8
// min is the value in the lastOctet byte of the IP
// max is basically 2^wildcardBits - i.e., the value when all the wildcardBits are set to 1
min := uint(netRange.IP[lastOctet])
max := uint((min + 1<<uint(wildcardBits)) - 1)
// here we generate the base domain (e.g., 100.in-addr.arpa., 16.172.in-addr.arpa., etc.)
rdnsSlice := []string{}
for i := lastOctet - 1; i >= 0; i-- {
rdnsSlice = append(rdnsSlice, fmt.Sprintf("%d", netRange.IP[i]))
}
rdnsSlice = append(rdnsSlice, "in-addr.arpa.")
rdnsBase := strings.Join(rdnsSlice, ".")
for i := min; i <= max; i++ {
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%d.%s", i, rdnsBase))
if err != nil {
continue
}
fqdns = append(fqdns, fqdn)
}
return fqdns, nil
}
func getMapResponseDNSConfig(dnsConfigOrig *tailcfg.DNSConfig, baseDomain string, m Machine, peers Machines) (*tailcfg.DNSConfig, error) {
var dnsConfig *tailcfg.DNSConfig
if dnsConfigOrig != nil && dnsConfigOrig.Proxied { // if MagicDNS is enabled
// Only inject the Search Domain of the current namespace - shared nodes should use their full FQDN
dnsConfig = dnsConfigOrig.Clone()
dnsConfig.Domains = append(dnsConfig.Domains, fmt.Sprintf("%s.%s", m.Namespace.Name, baseDomain))
namespaceSet := set.New(set.ThreadSafe)
namespaceSet.Add(m.Namespace)
for _, p := range peers {
namespaceSet.Add(p.Namespace)
}
for _, namespace := range namespaceSet.List() {
dnsRoute := fmt.Sprintf("%s.%s", namespace.(Namespace).Name, baseDomain)
dnsConfig.Routes[dnsRoute] = nil
}
} else {
dnsConfig = dnsConfigOrig
}
return dnsConfig, nil
}

View File

@@ -1,306 +0,0 @@
package headscale
import (
"fmt"
"gopkg.in/check.v1"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/dnstype"
)
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
prefix := netaddr.MustParseIPPrefix("100.64.0.0/10")
domains, err := generateMagicDNSRootDomains(prefix, "foobar.headscale.net")
c.Assert(err, check.IsNil)
found := false
for _, domain := range domains {
if domain == "64.100.in-addr.arpa." {
found = true
break
}
}
c.Assert(found, check.Equals, true)
found = false
for _, domain := range domains {
if domain == "100.100.in-addr.arpa." {
found = true
break
}
}
c.Assert(found, check.Equals, true)
found = false
for _, domain := range domains {
if domain == "127.100.in-addr.arpa." {
found = true
break
}
}
c.Assert(found, check.Equals, true)
}
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
prefix := netaddr.MustParseIPPrefix("172.16.0.0/16")
domains, err := generateMagicDNSRootDomains(prefix, "headscale.net")
c.Assert(err, check.IsNil)
found := false
for _, domain := range domains {
if domain == "0.16.172.in-addr.arpa." {
found = true
break
}
}
c.Assert(found, check.Equals, true)
found = false
for _, domain := range domains {
if domain == "255.16.172.in-addr.arpa." {
found = true
break
}
}
c.Assert(found, check.Equals, true)
}
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
n1, err := h.CreateNamespace("shared1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 1,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Namespace: *n2,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 3,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Namespace: *n3,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{
ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.4",
AuthKeyID: uint(pak4n1.ID),
}
h.db.Save(m4)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
baseDomain := "foobar.headscale.net"
dnsConfigOrig := tailcfg.DNSConfig{
Routes: make(map[string][]dnstype.Resolver),
Domains: []string{baseDomain},
Proxied: true,
}
m1peers, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
dnsConfig, err := getMapResponseDNSConfig(&dnsConfigOrig, baseDomain, *m1, m1peers)
c.Assert(err, check.IsNil)
c.Assert(dnsConfig, check.NotNil)
c.Assert(len(dnsConfig.Routes), check.Equals, 2)
routeN1 := fmt.Sprintf("%s.%s", n1.Name, baseDomain)
_, ok := dnsConfig.Routes[routeN1]
c.Assert(ok, check.Equals, true)
routeN2 := fmt.Sprintf("%s.%s", n2.Name, baseDomain)
_, ok = dnsConfig.Routes[routeN2]
c.Assert(ok, check.Equals, true)
routeN3 := fmt.Sprintf("%s.%s", n3.Name, baseDomain)
_, ok = dnsConfig.Routes[routeN3]
c.Assert(ok, check.Equals, false)
}
func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
n1, err := h.CreateNamespace("shared1")
c.Assert(err, check.IsNil)
n2, err := h.CreateNamespace("shared2")
c.Assert(err, check.IsNil)
n3, err := h.CreateNamespace("shared3")
c.Assert(err, check.IsNil)
pak1n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak2n2, err := h.CreatePreAuthKey(n2.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak3n3, err := h.CreatePreAuthKey(n3.Name, false, false, nil)
c.Assert(err, check.IsNil)
pak4n1, err := h.CreatePreAuthKey(n1.Name, false, false, nil)
c.Assert(err, check.IsNil)
_, err = h.GetMachine(n1.Name, "test_get_shared_nodes_1")
c.Assert(err, check.NotNil)
m1 := &Machine{
ID: 1,
MachineKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
NodeKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
DiscoKey: "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
Name: "test_get_shared_nodes_1",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.1",
AuthKeyID: uint(pak1n1.ID),
}
h.db.Save(m1)
_, err = h.GetMachine(n1.Name, m1.Name)
c.Assert(err, check.IsNil)
m2 := &Machine{
ID: 2,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_2",
NamespaceID: n2.ID,
Namespace: *n2,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.2",
AuthKeyID: uint(pak2n2.ID),
}
h.db.Save(m2)
_, err = h.GetMachine(n2.Name, m2.Name)
c.Assert(err, check.IsNil)
m3 := &Machine{
ID: 3,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_3",
NamespaceID: n3.ID,
Namespace: *n3,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.3",
AuthKeyID: uint(pak3n3.ID),
}
h.db.Save(m3)
_, err = h.GetMachine(n3.Name, m3.Name)
c.Assert(err, check.IsNil)
m4 := &Machine{
ID: 4,
MachineKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
NodeKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
DiscoKey: "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
Name: "test_get_shared_nodes_4",
NamespaceID: n1.ID,
Namespace: *n1,
Registered: true,
RegisterMethod: "authKey",
IPAddress: "100.64.0.4",
AuthKeyID: uint(pak4n1.ID),
}
h.db.Save(m4)
err = h.AddSharedMachineToNamespace(m2, n1)
c.Assert(err, check.IsNil)
baseDomain := "foobar.headscale.net"
dnsConfigOrig := tailcfg.DNSConfig{
Routes: make(map[string][]dnstype.Resolver),
Domains: []string{baseDomain},
Proxied: false,
}
m1peers, err := h.getPeers(m1)
c.Assert(err, check.IsNil)
dnsConfig, err := getMapResponseDNSConfig(&dnsConfigOrig, baseDomain, *m1, m1peers)
c.Assert(err, check.IsNil)
c.Assert(dnsConfig, check.NotNil)
c.Assert(len(dnsConfig.Routes), check.Equals, 0)
c.Assert(len(dnsConfig.Domains), check.Equals, 1)
}

View File

@@ -1,33 +0,0 @@
# DNS in Headscale
Headscale supports Tailscale's DNS configuration and MagicDNS. Please have a look to their KB to better understand what this means:
- https://tailscale.com/kb/1054/dns/
- https://tailscale.com/kb/1081/magicdns/
- https://tailscale.com/blog/2021-09-private-dns-with-magicdns/
Long story short, you can define the DNS servers you want to use in your tailnets, activate MagicDNS (so you don't have to remember the IP addresses of your nodes), define search domains, as well as predefined hosts. Headscale will inject that settings into your nodes.
## Configuration reference
The setup is done via the `config.json` file, under the `dns_config` key.
```json
{
"server_url": "http://127.0.0.1:8001",
"listen_addr": "0.0.0.0:8001",
"private_key_path": "private.key",
//...
"dns_config": {
"nameservers": ["1.1.1.1", "8.8.8.8"],
"domains": [],
"magic_dns": true,
"base_domain": "example.com"
}
}
```
- `nameservers`: The list of DNS servers to use.
- `domains`: Search domains to inject.
- `magic_dns`: Whether to use [MagicDNS](https://tailscale.com/kb/1081/magicdns/). Only works if there is at least a nameserver defined.
- `base_domain`: Defines the base domain to create the hostnames for MagicDNS. `base_domain` must be a FQDNs, without the trailing dot. The FQDN of the hosts will be `hostname.namespace.base_domain` (e.g., _myhost.mynamespace.example.com_).

16
docs/about/clients.md Normal file
View File

@@ -0,0 +1,16 @@
# Client and operating system support
We aim to support the [**last 10 releases** of the Tailscale client](https://tailscale.com/changelog#client) on all
provided operating systems and platforms. Some platforms might require additional configuration to connect with
headscale.
| OS | Supports headscale |
| ------- | ----------------------------------------------------------------------------------------------------- |
| Linux | Yes |
| OpenBSD | Yes |
| FreeBSD | Yes |
| Windows | Yes (see [docs](../usage/connect/windows.md) and `/windows` on your headscale for more information) |
| Android | Yes (see [docs](../usage/connect/android.md) for more information) |
| macOS | Yes (see [docs](../usage/connect/apple.md#macos) and `/apple` on your headscale for more information) |
| iOS | Yes (see [docs](../usage/connect/apple.md#ios) and `/apple` on your headscale for more information) |
| tvOS | Yes (see [docs](../usage/connect/apple.md#tvos) and `/apple` on your headscale for more information) |

View File

@@ -0,0 +1,3 @@
{%
include-markdown "../../CONTRIBUTING.md"
%}

177
docs/about/faq.md Normal file
View File

@@ -0,0 +1,177 @@
# 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_ Tailscale
network (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.
Please see [Contributing](contributing.md) for more information.
## 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're interested in contributing, please post a feature request about it.
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 satisfy 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. Visit our [installation guide using
official releases](../setup/install/official.md) for more information.
In addition to that, you may use packages provided by the community or from distributions. Learn more in the
[installation guide using community packages](../setup/install/community.md).
For convenience, we also [build container images with headscale](../setup/install/container.md). But **please be aware that
we don't officially support deploying headscale using Docker**. On our [Discord server](https://discord.gg/c84AZQhmpx)
we have a "docker-issues" channel where you can ask for Docker-specific help to the community.
## What is the recommended update path? Can I skip multiple versions while updating?
Please follow the steps outlined in the [upgrade guide](../setup/upgrade.md) to update your existing Headscale
installation. Its best to update from one stable version to the next (e.g. 0.24.0 &rarr; 0.25.1 &rarr; 0.26.1) in case
you are multiple releases behind. You should always pick the latest available patch release.
Be sure to check the [changelog](https://github.com/juanfont/headscale/blob/main/CHANGELOG.md) for version specific
upgrade instructions and breaking changes.
## Scaling / How many clients does Headscale support?
It depends. As often stated, Headscale is not enterprise software and our focus
is homelabbers and self-hosters. Of course, we do not prevent people from using
it in a commercial/professional setting and often get questions about scaling.
Please note that when Headscale is developed, performance is not part of the
consideration as the main audience is considered to be users with a modest
amount of devices. We focus on correctness and feature parity with Tailscale
SaaS over time.
To understand if you might be able to use Headscale for your use case, I will
describe two scenarios in an effort to explain what is the central bottleneck
of Headscale:
1. An environment with 1000 servers
- they rarely "move" (change their endpoints)
- new nodes are added rarely
2. An environment with 80 laptops/phones (end user devices)
- nodes move often, e.g. switching from home to office
Headscale calculates a map of all nodes that need to talk to each other,
creating this "world map" requires a lot of CPU time. When an event that
requires changes to this map happens, the whole "world" is recalculated, and a
new "world map" is created for every node in the network.
This means that under certain conditions, Headscale can likely handle 100s
of devices (maybe more), if there is _little to no change_ happening in the
network. For example, in Scenario 1, the process of computing the world map is
extremely demanding due to the size of the network, but when the map has been
created and the nodes are not changing, the Headscale instance will likely
return to a very low resource usage until the next time there is an event
requiring the new map.
In the case of Scenario 2, the process of computing the world map is less
demanding due to the smaller size of the network, however, the type of nodes
will likely change frequently, which would lead to a constant resource usage.
Headscale will start to struggle when the two scenarios overlap, e.g. many nodes
with frequent changes will cause the resource usage to remain constantly high.
In the worst case scenario, the queue of nodes waiting for their map will grow
to a point where Headscale never will be able to catch up, and nodes will never
learn about the current state of the world.
We expect that the performance will improve over time as we improve the code
base, but it is not a focus. In general, we will never make the tradeoff to make
things faster on the cost of less maintainable or readable code. We are a small
team and have to optimise for maintainability.
## Which database should I use?
We recommend the use of SQLite as database for headscale:
- SQLite is simple to setup and easy to use
- It scales well for all of headscale's use cases
- Development and testing happens primarily on SQLite
- PostgreSQL is still supported, but is considered to be in "maintenance mode"
The headscale project itself does not provide a tool to migrate from PostgreSQL to SQLite. Please have a look at [the
related tools documentation](../ref/integration/tools.md) for migration tooling provided by the community.
The choice of database has little to no impact on the performance of the server,
see [Scaling / How many clients does Headscale support?](#scaling-how-many-clients-does-headscale-support) for understanding how Headscale spends its resources.
## 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](../ref/integration/reverse-proxy.md) on how to configure various reverse proxies, and a
dedicated "reverse-proxy-issues" channel on our [Discord server](https://discord.gg/c84AZQhmpx) where you can ask for
help to the community.
## Can I use headscale and tailscale on the same machine?
Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported.
## Why do two nodes see each other in their status, even if an ACL allows traffic only in one direction?
A frequent use case is to allow traffic only from one node to another, but not the other way around. For example, the
workstation of an administrator should be able to connect to all nodes but the nodes themselves shouldn't be able to
connect back to the administrator's node. Why do all nodes see the administrator's workstation in the output of
`tailscale status`?
This is essentially how Tailscale works. If traffic is allowed to flow in one direction, then both nodes see each other
in their output of `tailscale status`. Traffic is still filtered according to the ACL, with the exception of `tailscale
ping` which is always allowed in either direction.
See also <https://tailscale.com/kb/1087/device-visibility>.
## My policy is stored in the database and Headscale refuses to start due to an invalid policy. How can I recover?
Headscale checks if the policy is valid during startup and refuses to start if it detects an error. The error message
indicates which part of the policy is invalid. Follow these steps to fix your policy:
- Dump the policy to a file: `headscale policy get --bypass-grpc-and-access-database-directly > policy.json`
- Edit and fixup `policy.json`. Use the command `headscale policy check --file policy.json` to validate the policy.
- Load the modified policy: `headscale policy set --bypass-grpc-and-access-database-directly --file policy.json`
- Start Headscale as usual.
!!! warning "Full server configuration required"
The above commands to get/set the policy require a complete server configuration file including database settings. A
minimal config to [control Headscale via remote CLI](../ref/api.md#grpc) is not sufficient. You may use `headscale
-c /path/to/config.yaml` to specify the path to an alternative configuration file.
## How can I avoid to send logs to Tailscale Inc?
A Tailscale client [collects logs about its operation and connection attempts with other
clients](https://tailscale.com/kb/1011/log-mesh-traffic#client-logs) and sends them to a central log service operated by
Tailscale Inc.
Headscale, by default, instructs clients to disable log submission to the central log service. This configuration is
applied by a client once it successfully connected with Headscale. See the configuration option `logtail.enabled` in the
[configuration file](../ref/configuration.md) for details.
Alternatively, logging can also be disabled on the client side. This is independent of Headscale and opting out of
client logging disables log submission early during client startup. The configuration is operating system specific and
is usually achieved by setting the environment variable `TS_NO_LOGS_NO_SUPPORT=true` or by passing the flag
`--no-logs-no-support` to `tailscaled`. See
<https://tailscale.com/kb/1011/log-mesh-traffic#opting-out-of-client-logging> for details.

37
docs/about/features.md Normal file
View File

@@ -0,0 +1,37 @@
# Features
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. This page
provides on overview of Headscale's feature and compatibility with the Tailscale control server:
- [x] Full "base" support of Tailscale's features
- [x] Node registration
- [x] Interactive
- [x] Pre authenticated key
- [x] [DNS](../ref/dns.md)
- [x] [MagicDNS](https://tailscale.com/kb/1081/magicdns)
- [x] [Global and restricted nameservers (split DNS)](https://tailscale.com/kb/1054/dns#nameservers)
- [x] [search domains](https://tailscale.com/kb/1054/dns#search-domains)
- [x] [Extra DNS records (Headscale only)](../ref/dns.md#setting-extra-dns-records)
- [x] [Taildrop (File Sharing)](https://tailscale.com/kb/1106/taildrop)
- [x] [Routes](../ref/routes.md)
- [x] [Subnet routers](../ref/routes.md#subnet-router)
- [x] [Exit nodes](../ref/routes.md#exit-node)
- [x] Dual stack (IPv4 and IPv6)
- [x] Ephemeral nodes
- [x] Embedded [DERP server](../ref/derp.md)
- [x] Access control lists ([GitHub label "policy"](https://github.com/juanfont/headscale/labels/policy%20%F0%9F%93%9D))
- [x] ACL management via API
- [x] Some [Autogroups](https://tailscale.com/kb/1396/targets#autogroups), currently: `autogroup:internet`,
`autogroup:nonroot`, `autogroup:member`, `autogroup:tagged`, `autogroup:self`
- [x] [Auto approvers](https://tailscale.com/kb/1337/acl-syntax#auto-approvers) for [subnet
routers](../ref/routes.md#automatically-approve-routes-of-a-subnet-router) and [exit
nodes](../ref/routes.md#automatically-approve-an-exit-node-with-auto-approvers)
- [x] [Tailscale SSH](https://tailscale.com/kb/1193/tailscale-ssh)
* [x] [Node registration using Single-Sign-On (OpenID Connect)](../ref/oidc.md) ([GitHub label "OIDC"](https://github.com/juanfont/headscale/labels/OIDC))
- [x] Basic registration
- [x] Update user profile from identity provider
- [ ] OIDC groups cannot be used in ACLs
- [ ] [Funnel](https://tailscale.com/kb/1223/funnel) ([#1040](https://github.com/juanfont/headscale/issues/1040))
- [ ] [Serve](https://tailscale.com/kb/1312/serve) ([#1234](https://github.com/juanfont/headscale/issues/1921))
- [ ] [Network flow logs](https://tailscale.com/kb/1219/network-flow-logs) ([#1687](https://github.com/juanfont/headscale/issues/1687))

5
docs/about/help.md Normal file
View File

@@ -0,0 +1,5 @@
# Getting help
Join our [Discord server](https://discord.gg/c84AZQhmpx) for announcements and community support.
Please report bugs via [GitHub issues](https://github.com/juanfont/headscale/issues)

10
docs/about/releases.md Normal file
View File

@@ -0,0 +1,10 @@
# Releases
All headscale releases are available on the [GitHub release page](https://github.com/juanfont/headscale/releases). Those
releases are available as binaries for various platforms and architectures, packages for Debian based systems and source
code archives. Container images are available on [Docker Hub](https://hub.docker.com/r/headscale/headscale) and
[GitHub Container Registry](https://github.com/juanfont/headscale/pkgs/container/headscale).
An Atom/RSS feed of headscale releases is available [here](https://github.com/juanfont/headscale/releases.atom).
See the "announcements" channel on our [Discord server](https://discord.gg/c84AZQhmpx) for news about headscale.

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