Skip to content

Commit

Permalink
ipn/ipnlocal: enable web client over 100.100.100.100 by default
Browse files Browse the repository at this point in the history
Enable the web client over 100.100.100.100 by default. Accepting traffic
from [tailnet IP]:5252 still requires setting the `webclient` user pref.

Updates #10261

Signed-off-by: Mario Minardi <mario@tailscale.com>
  • Loading branch information
mpminardi committed Mar 14, 2024
1 parent bdc6712 commit 60f5015
Showing 1 changed file with 53 additions and 34 deletions.
87 changes: 53 additions & 34 deletions ipn/ipnlocal/local.go
Expand Up @@ -156,30 +156,31 @@ type watchSession struct {
// state machine generates events back out to zero or more components.
type LocalBackend struct {
// Elements that are thread-safe or constant after construction.
ctx context.Context // canceled by Close
ctxCancel context.CancelFunc // cancels ctx
logf logger.Logf // general logging
keyLogf logger.Logf // for printing list of peers on change
statsLogf logger.Logf // for printing peers stats on change
sys *tsd.System
e wgengine.Engine // non-nil; TODO(bradfitz): remove; use sys
store ipn.StateStore // non-nil; TODO(bradfitz): remove; use sys
dialer *tsdial.Dialer // non-nil; TODO(bradfitz): remove; use sys
pushDeviceToken syncs.AtomicValue[string]
backendLogID logid.PublicID
unregisterNetMon func()
unregisterHealthWatch func()
portpoll *portlist.Poller // may be nil
portpollOnce sync.Once // guards starting readPoller
gotPortPollRes chan struct{} // closed upon first readPoller result
varRoot string // or empty if SetVarRoot never called
logFlushFunc func() // or nil if SetLogFlusher wasn't called
em *expiryManager // non-nil
sshAtomicBool atomic.Bool
webClientAtomicBool atomic.Bool
shutdownCalled bool // if Shutdown has been called
debugSink *capture.Sink
sockstatLogger *sockstatlog.Logger
ctx context.Context // canceled by Close
ctxCancel context.CancelFunc // cancels ctx
logf logger.Logf // general logging
keyLogf logger.Logf // for printing list of peers on change
statsLogf logger.Logf // for printing peers stats on change
sys *tsd.System
e wgengine.Engine // non-nil; TODO(bradfitz): remove; use sys
store ipn.StateStore // non-nil; TODO(bradfitz): remove; use sys
dialer *tsdial.Dialer // non-nil; TODO(bradfitz): remove; use sys
pushDeviceToken syncs.AtomicValue[string]
backendLogID logid.PublicID
unregisterNetMon func()
unregisterHealthWatch func()
portpoll *portlist.Poller // may be nil
portpollOnce sync.Once // guards starting readPoller
gotPortPollRes chan struct{} // closed upon first readPoller result
varRoot string // or empty if SetVarRoot never called
logFlushFunc func() // or nil if SetLogFlusher wasn't called
em *expiryManager // non-nil
sshAtomicBool atomic.Bool
webClientAtomicBool atomic.Bool
exposeWebClientAtomicBool atomic.Bool
shutdownCalled bool // if Shutdown has been called
debugSink *capture.Sink
sockstatLogger *sockstatlog.Logger

// getTCPHandlerForFunnelFlow returns a handler for an incoming TCP flow for
// the provided srcAddr and dstPort if one exists.
Expand Down Expand Up @@ -1160,7 +1161,7 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
// Perform all reconfiguration based on the netmap here.
if st.NetMap != nil {
b.capTailnetLock = hasCapability(st.NetMap, tailcfg.CapabilityTailnetLock)
b.setWebClientAtomicBoolLocked(st.NetMap, prefs.View())
b.setWebClientAtomicBoolLocked(st.NetMap)

b.mu.Unlock() // respect locking rules for tkaSyncIfNeeded
if err := b.tkaSyncIfNeeded(st.NetMap, prefs.View()); err != nil {
Expand Down Expand Up @@ -2719,11 +2720,12 @@ func (b *LocalBackend) setTCPPortsIntercepted(ports []uint16) {
b.shouldInterceptTCPPortAtomic.Store(f)
}

// setAtomicValuesFromPrefsLocked populates sshAtomicBool, containsViaIPFuncAtomic
// and shouldInterceptTCPPortAtomic from the prefs p, which may be !Valid().
// setAtomicValuesFromPrefsLocked populates sshAtomicBool, containsViaIPFuncAtomic,
// shouldInterceptTCPPortAtomic, and exposeWebClientAtomicBool from the prefs p,
// which may be !Valid().
func (b *LocalBackend) setAtomicValuesFromPrefsLocked(p ipn.PrefsView) {
b.sshAtomicBool.Store(p.Valid() && p.RunSSH() && envknob.CanSSHD())
b.setWebClientAtomicBoolLocked(b.netMap, p)
b.setExposeWebClientAtomicBoolLocked(p)

if !p.Valid() {
b.containsViaIPFuncAtomic.Store(tsaddr.FalseContainsIPFunc())
Expand Down Expand Up @@ -3356,6 +3358,8 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
if hittingServiceIP {
switch dst.Port() {
case 80:
// TODO(mpminardi): do we want to show an error message if the web client
// has been disabled instead of the more "basic" web UI?
if b.ShouldRunWebClient() {
return b.handleWebClientConn, opts
}
Expand All @@ -3380,7 +3384,7 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
return b.handleSSHConn, opts
}
// TODO(will,sonia): allow customizing web client port ?
if dst.Port() == webClientPort && b.ShouldRunWebClient() {
if dst.Port() == webClientPort && b.ShouldExposeWebClient() {
return b.handleWebClientConn, opts
}
if port, ok := b.GetPeerAPIPort(dst.Addr()); ok && dst.Port() == port {
Expand Down Expand Up @@ -4508,19 +4512,34 @@ func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Load() && en
// call regardless of whether b.mu is held or not.
func (b *LocalBackend) ShouldRunWebClient() bool { return b.webClientAtomicBool.Load() }

// ShouldExposeWebClient reports whether the web client should accept
// connections via [tailscale IP]:5252 in addition to the default
// behaviour of accepting local connections over 100.100.100.100.
func (b *LocalBackend) ShouldExposeWebClient() bool {
return b.ShouldRunWebClient() && b.exposeWebClientAtomicBool.Load()
}

// setWebClientAtomicBoolLocked sets webClientAtomicBool based on whether
// the RunWebClient pref is set, and whether tailcfg.NodeAttrDisableWebClient
// has been set in the netmap.NetworkMap.
// tailcfg.NodeAttrDisableWebClient has been set in the netmap.NetworkMap.
//
// b.mu must be held.
func (b *LocalBackend) setWebClientAtomicBoolLocked(nm *netmap.NetworkMap, prefs ipn.PrefsView) {
shouldRun := prefs.Valid() && prefs.RunWebClient() && !hasCapability(nm, tailcfg.NodeAttrDisableWebClient)
func (b *LocalBackend) setWebClientAtomicBoolLocked(nm *netmap.NetworkMap) {
shouldRun := !hasCapability(nm, tailcfg.NodeAttrDisableWebClient)
wasRunning := b.webClientAtomicBool.Swap(shouldRun)
if wasRunning && !shouldRun {
go b.webClientShutdown() // stop web client
}
}

// setExposeWebClientAtomicBoolLocked sets exposeWebClientAtomicBool based on
// whether the RunWebClient pref is set.
//
// b.mu must be held.
func (b *LocalBackend) setExposeWebClientAtomicBoolLocked(prefs ipn.PrefsView) {
shouldExpose := prefs.Valid() && prefs.RunWebClient()
b.exposeWebClientAtomicBool.Store(shouldExpose)
}

// ShouldHandleViaIP reports whether ip is an IPv6 address in the
// Tailscale ULA's v6 "via" range embedding an IPv4 address to be forwarded to
// by Tailscale.
Expand Down Expand Up @@ -4808,7 +4827,7 @@ func (b *LocalBackend) setTCPPortsInterceptedFromNetmapAndPrefsLocked(prefs ipn.
if prefs.Valid() && prefs.RunSSH() && envknob.CanSSHD() {
handlePorts = append(handlePorts, 22)
}
if b.ShouldRunWebClient() {
if b.ShouldExposeWebClient() {
handlePorts = append(handlePorts, webClientPort)

// don't listen on netmap addresses if we're in userspace mode
Expand Down

0 comments on commit 60f5015

Please sign in to comment.