cmd/tailscale/cli: configure a static endpoint flag on tailscale set

Fixes #13462

Signed-off-by: Lee Briggs <lee@leebriggs.co.uk>
jaxxstorm/static_endpoints
Lee Briggs 1 month ago
parent 77123a569b
commit 9e2df8093f
No known key found for this signature in database
GPG Key ID: A4D09B96FDFEB505

@ -65,6 +65,7 @@ type setArgsT struct {
statefulFiltering bool statefulFiltering bool
netfilterMode string netfilterMode string
relayServerPort string relayServerPort string
staticEndpoints string
} }
func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet { func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet {
@ -86,6 +87,7 @@ func newSetFlagSet(goos string, setArgs *setArgsT) *flag.FlagSet {
setf.BoolVar(&setArgs.reportPosture, "report-posture", false, "allow management plane to gather device posture information") setf.BoolVar(&setArgs.reportPosture, "report-posture", false, "allow management plane to gather device posture information")
setf.BoolVar(&setArgs.runWebClient, "webclient", false, "expose the web interface for managing this node over Tailscale at port 5252") setf.BoolVar(&setArgs.runWebClient, "webclient", false, "expose the web interface for managing this node over Tailscale at port 5252")
setf.StringVar(&setArgs.relayServerPort, "relay-server-port", "", "UDP port number (0 will pick a random unused port) for the relay server to bind to, on all interfaces, or empty string to disable relay server functionality") setf.StringVar(&setArgs.relayServerPort, "relay-server-port", "", "UDP port number (0 will pick a random unused port) for the relay server to bind to, on all interfaces, or empty string to disable relay server functionality")
setf.StringVar(&setArgs.staticEndpoints, "endpoint", "", "static endpoint(s) to advertise (comma-separated, e.g. \"192.168.1.100:41641,203.0.113.1:41641\")")
ffcomplete.Flag(setf, "exit-node", func(args []string) ([]string, ffcomplete.ShellCompDirective, error) { ffcomplete.Flag(setf, "exit-node", func(args []string) ([]string, ffcomplete.ShellCompDirective, error) {
st, err := localClient.Status(context.Background()) st, err := localClient.Status(context.Background())
@ -185,6 +187,26 @@ func runSet(ctx context.Context, args []string) (retErr error) {
} }
} }
if setArgs.staticEndpoints != "" {
const max = 10 // reasonable limit for static endpoints
remain := setArgs.staticEndpoints
var endpoints []netip.AddrPort
for remain != "" && len(endpoints) < max {
var s string
s, remain, _ = strings.Cut(remain, ",")
s = strings.TrimSpace(s)
if s == "" {
continue
}
ap, err := netip.ParseAddrPort(s)
if err != nil {
return fmt.Errorf("invalid endpoint %q: %v", s, err)
}
endpoints = append(endpoints, ap)
}
maskedPrefs.Prefs.StaticEndpoints = endpoints
}
warnOnAdvertiseRoutes(ctx, &maskedPrefs.Prefs) warnOnAdvertiseRoutes(ctx, &maskedPrefs.Prefs)
if err := checkExitNodeRisk(ctx, &maskedPrefs.Prefs, setArgs.acceptedRisks); err != nil { if err := checkExitNodeRisk(ctx, &maskedPrefs.Prefs, setArgs.acceptedRisks); err != nil {
return err return err

@ -889,6 +889,7 @@ func init() {
addPrefFlagMapping("advertise-connector", "AppConnector") addPrefFlagMapping("advertise-connector", "AppConnector")
addPrefFlagMapping("report-posture", "PostureChecking") addPrefFlagMapping("report-posture", "PostureChecking")
addPrefFlagMapping("relay-server-port", "RelayServerPort") addPrefFlagMapping("relay-server-port", "RelayServerPort")
addPrefFlagMapping("endpoint", "StaticEndpoints")
} }
func addPrefFlagMapping(flagName string, prefNames ...string) { func addPrefFlagMapping(flagName string, prefNames ...string) {

@ -64,6 +64,7 @@ func (src *Prefs) Clone() *Prefs {
if dst.RelayServerPort != nil { if dst.RelayServerPort != nil {
dst.RelayServerPort = ptr.To(*src.RelayServerPort) dst.RelayServerPort = ptr.To(*src.RelayServerPort)
} }
dst.StaticEndpoints = append(src.StaticEndpoints[:0:0], src.StaticEndpoints...)
dst.Persist = src.Persist.Clone() dst.Persist = src.Persist.Clone()
return dst return dst
} }
@ -101,6 +102,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
NetfilterKind string NetfilterKind string
DriveShares []*drive.Share DriveShares []*drive.Share
RelayServerPort *int RelayServerPort *int
StaticEndpoints []netip.AddrPort
AllowSingleHosts marshalAsTrueInJSON AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -442,6 +442,12 @@ func (v PrefsView) RelayServerPort() views.ValuePointer[int] {
return views.ValuePointerOf(v.ж.RelayServerPort) return views.ValuePointerOf(v.ж.RelayServerPort)
} }
// StaticEndpoints are additional, user-defined endpoints that this node
// should advertise amongst its wireguard endpoints.
func (v PrefsView) StaticEndpoints() views.Slice[netip.AddrPort] {
return views.SliceOf(v.ж.StaticEndpoints)
}
// AllowSingleHosts was a legacy field that was always true // AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale // for the past 4.5 years. It controlled whether Tailscale
// peers got /32 or /127 routes for each other. // peers got /32 or /127 routes for each other.
@ -493,6 +499,7 @@ var _PrefsViewNeedsRegeneration = Prefs(struct {
NetfilterKind string NetfilterKind string
DriveShares []*drive.Share DriveShares []*drive.Share
RelayServerPort *int RelayServerPort *int
StaticEndpoints []netip.AddrPort
AllowSingleHosts marshalAsTrueInJSON AllowSingleHosts marshalAsTrueInJSON
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -4656,6 +4656,13 @@ func (b *LocalBackend) setPrefsLockedOnEntry(newp *ipn.Prefs, unlock unlockOnce)
b.resetAlwaysOnOverrideLocked() b.resetAlwaysOnOverrideLocked()
} }
// Update static endpoints if they've changed
if !oldp.Valid() || !slices.Equal(oldp.StaticEndpoints().AsSlice(), prefs.StaticEndpoints().AsSlice()) {
if ms, ok := b.sys.MagicSock.GetOK(); ok {
ms.SetStaticEndpoints(prefs.StaticEndpoints())
}
}
unlock.UnlockEarly() unlock.UnlockEarly()
if oldp.ShieldsUp() != newp.ShieldsUp || hostInfoChanged { if oldp.ShieldsUp() != newp.ShieldsUp || hostInfoChanged {

@ -282,6 +282,10 @@ type Prefs struct {
// non-nil/enabled. // non-nil/enabled.
RelayServerPort *int `json:",omitempty"` RelayServerPort *int `json:",omitempty"`
// StaticEndpoints are additional, user-defined endpoints that this node
// should advertise amongst its wireguard endpoints.
StaticEndpoints []netip.AddrPort `json:",omitempty"`
// AllowSingleHosts was a legacy field that was always true // AllowSingleHosts was a legacy field that was always true
// for the past 4.5 years. It controlled whether Tailscale // for the past 4.5 years. It controlled whether Tailscale
// peers got /32 or /127 routes for each other. // peers got /32 or /127 routes for each other.
@ -375,6 +379,7 @@ type MaskedPrefs struct {
NetfilterKindSet bool `json:",omitempty"` NetfilterKindSet bool `json:",omitempty"`
DriveSharesSet bool `json:",omitempty"` DriveSharesSet bool `json:",omitempty"`
RelayServerPortSet bool `json:",omitempty"` RelayServerPortSet bool `json:",omitempty"`
StaticEndpointsSet bool `json:",omitempty"`
} }
// SetsInternal reports whether mp has any of the Internal*Set field bools set // SetsInternal reports whether mp has any of the Internal*Set field bools set

Loading…
Cancel
Save