Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+63
-37
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package ipn
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tailscale.com/atomicfile"
|
||||
"tailscale.com/drive"
|
||||
@@ -129,7 +130,8 @@ type Prefs struct {
|
||||
ExitNodeAllowLANAccess bool
|
||||
|
||||
// CorpDNS specifies whether to install the Tailscale network's
|
||||
// DNS configuration, if it exists.
|
||||
// DNS configuration, if it exists. It is the internal name for
|
||||
// the "tailscale set --accept-dns=" flag.
|
||||
CorpDNS bool
|
||||
|
||||
// RunSSH bool is whether this node should run an SSH
|
||||
@@ -222,7 +224,7 @@ type Prefs struct {
|
||||
// network to route Tailscale traffic back to the subnet relay
|
||||
// machine.
|
||||
//
|
||||
// Linux-only.
|
||||
// Linux and FreeBSD only.
|
||||
NoSNAT bool
|
||||
|
||||
// NoStatefulFiltering specifies whether to apply stateful filtering when
|
||||
@@ -276,6 +278,24 @@ type Prefs struct {
|
||||
// Linux-only.
|
||||
NetfilterKind string
|
||||
|
||||
// RemoteConfig, if true, delegates full remote control of this node's
|
||||
// prefs and LocalAPI to the tailnet admin via the control plane. When
|
||||
// enabled, the control server can read and edit any of this node's
|
||||
// prefs at any time, and invoke any LocalAPI endpoint on this node,
|
||||
// without any further local consent (no CLI or GUI confirmation).
|
||||
//
|
||||
// This is an alternative to Tailscale's default per-feature double
|
||||
// opt-in model, in which both the tailnet admin and the local machine
|
||||
// owner must agree to each individual setting change. RemoteConfig is
|
||||
// a single client-side "I trust the tailnet admin" switch that hands
|
||||
// over full remote management of this node.
|
||||
//
|
||||
// Only enable this when the tailnet admin owns the machine (e.g. a
|
||||
// corporate fleet device) or the local user has explicitly delegated
|
||||
// full control to the tailnet admin. Do NOT enable this on personal
|
||||
// or BYOD devices where the tailnet admin is not fully trusted.
|
||||
RemoteConfig bool
|
||||
|
||||
// DriveShares are the configured DriveShares, stored in increasing order
|
||||
// by name.
|
||||
DriveShares []*drive.Share
|
||||
@@ -291,16 +311,6 @@ type Prefs struct {
|
||||
// non-nil.
|
||||
RelayServerStaticEndpoints []netip.AddrPort `json:",omitempty"`
|
||||
|
||||
// AllowSingleHosts was a legacy field that was always true
|
||||
// for the past 4.5 years. It controlled whether Tailscale
|
||||
// peers got /32 or /128 routes for each other.
|
||||
// As of 2024-05-17 we're starting to ignore it, but to let
|
||||
// people still downgrade Tailscale versions and not break
|
||||
// all peer-to-peer networking we still write it to disk (as JSON)
|
||||
// so it can be loaded back by old versions.
|
||||
// TODO(bradfitz): delete this in 2025 sometime. See #12058.
|
||||
AllowSingleHosts marshalAsTrueInJSON
|
||||
|
||||
// The Persist field is named 'Config' in the file for backward
|
||||
// compatibility with earlier versions.
|
||||
// TODO(apenwarr): We should move this out of here, it's not a pref.
|
||||
@@ -331,13 +341,6 @@ func (au1 AutoUpdatePrefs) Equals(au2 AutoUpdatePrefs) bool {
|
||||
ok1 == ok2
|
||||
}
|
||||
|
||||
type marshalAsTrueInJSON struct{}
|
||||
|
||||
var trueJSON = []byte("true")
|
||||
|
||||
func (marshalAsTrueInJSON) MarshalJSON() ([]byte, error) { return trueJSON, nil }
|
||||
func (*marshalAsTrueInJSON) UnmarshalJSON([]byte) error { return nil }
|
||||
|
||||
// AppConnectorPrefs are the app connector settings for the node agent.
|
||||
type AppConnectorPrefs struct {
|
||||
// Advertise specifies whether the app connector subsystem is advertising
|
||||
@@ -383,6 +386,7 @@ type MaskedPrefs struct {
|
||||
AppConnectorSet bool `json:",omitempty"`
|
||||
PostureCheckingSet bool `json:",omitempty"`
|
||||
NetfilterKindSet bool `json:",omitempty"`
|
||||
RemoteConfigSet bool `json:",omitempty"`
|
||||
DriveSharesSet bool `json:",omitempty"`
|
||||
RelayServerPortSet bool `json:",omitempty"`
|
||||
RelayServerStaticEndpointsSet bool `json:",omitzero"`
|
||||
@@ -439,12 +443,11 @@ func applyPrefsEdits(src, dst reflect.Value, mask map[string]reflect.Value) {
|
||||
|
||||
func maskFields(v reflect.Value) map[string]reflect.Value {
|
||||
mask := make(map[string]reflect.Value)
|
||||
for i := range v.NumField() {
|
||||
f := v.Type().Field(i).Name
|
||||
if !strings.HasSuffix(f, "Set") {
|
||||
for sf, fv := range v.Fields() {
|
||||
if !strings.HasSuffix(sf.Name, "Set") {
|
||||
continue
|
||||
}
|
||||
mask[strings.TrimSuffix(f, "Set")] = v.Field(i)
|
||||
mask[strings.TrimSuffix(sf.Name, "Set")] = fv
|
||||
}
|
||||
return mask
|
||||
}
|
||||
@@ -570,6 +573,9 @@ func (p *Prefs) pretty(goos string) string {
|
||||
if p.ShieldsUp {
|
||||
sb.WriteString("shields=true ")
|
||||
}
|
||||
if p.RemoteConfig {
|
||||
sb.WriteString("remoteconfig=true ")
|
||||
}
|
||||
if buildfeatures.HasUseExitNode {
|
||||
if p.ExitNodeIP.IsValid() {
|
||||
fmt.Fprintf(&sb, "exit=%v lan=%t ", p.ExitNodeIP, p.ExitNodeAllowLANAccess)
|
||||
@@ -692,6 +698,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
|
||||
p.PostureChecking == p2.PostureChecking &&
|
||||
slices.EqualFunc(p.DriveShares, p2.DriveShares, drive.SharesEqual) &&
|
||||
p.NetfilterKind == p2.NetfilterKind &&
|
||||
p.RemoteConfig == p2.RemoteConfig &&
|
||||
compareUint16Ptrs(p.RelayServerPort, p2.RelayServerPort) &&
|
||||
slices.Equal(p.RelayServerStaticEndpoints, p2.RelayServerStaticEndpoints)
|
||||
}
|
||||
@@ -845,22 +852,15 @@ func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
|
||||
// Tailscale IP.
|
||||
func peerWithTailscaleIP(st *ipnstate.Status, ip netip.Addr) (ps *ipnstate.PeerStatus, ok bool) {
|
||||
for _, ps := range st.Peer {
|
||||
for _, ip2 := range ps.TailscaleIPs {
|
||||
if ip == ip2 {
|
||||
return ps, true
|
||||
}
|
||||
if slices.Contains(ps.TailscaleIPs, ip) {
|
||||
return ps, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func isRemoteIP(st *ipnstate.Status, ip netip.Addr) bool {
|
||||
for _, selfIP := range st.TailscaleIPs {
|
||||
if ip == selfIP {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return !slices.Contains(st.TailscaleIPs, ip)
|
||||
}
|
||||
|
||||
// ClearExitNode sets the ExitNodeID and ExitNodeIP to their zero values.
|
||||
@@ -880,10 +880,14 @@ func (e ExitNodeLocalIPError) Error() string {
|
||||
return fmt.Sprintf("cannot use %s as an exit node as it is a local IP address to this machine", e.hostOrIP)
|
||||
}
|
||||
|
||||
// exitNodeIPOfArg returns the IP address of the exit node based on the
|
||||
// user-provided string.
|
||||
func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netip.Addr, err error) {
|
||||
if s == "" {
|
||||
return ip, os.ErrInvalid
|
||||
}
|
||||
|
||||
// If the string is a valid IP address, that's the exit node.
|
||||
ip, err = netip.ParseAddr(s)
|
||||
if err == nil {
|
||||
if !isRemoteIP(st, ip) {
|
||||
@@ -902,10 +906,26 @@ func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netip.Addr, err error) {
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// If the string is not a valid IP address, assume it's a hostname.
|
||||
// Search the list of peers for a matching hostname.
|
||||
if len(st.Peer) == 0 {
|
||||
return ip, errors.New("cannot resolve exit node by hostname while Tailscale is starting up; " +
|
||||
"please use its Tailscale IP address instead")
|
||||
}
|
||||
match := 0
|
||||
for _, ps := range st.Peer {
|
||||
baseName := dnsname.TrimSuffix(ps.DNSName, st.MagicDNSSuffix)
|
||||
if !strings.EqualFold(s, baseName) && !strings.EqualFold(s, ps.DNSName) {
|
||||
// Compare to the peer name in three forms:
|
||||
//
|
||||
// - base name ("example")
|
||||
// - FQDN ("example.tail1234.ts.net.")
|
||||
// - FQDN sans dot ("example.tail1234.ts.net", as returned by `tailscale exit-node list`
|
||||
// and the admin console)
|
||||
//
|
||||
fqdn := ps.DNSName
|
||||
baseName := dnsname.TrimSuffix(fqdn, st.MagicDNSSuffix)
|
||||
fqdnSansDot := dnsname.TrimSuffix(fqdn, ".")
|
||||
if !strings.EqualFold(s, baseName) && !strings.EqualFold(s, fqdn) && !strings.EqualFold(s, fqdnSansDot) {
|
||||
continue
|
||||
}
|
||||
match++
|
||||
@@ -919,7 +939,7 @@ func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netip.Addr, err error) {
|
||||
}
|
||||
switch match {
|
||||
case 0:
|
||||
return ip, fmt.Errorf("invalid value %q for --exit-node; must be IP or unique node name", s)
|
||||
return ip, fmt.Errorf("invalid value %q for --exit-node; must be IP or peer hostname", s)
|
||||
case 1:
|
||||
if !isRemoteIP(st, ip) {
|
||||
return ip, ExitNodeLocalIPError{s}
|
||||
@@ -1096,6 +1116,12 @@ type LoginProfile struct {
|
||||
// ControlURL is the URL of the control server that this profile is logged
|
||||
// into.
|
||||
ControlURL string
|
||||
|
||||
// Created is when this profile was first added to this client. It is
|
||||
// stamped once at profile creation and never changes. It is used to sort
|
||||
// the profile list with newest first; profiles created before this field
|
||||
// existed have a zero value and sort after all stamped profiles.
|
||||
Created time.Time `json:",omitzero"`
|
||||
}
|
||||
|
||||
// Equals reports whether p and p2 are equal.
|
||||
|
||||
Reference in new issue
Block a user