mirror of
https://github.com/tailscale/tailscale.git
synced 2025-07-29 15:23:45 +00:00
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>
This commit is contained in:
parent
cc8dc9e4dc
commit
db34cdcfe7
@ -788,6 +788,25 @@ func (lc *Client) CheckUDPGROForwarding(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckReversePathFiltering asks the local Tailscale daemon whether strict
|
||||||
|
// reverse path filtering is enabled, which would break exit node usage on Linux.
|
||||||
|
func (lc *Client) CheckReversePathFiltering(ctx context.Context) error {
|
||||||
|
body, err := lc.get200(ctx, "/localapi/v0/check-reverse-path-filtering")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var jres struct {
|
||||||
|
Warning string
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &jres); err != nil {
|
||||||
|
return fmt.Errorf("invalid JSON from check-reverse-path-filtering: %w", err)
|
||||||
|
}
|
||||||
|
if jres.Warning != "" {
|
||||||
|
return errors.New(jres.Warning)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetUDPGROForwarding enables UDP GRO forwarding for the main interface of this
|
// SetUDPGROForwarding enables UDP GRO forwarding for the main interface of this
|
||||||
// node. This can be done to improve performance of tailnet nodes acting as exit
|
// node. This can be done to improve performance of tailnet nodes acting as exit
|
||||||
// nodes or subnet routers.
|
// nodes or subnet routers.
|
||||||
|
@ -796,7 +796,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
|||||||
tailscale.com/envknob/featureknob from tailscale.com/client/web+
|
tailscale.com/envknob/featureknob from tailscale.com/client/web+
|
||||||
tailscale.com/feature from tailscale.com/ipn/ipnext+
|
tailscale.com/feature from tailscale.com/ipn/ipnext+
|
||||||
tailscale.com/health from tailscale.com/control/controlclient+
|
tailscale.com/health from tailscale.com/control/controlclient+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
tailscale.com/internal/client/tailscale from tailscale.com/cmd/k8s-operator
|
tailscale.com/internal/client/tailscale from tailscale.com/cmd/k8s-operator
|
||||||
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
||||||
|
@ -4,15 +4,18 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,11 +23,12 @@ var (
|
|||||||
riskTypes []string
|
riskTypes []string
|
||||||
riskLoseSSH = registerRiskType("lose-ssh")
|
riskLoseSSH = registerRiskType("lose-ssh")
|
||||||
riskMacAppConnector = registerRiskType("mac-app-connector")
|
riskMacAppConnector = registerRiskType("mac-app-connector")
|
||||||
|
riskStrictRPFilter = registerRiskType("linux-strict-rp-filter")
|
||||||
riskAll = registerRiskType("all")
|
riskAll = registerRiskType("all")
|
||||||
)
|
)
|
||||||
|
|
||||||
const riskMacAppConnectorMessage = `
|
const riskMacAppConnectorMessage = `
|
||||||
You are trying to configure an app connector on macOS, which is not officially supported due to system limitations. This may result in performance and reliability issues.
|
You are trying to configure an app connector on macOS, which is not officially supported due to system limitations. This may result in performance and reliability issues.
|
||||||
|
|
||||||
Do not use a macOS app connector for any mission-critical purposes. For the best experience, Linux is the only recommended platform for app connectors.
|
Do not use a macOS app connector for any mission-critical purposes. For the best experience, Linux is the only recommended platform for app connectors.
|
||||||
`
|
`
|
||||||
@ -89,3 +93,18 @@ func presentRiskToUser(riskType, riskMessage, acceptedRisks string) error {
|
|||||||
printf("\r%s\r", strings.Repeat(" ", msgLen))
|
printf("\r%s\r", strings.Repeat(" ", msgLen))
|
||||||
return errAborted
|
return errAborted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkExitNodeRisk checks if the user is using an exit node on Linux and
|
||||||
|
// whether reverse path filtering is enabled. If so, it presents a risk message.
|
||||||
|
func checkExitNodeRisk(ctx context.Context, prefs *ipn.Prefs, acceptedRisks string) error {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !prefs.ExitNodeIP.IsValid() && prefs.ExitNodeID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := localClient.CheckReversePathFiltering(ctx); err != nil {
|
||||||
|
return presentRiskToUser(riskStrictRPFilter, err.Error(), acceptedRisks)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -183,6 +183,9 @@ func runSet(ctx context.Context, args []string) (retErr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
warnOnAdvertiseRouts(ctx, &maskedPrefs.Prefs)
|
warnOnAdvertiseRouts(ctx, &maskedPrefs.Prefs)
|
||||||
|
if err := checkExitNodeRisk(ctx, &maskedPrefs.Prefs, setArgs.acceptedRisks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var advertiseExitNodeSet, advertiseRoutesSet bool
|
var advertiseExitNodeSet, advertiseRoutesSet bool
|
||||||
setFlagSet.Visit(func(f *flag.Flag) {
|
setFlagSet.Visit(func(f *flag.Flag) {
|
||||||
updateMaskedPrefsFromUpOrSetFlag(maskedPrefs, f.Name)
|
updateMaskedPrefsFromUpOrSetFlag(maskedPrefs, f.Name)
|
||||||
|
@ -481,6 +481,9 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE
|
|||||||
}
|
}
|
||||||
|
|
||||||
warnOnAdvertiseRouts(ctx, prefs)
|
warnOnAdvertiseRouts(ctx, prefs)
|
||||||
|
if err := checkExitNodeRisk(ctx, prefs, upArgs.acceptedRisks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
curPrefs, err := localClient.GetPrefs(ctx)
|
curPrefs, err := localClient.GetPrefs(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -281,7 +281,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/feature/tpm from tailscale.com/feature/condregister
|
tailscale.com/feature/tpm from tailscale.com/feature/condregister
|
||||||
tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister
|
tailscale.com/feature/wakeonlan from tailscale.com/feature/condregister
|
||||||
tailscale.com/health from tailscale.com/control/controlclient+
|
tailscale.com/health from tailscale.com/control/controlclient+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
||||||
tailscale.com/ipn from tailscale.com/client/local+
|
tailscale.com/ipn from tailscale.com/client/local+
|
||||||
|
@ -12,4 +12,5 @@ const (
|
|||||||
TailscaleSSHOnBut = "Tailscale SSH enabled, but " // + ... something from caller
|
TailscaleSSHOnBut = "Tailscale SSH enabled, but " // + ... something from caller
|
||||||
LockedOut = "this node is locked out; it will not have connectivity until it is signed. For more info, see https://tailscale.com/s/locked-out"
|
LockedOut = "this node is locked out; it will not have connectivity until it is signed. For more info, see https://tailscale.com/s/locked-out"
|
||||||
WarnExitNodeUsage = "The following issues on your machine will likely make usage of exit nodes impossible"
|
WarnExitNodeUsage = "The following issues on your machine will likely make usage of exit nodes impossible"
|
||||||
|
DisableRPFilter = "Please set rp_filter=2 instead of rp_filter=1; see https://github.com/tailscale/tailscale/issues/3310"
|
||||||
)
|
)
|
||||||
|
@ -4112,9 +4112,8 @@ func updateExitNodeUsageWarning(p ipn.PrefsView, state *netmon.State, healthTrac
|
|||||||
var msg string
|
var msg string
|
||||||
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
if p.ExitNodeIP().IsValid() || p.ExitNodeID() != "" {
|
||||||
warn, _ := netutil.CheckReversePathFiltering(state)
|
warn, _ := netutil.CheckReversePathFiltering(state)
|
||||||
const comment = "please set rp_filter=2 instead of rp_filter=1; see https://github.com/tailscale/tailscale/issues/3310"
|
|
||||||
if len(warn) > 0 {
|
if len(warn) > 0 {
|
||||||
msg = fmt.Sprintf("%s: %v, %s", healthmsg.WarnExitNodeUsage, warn, comment)
|
msg = fmt.Sprintf("%s: %v, %s", healthmsg.WarnExitNodeUsage, warn, healthmsg.DisableRPFilter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(msg) > 0 {
|
if len(msg) > 0 {
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"tailscale.com/clientupdate"
|
"tailscale.com/clientupdate"
|
||||||
"tailscale.com/drive"
|
"tailscale.com/drive"
|
||||||
"tailscale.com/envknob"
|
"tailscale.com/envknob"
|
||||||
|
"tailscale.com/health/healthmsg"
|
||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/ipn/ipnauth"
|
"tailscale.com/ipn/ipnauth"
|
||||||
@ -82,71 +83,72 @@ var handler = map[string]LocalAPIHandler{
|
|||||||
|
|
||||||
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
// The other /localapi/v0/NAME handlers are exact matches and contain only NAME
|
||||||
// without a trailing slash:
|
// without a trailing slash:
|
||||||
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
|
"alpha-set-device-attrs": (*Handler).serveSetDeviceAttrs, // see tailscale/corp#24690
|
||||||
"bugreport": (*Handler).serveBugReport,
|
"bugreport": (*Handler).serveBugReport,
|
||||||
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
|
"check-ip-forwarding": (*Handler).serveCheckIPForwarding,
|
||||||
"check-prefs": (*Handler).serveCheckPrefs,
|
"check-prefs": (*Handler).serveCheckPrefs,
|
||||||
"check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
|
"check-reverse-path-filtering": (*Handler).serveCheckReversePathFiltering,
|
||||||
"component-debug-logging": (*Handler).serveComponentDebugLogging,
|
"check-udp-gro-forwarding": (*Handler).serveCheckUDPGROForwarding,
|
||||||
"debug": (*Handler).serveDebug,
|
"component-debug-logging": (*Handler).serveComponentDebugLogging,
|
||||||
"debug-derp-region": (*Handler).serveDebugDERPRegion,
|
"debug": (*Handler).serveDebug,
|
||||||
"debug-dial-types": (*Handler).serveDebugDialTypes,
|
"debug-derp-region": (*Handler).serveDebugDERPRegion,
|
||||||
"debug-log": (*Handler).serveDebugLog,
|
"debug-dial-types": (*Handler).serveDebugDialTypes,
|
||||||
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
|
"debug-log": (*Handler).serveDebugLog,
|
||||||
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
|
"debug-packet-filter-matches": (*Handler).serveDebugPacketFilterMatches,
|
||||||
"debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges,
|
"debug-packet-filter-rules": (*Handler).serveDebugPacketFilterRules,
|
||||||
"debug-portmap": (*Handler).serveDebugPortmap,
|
"debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges,
|
||||||
"derpmap": (*Handler).serveDERPMap,
|
"debug-portmap": (*Handler).serveDebugPortmap,
|
||||||
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
"derpmap": (*Handler).serveDERPMap,
|
||||||
"dial": (*Handler).serveDial,
|
"dev-set-state-store": (*Handler).serveDevSetStateStore,
|
||||||
"disconnect-control": (*Handler).disconnectControl,
|
"dial": (*Handler).serveDial,
|
||||||
"dns-osconfig": (*Handler).serveDNSOSConfig,
|
"disconnect-control": (*Handler).disconnectControl,
|
||||||
"dns-query": (*Handler).serveDNSQuery,
|
"dns-osconfig": (*Handler).serveDNSOSConfig,
|
||||||
"drive/fileserver-address": (*Handler).serveDriveServerAddr,
|
"dns-query": (*Handler).serveDNSQuery,
|
||||||
"drive/shares": (*Handler).serveShares,
|
"drive/fileserver-address": (*Handler).serveDriveServerAddr,
|
||||||
"goroutines": (*Handler).serveGoroutines,
|
"drive/shares": (*Handler).serveShares,
|
||||||
"handle-push-message": (*Handler).serveHandlePushMessage,
|
"goroutines": (*Handler).serveGoroutines,
|
||||||
"id-token": (*Handler).serveIDToken,
|
"handle-push-message": (*Handler).serveHandlePushMessage,
|
||||||
"login-interactive": (*Handler).serveLoginInteractive,
|
"id-token": (*Handler).serveIDToken,
|
||||||
"logout": (*Handler).serveLogout,
|
"login-interactive": (*Handler).serveLoginInteractive,
|
||||||
"logtap": (*Handler).serveLogTap,
|
"logout": (*Handler).serveLogout,
|
||||||
"metrics": (*Handler).serveMetrics,
|
"logtap": (*Handler).serveLogTap,
|
||||||
"ping": (*Handler).servePing,
|
"metrics": (*Handler).serveMetrics,
|
||||||
"pprof": (*Handler).servePprof,
|
"ping": (*Handler).servePing,
|
||||||
"prefs": (*Handler).servePrefs,
|
"pprof": (*Handler).servePprof,
|
||||||
"query-feature": (*Handler).serveQueryFeature,
|
"prefs": (*Handler).servePrefs,
|
||||||
"reload-config": (*Handler).reloadConfig,
|
"query-feature": (*Handler).serveQueryFeature,
|
||||||
"reset-auth": (*Handler).serveResetAuth,
|
"reload-config": (*Handler).reloadConfig,
|
||||||
"serve-config": (*Handler).serveServeConfig,
|
"reset-auth": (*Handler).serveResetAuth,
|
||||||
"set-dns": (*Handler).serveSetDNS,
|
"serve-config": (*Handler).serveServeConfig,
|
||||||
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
"set-dns": (*Handler).serveSetDNS,
|
||||||
"set-gui-visible": (*Handler).serveSetGUIVisible,
|
"set-expiry-sooner": (*Handler).serveSetExpirySooner,
|
||||||
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
"set-gui-visible": (*Handler).serveSetGUIVisible,
|
||||||
"set-udp-gro-forwarding": (*Handler).serveSetUDPGROForwarding,
|
"set-push-device-token": (*Handler).serveSetPushDeviceToken,
|
||||||
"set-use-exit-node-enabled": (*Handler).serveSetUseExitNodeEnabled,
|
"set-udp-gro-forwarding": (*Handler).serveSetUDPGROForwarding,
|
||||||
"start": (*Handler).serveStart,
|
"set-use-exit-node-enabled": (*Handler).serveSetUseExitNodeEnabled,
|
||||||
"status": (*Handler).serveStatus,
|
"start": (*Handler).serveStart,
|
||||||
"suggest-exit-node": (*Handler).serveSuggestExitNode,
|
"status": (*Handler).serveStatus,
|
||||||
"tka/affected-sigs": (*Handler).serveTKAAffectedSigs,
|
"suggest-exit-node": (*Handler).serveSuggestExitNode,
|
||||||
"tka/cosign-recovery-aum": (*Handler).serveTKACosignRecoveryAUM,
|
"tka/affected-sigs": (*Handler).serveTKAAffectedSigs,
|
||||||
"tka/disable": (*Handler).serveTKADisable,
|
"tka/cosign-recovery-aum": (*Handler).serveTKACosignRecoveryAUM,
|
||||||
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
|
"tka/disable": (*Handler).serveTKADisable,
|
||||||
"tka/generate-recovery-aum": (*Handler).serveTKAGenerateRecoveryAUM,
|
"tka/force-local-disable": (*Handler).serveTKALocalDisable,
|
||||||
"tka/init": (*Handler).serveTKAInit,
|
"tka/generate-recovery-aum": (*Handler).serveTKAGenerateRecoveryAUM,
|
||||||
"tka/log": (*Handler).serveTKALog,
|
"tka/init": (*Handler).serveTKAInit,
|
||||||
"tka/modify": (*Handler).serveTKAModify,
|
"tka/log": (*Handler).serveTKALog,
|
||||||
"tka/sign": (*Handler).serveTKASign,
|
"tka/modify": (*Handler).serveTKAModify,
|
||||||
"tka/status": (*Handler).serveTKAStatus,
|
"tka/sign": (*Handler).serveTKASign,
|
||||||
"tka/submit-recovery-aum": (*Handler).serveTKASubmitRecoveryAUM,
|
"tka/status": (*Handler).serveTKAStatus,
|
||||||
"tka/verify-deeplink": (*Handler).serveTKAVerifySigningDeeplink,
|
"tka/submit-recovery-aum": (*Handler).serveTKASubmitRecoveryAUM,
|
||||||
"tka/wrap-preauth-key": (*Handler).serveTKAWrapPreauthKey,
|
"tka/verify-deeplink": (*Handler).serveTKAVerifySigningDeeplink,
|
||||||
"update/check": (*Handler).serveUpdateCheck,
|
"tka/wrap-preauth-key": (*Handler).serveTKAWrapPreauthKey,
|
||||||
"update/install": (*Handler).serveUpdateInstall,
|
"update/check": (*Handler).serveUpdateCheck,
|
||||||
"update/progress": (*Handler).serveUpdateProgress,
|
"update/install": (*Handler).serveUpdateInstall,
|
||||||
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
"update/progress": (*Handler).serveUpdateProgress,
|
||||||
"usermetrics": (*Handler).serveUserMetrics,
|
"upload-client-metrics": (*Handler).serveUploadClientMetrics,
|
||||||
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
"usermetrics": (*Handler).serveUserMetrics,
|
||||||
"whois": (*Handler).serveWhoIs,
|
"watch-ipn-bus": (*Handler).serveWatchIPNBus,
|
||||||
|
"whois": (*Handler).serveWhoIs,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register registers a new LocalAPI handler for the given name.
|
// Register registers a new LocalAPI handler for the given name.
|
||||||
@ -1175,6 +1177,32 @@ func (h *Handler) serveCheckIPForwarding(w http.ResponseWriter, r *http.Request)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) serveCheckReversePathFiltering(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !h.PermitRead {
|
||||||
|
http.Error(w, "reverse path filtering check access denied", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var warning string
|
||||||
|
|
||||||
|
state := h.b.Sys().NetMon.Get().InterfaceState()
|
||||||
|
warn, err := netutil.CheckReversePathFiltering(state)
|
||||||
|
if err == nil && len(warn) > 0 {
|
||||||
|
var msg strings.Builder
|
||||||
|
msg.WriteString(healthmsg.WarnExitNodeUsage + ":\n")
|
||||||
|
for _, w := range warn {
|
||||||
|
msg.WriteString("- " + w + "\n")
|
||||||
|
}
|
||||||
|
msg.WriteString(healthmsg.DisableRPFilter)
|
||||||
|
warning = msg.String()
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(struct {
|
||||||
|
Warning string
|
||||||
|
}{
|
||||||
|
Warning: warning,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) serveCheckUDPGROForwarding(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) serveCheckUDPGROForwarding(w http.ResponseWriter, r *http.Request) {
|
||||||
if !h.PermitRead {
|
if !h.PermitRead {
|
||||||
http.Error(w, "UDP GRO forwarding check access denied", http.StatusForbidden)
|
http.Error(w, "UDP GRO forwarding check access denied", http.StatusForbidden)
|
||||||
|
@ -237,7 +237,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
|||||||
tailscale.com/envknob/featureknob from tailscale.com/client/web+
|
tailscale.com/envknob/featureknob from tailscale.com/client/web+
|
||||||
tailscale.com/feature from tailscale.com/ipn/ipnext+
|
tailscale.com/feature from tailscale.com/ipn/ipnext+
|
||||||
tailscale.com/health from tailscale.com/control/controlclient+
|
tailscale.com/health from tailscale.com/control/controlclient+
|
||||||
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal
|
tailscale.com/health/healthmsg from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/hostinfo from tailscale.com/client/web+
|
tailscale.com/hostinfo from tailscale.com/client/web+
|
||||||
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
tailscale.com/internal/noiseconn from tailscale.com/control/controlclient
|
||||||
tailscale.com/ipn from tailscale.com/client/local+
|
tailscale.com/ipn from tailscale.com/client/local+
|
||||||
|
Loading…
x
Reference in New Issue
Block a user