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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
(*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>
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>
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>
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>
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>
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>
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>
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.
Fixestailscale/corp#29219
Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
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>
* 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>
Currently, LocalBackend/ExtensionHost doesn't invoke the profile change callback for the initial profile.
Since the initial profile may vary depending on loaded extensions and applied policy settings,
it can't be reliably determined until all extensions are initialized. Additionally, some extensions
may asynchronously trigger a switch to the "best" profile (based on system state and policy settings) during
initialization.
We intended to address these issues as part of the ongoing profileManager/LocalBackend refactoring,
but the changes didn't land in time for the v1.84 release and the Taildrop refactoring.
In this PR, we update the Taildrop extension to retrieve the current profile at initialization time
and handle it as a profile change.
We also defer extension initialization until LocalBackend has started, since the Taildrop extension
already relies on this behavior (e.g., it requires clients to call SetDirectFileRoot before Init).
Fixes#15970
Updates #15812
Updates tailscale/corp#28449
Signed-off-by: Nick Khyl <nickk@tailscale.com>
We previously kept these methods in local.go when we started moving node-specific state
from LocalBackend to nodeBackend, to make those changes easier to review. But it's time
to move them to node_backend.go.
Updates #cleanup
Updates #12614
Signed-off-by: Nick Khyl <nickk@tailscale.com>
In this PR, we make the "user-dial-routes" behavior default on all platforms except for iOS and Android.
It can be disabled by setting the TS_DNS_FORWARD_USE_ROUTES envknob to 0 or false.
Updates #12027
Updates #13837
Signed-off-by: Nick Khyl <nickk@tailscale.com>
I'd moved the osshare calls to feature/taildrop hooks, but forgot to
remove them from ipnlocal, or lost them during a rebase.
But then I noticed cmd/tailscaled also had some, so turn those into a
hook.
Updates #12614
Change-Id: I024fb1d27fbcc49c013158882ee5982c2737037d
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This is an integration test that covers all the code in Direct, Auto, and
LocalBackend that processes NetMaps and creates a Filter. The test uses
tsnet as a convenient proxy for setting up all the client pieces correctly,
but is not actually a test specific to tsnet.
Updates tailscale/corp#20514
Signed-off-by: James Sanderson <jsanderson@tailscale.com>
Until we turn on AAAA by default (which might make some people rely on
Happy Eyeballs for targets without IPv6), this lets people turn it on
explicitly if they want.
We still should add a peer cap as well in the future to let a peer
explicitly say that it's cool with IPv6.
Related: #9574
Updates #1813
Updates #1152
Change-Id: Iec6ec9b4b5db7a4dc700ecdf4a11146cc5303989
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Cleanup after #15866. It was using a mix of "b" and "c" before. But "b"
is ambiguous with LocalBackend's usual "b".
Updates #12614
Change-Id: I8c2e84597555ec3db0d783a00ac1c12549ce6706
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
As just discussed on Slack with @nickkhyl.
Updates #12614
Change-Id: I138dd7eaffb274494297567375d969b4122f3f50
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This fixes the Taildrop deadlock from 8b72dd787320.
Fixes#15824
Change-Id: I5ca583de20dd0d0b513ce546439dc632408ca1f1
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
We update profileManager to allow registering a single state (profile+prefs) change hook.
This is to invert the dependency between the profileManager and the LocalBackend, so that
instead of LocalBackend asking profileManager for the state, we can have profileManager
call LocalBackend when the state changes.
We also update feature.Hook with a new (*feature.Hook).GetOk method to avoid calling both
IsSet and Get.
Updates tailscale/corp#28014
Updates #12614
Signed-off-by: Nick Khyl <nickk@tailscale.com>
This adds a feature/taildrop package, a ts_omit_taildrop build tag,
and starts moving code to feature/taildrop. In some cases, code
remains where it was but is now behind a build tag. Future changes
will move code to an extension and out of LocalBackend, etc.
Updates #12614
Change-Id: Idf96c61144d1a5f707039ceb2ff59c99f5c1642f
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
In this PR, we enable extensions to track changes in the current prefs. These changes can result from a profile switch
or from the user or system modifying the current profile’s prefs. Since some extensions may want to distinguish between
the two events, while others may treat them similarly, we rename the existing profile-change callback to become
a profile-state-change callback and invoke it whenever the current profile or its preferences change. Extensions can still
use the sameNode parameter to distinguish between situations where the profile information, including its preferences,
has been updated but still represents the same tailnet node, and situations where a switch to a different profile has been made.
Having dedicated prefs-change callbacks is being considered, but currently seems redundant. A single profile-state-change callback
is easier to maintain. We’ll revisit the idea of adding a separate callback as we progress on extracting existing features from LocalBackend,
but the conversion to a profile-state-change callback is intended to be permanent.
Finally, we let extensions retrieve the current prefs or profile state (profile info + prefs) at any time using the new
CurrentProfileState and CurrentPrefs methods. We also simplify the NewControlClientCallback signature to exclude
profile prefs. It’s optional, and extensions can retrieve the current prefs themselves if needed.
Updates #12614
Updates tailscale/corp#27645
Updates tailscale/corp#26435
Updates tailscale/corp#27502
Signed-off-by: Nick Khyl <nickk@tailscale.com>
updates tailscale/tailscale#13476
On darwin, os.Hostname is no longer reliable when called
from a sandboxed process. To fix this, we will allow clients
to set an optional callback to query the hostname via an
alternative native API.
We will leave the default implementation as os.Hostname since
this works perfectly well for almost everything besides sandboxed
darwin clients.
Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
In this PR, we add two methods to facilitate extension lookup by both extensions,
and non-extensions (e.g., PeerAPI or LocalAPI handlers):
- FindExtensionByName returns an extension with the specified name.
It can then be type asserted to a given type.
- FindMatchingExtension is like errors.As, but for extensions.
It returns the first extension that matches the target type (either a specific extension
or an interface).
Updates tailscale/corp#27645
Updates tailscale/corp#27502
Signed-off-by: Nick Khyl <nickk@tailscale.com>