1747 Commits

Author SHA1 Message Date
Simon Law
c18ba4470b
ipn/ipnlocal: add traffic steering support to exit-node suggestions (#16527)
When `tailscale exit-node suggest` contacts the LocalAPI for a
suggested exit node, the client consults its netmap for peers that
contain the `suggest-exit-node` peercap. It currently uses a series of
heuristics to determine the exit node to suggest.

When the `traffic-steering` feature flag is enabled on its tailnet,
the client will defer to Control’s priority scores for a particular
peer. These scores, in `tailcfg.Hostinfo.Location.Priority`, were
historically only used for Mullvad exit nodes, but they have now been
extended to score any peer that could host a redundant resource.

Client capability version 119 is the earliest client that understands
these traffic steering scores. Control tells the client to switch to
rely on these scores by adding `tailcfg.NodeAttrTrafficSteering` to
its `AllCaps`.

Updates tailscale/corp#29966

Signed-off-by: Simon Law <sfllaw@tailscale.com>
2025-07-10 22:15:55 -07:00
Dylan Bargatze
fed72e2aa9
cmd/tailscale, ipn/ipnstate, wgengine/magicsock: update ping output for peer relay (#16515)
Updates the output for "tailscale ping" to indicate if a peer relay was traversed, just like the output for DERP or direct connections.

Fixes tailscale/corp#30034

Signed-off-by: Dylan Bargatze <dylan@tailscale.com>
2025-07-10 18:22:25 -04:00
Claus Lensbøl
fbc4c34cf7
ipn/localapi: do not break client on event marshalling errors (#16503)
Errors were mashalled without the correct newlines. Also, they could
generally be mashalled with more data, so an intermediate was introduced
to make them slightly nicer to look at.

Updates #15160

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2025-07-10 03:04:29 -04:00
Jordan Whited
ae8641735d
cmd/tailscale/cli,ipn/ipnstate,wgengine/magicsock: label peer-relay (#16510)
Updates tailscale/corp#30033

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2025-07-09 15:17:51 -07:00
Nick Khyl
ff1803158a ipn/ipnlocal: change order of exit node refresh and netmap update so that clients receive the new netmap first
If the GUI receives a new exit node ID before the new netmap, it may treat the node as offline or invalid
if the previous netmap didn't include the peer at all, or if the peer was offline or not advertised as an exit node.
This may result in briefly issuing and dismissing a warning, or a similar issue, which isn't ideal.

In this PR, we change the operation order to send the new netmap to clients first before selecting the new exit node
and notifying them of the Exit Node change.

Updates tailscale/corp#30252 (an old issue discovered during testing this)

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-09 13:45:33 -05:00
Nick Khyl
21a4058ec7 ipn/ipnlocal: add test to verify handling of unknown auto exit node expressions
We already check this for cases where ipn.Prefs.AutoExitNode is configured via syspolicy.
Configuring it directly through EditPrefs should behave the same, so we add a test for that as well.

Additionally, we clarify the implementation and future extensibility in (*LocalBackend).resolveAutoExitNodeLocked,
where the AutoExitNode is actually enforced.

Updates tailscale/corp#29969
Updates #cleanup

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-09 12:48:12 -05:00
Nick Khyl
cc2f4ac921 ipn: move ParseAutoExitNodeID from ipn/ipnlocal to ipn
So it can be used from the CLI without importing ipnlocal.

While there, also remove isAutoExitNodeID, a wrapper around parseAutoExitNodeID
that's no longer used.

Updates tailscale/corp#29969
Updates #cleanup

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-09 12:48:12 -05:00
Nick Khyl
740b77df59 ipn/ipnlocal,util/syspolicy: add support for ExitNode.AllowOverride policy setting
When the policy setting is enabled, it allows users to override the exit node enforced by the ExitNodeID
or ExitNodeIP policy. It's primarily intended for use when ExitNodeID is set to auto:any, but it can also
be used with specific exit nodes. It does not allow disabling exit node usage entirely.

Once the exit node policy is overridden, it will not be enforced again until the policy changes,
the user connects or disconnects Tailscale, switches profiles, or disables the override.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-08 17:17:47 -05:00
Nick Khyl
2c630e126b ipn/ipnlocal: make applySysPolicy a method on LocalBackend
Now that applySysPolicy is only called by (*LocalBackend).reconcilePrefsLocked,
we can make it a method to avoid passing state via parameters and to support
future extensibility.

Also factor out exit node-specific logic into applyExitNodeSysPolicyLocked.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-08 16:06:40 -05:00
Nick Khyl
9bf99741dd ipn/ipnlocal: refactor resolveExitNodeInPrefsLocked, setExitNodeID and resolveExitNodeIP
Now that resolveExitNodeInPrefsLocked is the only caller of setExitNodeID,
and setExitNodeID is the only caller of resolveExitNodeIP, we can restructure
the code with resolveExitNodeInPrefsLocked now calling both
resolveAutoExitNodeLocked and resolveExitNodeIPLocked directly.

This prepares for factoring out resolveAutoExitNodeLocked and related
auto-exit-node logic into an ipnext extension in a future commit.

While there, we also update exit node by IP lookup to use (*nodeBackend).NodeByAddr
and (*nodeBackend).NodeByID instead of iterating over all peers in the most recent netmap.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-08 16:06:24 -05:00
Nick Khyl
1fe82d6ef5 cmd/tailscale/cli,ipn/ipnlocal: restrict logout when AlwaysOn mode is enabled
In this PR, we start passing a LocalAPI actor to (*LocalBackend).Logout to make it subject
to the same access check as disconnects made via tailscale down or the GUI.

We then update the CLI to allow `tailscale logout` to accept a reason, similar to `tailscale down`.

Updates tailscale/corp#26249

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-08 15:02:51 -05:00
Nick Khyl
ea4018b757 ipn/ipnlocal: fix missing defer in testExtension.Shutdown
Updates #cleanup

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 19:19:32 -05:00
Nick Khyl
f1c7b463cd ipn/{ipnauth,ipnlocal,localapi}: make EditPrefs return an error if changing exit node is restricted by policy
We extract checkEditPrefsAccessLocked, adjustEditPrefsLocked, and onEditPrefsLocked from the EditPrefs
execution path, defining when each step is performed and what behavior is allowed at each stage.

Currently, this is primarily used to support Always On mode, to handle the Exit Node enablement toggle,
and to report prefs edit metrics.

We then use it to enforce Exit Node policy settings by preventing users from setting an exit node
and making EditPrefs return an error when an exit node is restricted by policy. This enforcement is also
extended to the Exit Node toggle.

These changes prepare for supporting Exit Node overrides when permitted by policy and preventing logout
while Always On mode is enabled.

In the future, implementation of these methods can be delegated to ipnext extensions via the feature hooks.

Updates tailscale/corp#29969
Updates tailscale/corp#26249

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 17:18:00 -05:00
Nick Khyl
cb7b49941e ipn/ipnlocal: add (*LocalBackend).reconcilePrefsLocked
We have several places where we call applySysPolicy, suggestExitNodeLocked, and setExitNodeID.
While there are cases where we want to resolve the exit node specifically, such as when network
conditions change or a new netmap is received, we typically need to perform all three steps.
For example, enforcing policy settings may enable auto exit nodes or set an ExitNodeIP,
which in turn requires picking a suggested exit node or resolving the IP to an ID, respectively.

In this PR, we introduce (*LocalBackend).resolveExitNodeInPrefsLocked and (*LocalBackend).reconcilePrefsLocked,
with the latter calling both applySysPolicy and resolveExitNodeInPrefsLocked.

Consolidating these steps into a single extensibility point would also make it easier to support
future hooks registered by ipnext extensions.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 16:09:18 -05:00
Nick Khyl
381fdcc3f1 ipn/ipnlocal,util/syspolicy/source: retain existing exit node when using auto exit node, if it's allowed by policy
In this PR, we update setExitNodeID to retain the existing exit node if auto exit node is enabled,
the current exit node is allowed by policy, and no suggested exit node is available yet.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 15:38:30 -05:00
Nick Khyl
4c1c0bac8d ipn/ipnlocal: plumb nodeBackend into suggestExitNode to support delta updates, such as online status changes
Now that (*LocalBackend).suggestExitNodeLocked is never called with a non-current netmap
(the netMap parameter is always nil, indicating that the current netmap should be used),
we can remove the unused parameter.

Additionally, instead of suggestExitNodeLocked passing the most recent full netmap to suggestExitNode,
we now pass the current nodeBackend so it can access peers with delta updates applied.

Finally, with that fixed, we no longer need to skip TestUpdateNetmapDeltaAutoExitNode.

Updates tailscale/corp#29969
Fixes #16455

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 15:36:34 -05:00
Nick Khyl
3e01652e4d ipn/ipnlocal: add (*LocalBackend).RefreshExitNode
In this PR, we add (*LocalBackend).RefreshExitNode which determines which exit node
to use based on the current prefs and netmap and switches to it if needed. It supports
both scenarios when an exit node is specified by IP (rather than ID) and needs to be resolved
once the netmap is ready as well as auto exit nodes.

We then use it in (*LocalBackend).SetControlClientStatus when the netmap changes,
and wherever (*LocalBackend).pickNewAutoExitNode was previously used.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-07 15:13:49 -05:00
Nick Khyl
a8055b5f40 cmd/tailscale/cli,ipn,ipn/ipnlocal: add AutoExitNode preference for automatic exit node selection
With this change, policy enforcement and exit node resolution can happen in separate steps,
since enforcement no longer depends on resolving the suggested exit node. This keeps policy
enforcement synchronous (e.g., when switching profiles), while allowing exit node resolution
to be asynchronous on netmap updates, link changes, etc.

Additionally, the new preference will be used to let GUIs and CLIs switch back to "auto" mode
after a manual exit node override, which is necessary for tailscale/corp#29969.

Updates tailscale/corp#29969
Updates #16459

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-03 14:12:25 -05:00
Nick Khyl
0098822981 ipn/ipnlocal: update suggestExitNode to skip offline candidates and fix TestSetControlClientStatusAutoExitNode
TestSetControlClientStatusAutoExitNode is broken similarly to TestUpdateNetmapDeltaAutoExitNode
as suggestExitNode didn't previously check the online status of exit nodes, and similarly to the other test
it succeeded because the test itself is also broken.

However, it is easier to fix as it sends out a full netmap update rather than a delta peer update,
so it doesn't depend on the same refactoring as TestSetControlClientStatusAutoExitNode.

Updates #16455
Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-03 13:54:33 -05:00
Nick Khyl
6ecc25b26a ipn/ipnlocal: skip TestUpdateNetmapDeltaAutoExitNode
suggestExitNode never checks whether an exit node candidate is online.
It also accepts a full netmap, which doesn't include changes from delta updates.
The test can't work correctly until both issues are fixed.

Previously, it passed only because the test itself is flawed.
It doesn't succeed because the currently selected node goes offline and a new one is chosen.
Instead, it succeeds because lastSuggestedExitNode is incorrect, and suggestExitNode picks
the correct node the first time it runs, based on the DERP map and the netcheck report.
The node in exitNodeIDWant just happens to be the optimal choice.

Fixing SuggestExitNode requires refactoring its callers first, which in turn reveals the flawed test,
as suggestExitNode ends up being called slightly earlier.

In this PR, we update the test to correctly fail due to existing bugs in SuggestExitNode,
and temporarily skip it until those issues are addressed in a future commit.

Updates #16455
Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-03 13:54:33 -05:00
Nick Khyl
56d772bd63 ipn/ipnlocal: simplify pickNewAutoExitNode
(*profileManager).CurrentPrefs() is always valid. Additionally, there's no value in cloning
and passing the full ipn.Prefs when editing preferences. Instead, ipn.MaskedPrefs should
only have ExitNodeID set.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-03 13:54:33 -05:00
Nick Khyl
1a2185b1ee ipn/ipnlocal: rename setAutoExitNodeIDLockedOnEntry to pickNewAutoExitNode; drop old function
Currently, (*LocalBackend).pickNewAutoExitNode() is just a wrapper around
setAutoExitNodeIDLockedOnEntry that sends a prefs-change notification at the end.
It doesn't need to do that, since setPrefsLockedOnEntry already sends the notification
(setAutoExitNodeIDLockedOnEntry calls it via editPrefsLockedOnEntry).

This PR removes the old pickNewAutoExitNode function and renames
setAutoExitNodeIDLockedOnEntry to pickNewAutoExitNode for clarity.

Updates tailscale/corp#29969

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-07-03 13:54:33 -05:00
Andrew Lytvynov
172e26b3e3
tailcfg: report StateEncrypted in Hostinfo (#16434)
Report whether the client is configured with state encryption (which
varies by platform and can be optional on some). Wire it up to
`--encrypt-state` in tailscaled, which is set for Linux/Windows, and set
defaults for other platforms. Macsys will also report this if full
Keychain migration is done.

Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
2025-07-02 10:52:00 -07:00
kari-ts
d15b2312c4
tailcfg: add CapabilityOwner (#16426)
We would like to start sending whether a node is a Tailnet owner in netmap responses so that clients can determine what information to display to a user who wants to request account deletion.

Updates tailscale/corp#30016

Signed-off-by: kari-ts <kari@tailscale.com>
2025-07-01 09:28:48 -07:00
Percy Wegmann
454d856be8 drive,ipn/ipnlocal: calculate peer taildrive URLs on-demand
Instead of calculating the PeerAPI URL at the time that we add the peer,
we now calculate it on every access to the peer. This way, if we
initially did not have a shared address family with the peer, but
later do, this allows us to access the peer at that point. This
follows the pattern from other places where we access the peer API,
which also calculate the URL on an as-needed basis.

Additionally, we now show peers as not Available when we can't get
a peer API URL.

Lastly, this moves some of the more frequent verbose Taildrive logging
from [v1] to [v2] level.

Updates #29702

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2025-07-01 10:59:58 -05:00
Percy Wegmann
6a9bf9172b ipn/ipnlocal: add verbose Taildrive logging on client side
This allows logging the following Taildrive behavior from the client's perspective
when --verbose=1:

- Initialization of Taildrive remotes for every peer
- Peer availability checks
- All HTTP requests to peers (not just GET and PUT)

Updates tailscale/corp#29702

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2025-06-30 14:29:04 -05:00
Andrew Lytvynov
76b9afb54d
ipn/store: make StateStore.All optional (#16409)
This method is only needed to migrate between store.FileStore and
tpm.tpmStore. We can make a runtime type assertion instead of
implementing an unused method for every platform.

Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
2025-06-27 15:14:18 -07:00
Andrew Lytvynov
6feb3c35cb
ipn/store: automatically migrate between plaintext and encrypted state (#16318)
Add a new `--encrypt-state` flag to `cmd/tailscaled`. Based on that
flag, migrate the existing state file to/from encrypted format if
needed.

Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
2025-06-26 17:09:13 -07:00
JerryYan
99aaa6e92c
ipn/ipnlocal: update PeerByID to return SelfNode and rename it to NodeByID (#16096)
Like NodeByKey, add an if stmt for checking the NodeId is SelfNode.

Updates #16052

Signed-off-by: Jerry Yan <792602257@qq.com>
2025-06-26 11:43:48 -05:00
Nick Khyl
9e28bfc69c ipn/ipnlocal,wgengine/magicsock: wait for magicsock to process pending events on authReconfig
Updates #16369

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-06-24 16:39:08 -05:00
Andrew Lytvynov
4979ce7a94
feature/tpm: implement ipn.StateStore using TPM sealing (#16030)
Updates #15830

Signed-off-by: Andrew Lytvynov <awly@tailscale.com>
2025-06-18 14:17:12 -07:00
Jordan Whited
fcab50b276
ipn/ipnlocal,wgengine{/magicsock}: replace SetNetworkMap with eventbus (#16299)
Same with UpdateNetmapDelta.

Updates tailscale/corp#27502
Updates #15160

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2025-06-18 10:31:00 -07:00
Anton Tolchanov
a91fcc8813 ipn/ipnlocal: make pricing restriction message for Tailnet Lock clearer
Fixes tailscale/corp#24417

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
2025-06-18 15:01:37 +01:00
Juan Francisco Cantero Hurtado
cbc14bd3b0 ipn: add missing entries for OpenBSD
Signed-off-by: Juan Francisco Cantero Hurtado <jfch@30041993.xyz>
2025-06-17 17:53:22 -07:00
Percy Wegmann
4431fb89c2 ipn/ipnlocal: add some verbose logging to taildrive peerapi handler
Updates tailscale/corp#29702

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2025-06-17 17:01:36 -05:00
Brad Fitzpatrick
259bab9bff scripts/check_license_headers.sh: delete, rewrite as a Go test
Updates tailscale/corp#29650

Change-Id: Iad4e4ccd9d68ebb1d1a12f335cc5295d0bd05b60
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-06-16 13:35:38 -07:00
Jordan Whited
8e6f63cf11
ipn/ipnlocal,wgengine/magicsock: use eventbus for node & filter updates (#16271)
nodeBackend now publishes filter and node changes to eventbus topics
that are consumed by magicsock.Conn

Updates tailscale/corp#27502
Updates tailscale/corp#29543

Signed-off-by: Jordan Whited <jordan@tailscale.com>
2025-06-16 08:42:09 -07:00
Nick Khyl
733bfaeffe ipn/ipnlocal: signal nodeBackend readiness and shutdown
We update LocalBackend to shut down the current nodeBackend
when switching to a different node, and to mark the new node's
nodeBackend as ready when the switch completes.

Updates tailscale/corp#28014
Updates tailscale/corp#29543
Updates #12614

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-06-13 17:58:21 -05:00
Nick Khyl
6a4d92ecef ipn/ipnlocal: replace nodeContext with nodeBackend in comments
We renamed the type in #15866 but didn't update the comments at the time.

Updates #cleanup

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-06-13 14:53:08 -05:00
Claus Lensbøl
6010812f0c
ipn/localapi,client/local: add debug watcher for bus events (#16239)
Updates: #15160

Signed-off-by: Claus Lensbøl <claus@tailscale.com>
2025-06-11 14:22:30 -04:00
Anton Tolchanov
db34cdcfe7 cmd/tailscale/cli: add a risk message about rp_filter
We already present a health warning about this, but it is easy to miss
on a server when blackholing traffic makes it unreachable.

In addition to a health warning, present a risk message when exit node
is enabled.

Example:

```
$ tailscale up --exit-node=lizard
The following issues on your machine will likely make usage of exit nodes impossible:
- interface "ens4" has strict reverse-path filtering enabled
- interface "tailscale0" has strict reverse-path filtering enabled
Please set rp_filter=2 instead of rp_filter=1; see https://github.com/tailscale/tailscale/issues/3310
To skip this warning, use --accept-risk=linux-strict-rp-filter
$
```

Updates #3310

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
2025-06-10 07:38:06 +01:00
James Sanderson
5716d0977d health: prefix Warnables received from the control plane
Updates tailscale/corp#27759

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
2025-06-09 10:35:22 +01:00
KevinLiang10
7b06532ea1
ipn/ipnlocal: Update hostinfo to control on service config change (#16146)
This commit fixes the bug that c2n requests are skiped when updating vipServices in serveConfig. This then resulted
netmap update being skipped which caused inaccuracy of Capmap info on client side. After this fix, client always
inform control about it's vipServices config changes.

Fixes tailscale/corp#29219

Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
2025-06-06 15:20:23 -04:00
James Sanderson
5fde183754 ipn: add watch opt to include actions in health messages
Updates tailscale/corp#27759

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
2025-06-04 11:01:02 +01:00
James Sanderson
8a3afa5963 ipn/ipnlocal: fix deadlock when filtering DisplayMessage URLs
Updates tailscale/corp#27759

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
2025-06-03 11:09:38 +01:00
James Sanderson
11e83f9da5 controlclient,health,ipnlocal,tailcfg: add DisplayMessage support
Updates tailscale/corp#27759

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
2025-05-30 14:48:11 +01:00
Nick Khyl
4cccd15eeb ipn/ipnlocal: fix data race when accessing b.appConnector
The field must only be accessed while holding LocalBackend's mutex,
but there are two places where it's accessed without the mutex:
 - (LocalBackend).MaybeClearAppConnector()
 - handleC2NAppConnectorDomainRoutesGet()

Fixes #16123

Signed-off-by: Nick Khyl <nickk@tailscale.com>
2025-05-29 14:07:03 -05:00
Jonathan Nobels
842df37803
ipn: set RouteAll=true by default for new accounts on iOS and Android (#16110)
fixes tailscale/tailscale#16082

RouteAll should be true by default on iOS and Android.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
2025-05-28 10:08:06 -04:00
Zach Buchheit
4a11514db5
ipn/ipnlocal: improve dohQuery error to suggest ?dns= and ?q= (#16056)
Previously, a missing or invalid `dns` parameter on GET `/dns-query`
returned only “missing ‘dns’ parameter”. Now the error message guides
users to use `?dns=` or `?q=`.

Updates: #16055

Signed-off-by: Zach Buchheit <zachb@tailscale.com>
2025-05-23 14:17:28 -07:00
James 'zofrex' Sanderson
aa8bc23c49
control/controlclient,health,tailcfg: refactor control health messages (#15839)
* control/controlclient,health,tailcfg: refactor control health messages

Updates tailscale/corp#27759

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
Signed-off-by: Paul Scott <408401+icio@users.noreply.github.com>
Co-authored-by: Paul Scott <408401+icio@users.noreply.github.com>
2025-05-22 13:40:32 +01:00