mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-19 19:38:40 +00:00
ipn: add user pref for running web client
This is not currently exposed as a user-settable preference through `tailscale up` or `tailscale set`. Instead, the preference is set when turning the web client on and off via localapi. In a subsequent commit, the pref will be used to automatically start the web client on startup when appropriate. Updates tailscale/corp#14335 Signed-off-by: Will Norris <will@tailscale.com>
This commit is contained in:
parent
dd842d4d37
commit
28ad910840
@ -810,6 +810,9 @@ func TestPrefFlagMapping(t *testing.T) {
|
|||||||
case "Egg":
|
case "Egg":
|
||||||
// Not applicable.
|
// Not applicable.
|
||||||
continue
|
continue
|
||||||
|
case "RunWebClient":
|
||||||
|
// TODO(tailscale/corp#14335): Currently behind a feature flag.
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
t.Errorf("unexpected new ipn.Pref field %q is not handled by up.go (see addPrefFlagMapping and checkForAccidentalSettingReverts)", prefName)
|
t.Errorf("unexpected new ipn.Pref field %q is not handled by up.go (see addPrefFlagMapping and checkForAccidentalSettingReverts)", prefName)
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ type ConfigVAlpha struct {
|
|||||||
|
|
||||||
PostureChecking opt.Bool `json:",omitempty"`
|
PostureChecking opt.Bool `json:",omitempty"`
|
||||||
RunSSHServer opt.Bool `json:",omitempty"` // Tailscale SSH
|
RunSSHServer opt.Bool `json:",omitempty"` // Tailscale SSH
|
||||||
|
RunWebClient opt.Bool `json:",omitempty"`
|
||||||
ShieldsUp opt.Bool `json:",omitempty"`
|
ShieldsUp opt.Bool `json:",omitempty"`
|
||||||
AutoUpdate *AutoUpdatePrefs `json:",omitempty"`
|
AutoUpdate *AutoUpdatePrefs `json:",omitempty"`
|
||||||
ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this
|
ServeConfigTemp *ServeConfig `json:",omitempty"` // TODO(bradfitz,maisem): make separate stable type for this
|
||||||
@ -113,6 +114,10 @@ func (c *ConfigVAlpha) ToPrefs() (MaskedPrefs, error) {
|
|||||||
mp.RunSSH = c.RunSSHServer.EqualBool(true)
|
mp.RunSSH = c.RunSSHServer.EqualBool(true)
|
||||||
mp.RunSSHSet = true
|
mp.RunSSHSet = true
|
||||||
}
|
}
|
||||||
|
if c.RunWebClient != "" {
|
||||||
|
mp.RunWebClient = c.RunWebClient.EqualBool(true)
|
||||||
|
mp.RunWebClientSet = true
|
||||||
|
}
|
||||||
if c.ShieldsUp != "" {
|
if c.ShieldsUp != "" {
|
||||||
mp.ShieldsUp = c.ShieldsUp.EqualBool(true)
|
mp.ShieldsUp = c.ShieldsUp.EqualBool(true)
|
||||||
mp.ShieldsUpSet = true
|
mp.ShieldsUpSet = true
|
||||||
|
@ -38,6 +38,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
|
|||||||
ExitNodeAllowLANAccess bool
|
ExitNodeAllowLANAccess bool
|
||||||
CorpDNS bool
|
CorpDNS bool
|
||||||
RunSSH bool
|
RunSSH bool
|
||||||
|
RunWebClient bool
|
||||||
WantRunning bool
|
WantRunning bool
|
||||||
LoggedOut bool
|
LoggedOut bool
|
||||||
ShieldsUp bool
|
ShieldsUp bool
|
||||||
|
@ -71,6 +71,7 @@ func (v PrefsView) ExitNodeIP() netip.Addr { return v.ж.ExitNodeIP
|
|||||||
func (v PrefsView) ExitNodeAllowLANAccess() bool { return v.ж.ExitNodeAllowLANAccess }
|
func (v PrefsView) ExitNodeAllowLANAccess() bool { return v.ж.ExitNodeAllowLANAccess }
|
||||||
func (v PrefsView) CorpDNS() bool { return v.ж.CorpDNS }
|
func (v PrefsView) CorpDNS() bool { return v.ж.CorpDNS }
|
||||||
func (v PrefsView) RunSSH() bool { return v.ж.RunSSH }
|
func (v PrefsView) RunSSH() bool { return v.ж.RunSSH }
|
||||||
|
func (v PrefsView) RunWebClient() bool { return v.ж.RunWebClient }
|
||||||
func (v PrefsView) WantRunning() bool { return v.ж.WantRunning }
|
func (v PrefsView) WantRunning() bool { return v.ж.WantRunning }
|
||||||
func (v PrefsView) LoggedOut() bool { return v.ж.LoggedOut }
|
func (v PrefsView) LoggedOut() bool { return v.ж.LoggedOut }
|
||||||
func (v PrefsView) ShieldsUp() bool { return v.ж.ShieldsUp }
|
func (v PrefsView) ShieldsUp() bool { return v.ж.ShieldsUp }
|
||||||
@ -100,6 +101,7 @@ var _PrefsViewNeedsRegeneration = Prefs(struct {
|
|||||||
ExitNodeAllowLANAccess bool
|
ExitNodeAllowLANAccess bool
|
||||||
CorpDNS bool
|
CorpDNS bool
|
||||||
RunSSH bool
|
RunSSH bool
|
||||||
|
RunWebClient bool
|
||||||
WantRunning bool
|
WantRunning bool
|
||||||
LoggedOut bool
|
LoggedOut bool
|
||||||
ShieldsUp bool
|
ShieldsUp bool
|
||||||
|
@ -168,6 +168,7 @@ type LocalBackend struct {
|
|||||||
logFlushFunc func() // or nil if SetLogFlusher wasn't called
|
logFlushFunc func() // or nil if SetLogFlusher wasn't called
|
||||||
em *expiryManager // non-nil
|
em *expiryManager // non-nil
|
||||||
sshAtomicBool atomic.Bool
|
sshAtomicBool atomic.Bool
|
||||||
|
webclientAtomicBool atomic.Bool
|
||||||
shutdownCalled bool // if Shutdown has been called
|
shutdownCalled bool // if Shutdown has been called
|
||||||
debugSink *capture.Sink
|
debugSink *capture.Sink
|
||||||
sockstatLogger *sockstatlog.Logger
|
sockstatLogger *sockstatlog.Logger
|
||||||
@ -2500,6 +2501,7 @@ func (b *LocalBackend) setTCPPortsIntercepted(ports []uint16) {
|
|||||||
// and shouldInterceptTCPPortAtomic from the prefs p, which may be !Valid().
|
// and shouldInterceptTCPPortAtomic from the prefs p, which may be !Valid().
|
||||||
func (b *LocalBackend) setAtomicValuesFromPrefsLocked(p ipn.PrefsView) {
|
func (b *LocalBackend) setAtomicValuesFromPrefsLocked(p ipn.PrefsView) {
|
||||||
b.sshAtomicBool.Store(p.Valid() && p.RunSSH() && envknob.CanSSHD())
|
b.sshAtomicBool.Store(p.Valid() && p.RunSSH() && envknob.CanSSHD())
|
||||||
|
b.webclientAtomicBool.Store(p.Valid() && p.RunWebClient())
|
||||||
|
|
||||||
if !p.Valid() {
|
if !p.Valid() {
|
||||||
b.containsViaIPFuncAtomic.Store(tsaddr.FalseContainsIPFunc())
|
b.containsViaIPFuncAtomic.Store(tsaddr.FalseContainsIPFunc())
|
||||||
@ -2918,6 +2920,11 @@ func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
|||||||
b.logf("EditPrefs requests SSH, but disabled by envknob; returning error")
|
b.logf("EditPrefs requests SSH, but disabled by envknob; returning error")
|
||||||
return ipn.PrefsView{}, errors.New("Tailscale SSH server administratively disabled.")
|
return ipn.PrefsView{}, errors.New("Tailscale SSH server administratively disabled.")
|
||||||
}
|
}
|
||||||
|
if p1.RunWebClient && !envknob.Bool("TS_DEBUG_WEB_UI") {
|
||||||
|
b.mu.Unlock()
|
||||||
|
b.logf("EditPrefs requests web client, but disabled by envknob; returning error")
|
||||||
|
return ipn.PrefsView{}, errors.New("web ui flag not set")
|
||||||
|
}
|
||||||
if p1.View().Equals(p0) {
|
if p1.View().Equals(p0) {
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
return stripKeysFromPrefs(p0), nil
|
return stripKeysFromPrefs(p0), nil
|
||||||
@ -3010,6 +3017,9 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) ipn
|
|||||||
b.sshServer = nil
|
b.sshServer = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if oldp.ShouldWebClientBeRunning() && !newp.ShouldWebClientBeRunning() {
|
||||||
|
b.WebShutdown()
|
||||||
|
}
|
||||||
if netMap != nil {
|
if netMap != nil {
|
||||||
newProfile := netMap.UserProfiles[netMap.User()]
|
newProfile := netMap.UserProfiles[netMap.User()]
|
||||||
if newLoginName := newProfile.LoginName; newLoginName != "" {
|
if newLoginName := newProfile.LoginName; newLoginName != "" {
|
||||||
@ -4146,6 +4156,10 @@ func (b *LocalBackend) ResetForClientDisconnect() {
|
|||||||
|
|
||||||
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Load() && envknob.CanSSHD() }
|
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Load() && envknob.CanSSHD() }
|
||||||
|
|
||||||
|
func (b *LocalBackend) ShouldRunWebClient() bool {
|
||||||
|
return b.webclientAtomicBool.Load() && envknob.Bool("TS_DEBUG_WEB_UI")
|
||||||
|
}
|
||||||
|
|
||||||
// ShouldHandleViaIP reports whether ip is an IPv6 address in the
|
// ShouldHandleViaIP reports whether ip is an IPv6 address in the
|
||||||
// Tailscale ULA's v6 "via" range embedding an IPv4 address to be forwarded to
|
// Tailscale ULA's v6 "via" range embedding an IPv4 address to be forwarded to
|
||||||
// by Tailscale.
|
// by Tailscale.
|
||||||
|
@ -2249,10 +2249,20 @@ func (h *Handler) serveWeb(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// try to set pref, but ignore errors
|
||||||
|
_, _ = h.b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
|
Prefs: ipn.Prefs{RunWebClient: true},
|
||||||
|
RunWebClientSet: true,
|
||||||
|
})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
case "/localapi/v0/web/stop":
|
case "/localapi/v0/web/stop":
|
||||||
h.b.WebShutdown()
|
h.b.WebShutdown()
|
||||||
|
// try to set pref, but ignore errors
|
||||||
|
_, _ = h.b.EditPrefs(&ipn.MaskedPrefs{
|
||||||
|
Prefs: ipn.Prefs{RunWebClient: false},
|
||||||
|
RunWebClientSet: true,
|
||||||
|
})
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
|
22
ipn/prefs.go
22
ipn/prefs.go
@ -112,6 +112,11 @@ type Prefs struct {
|
|||||||
// policies as configured by the Tailnet's admin(s).
|
// policies as configured by the Tailnet's admin(s).
|
||||||
RunSSH bool
|
RunSSH bool
|
||||||
|
|
||||||
|
// RunWebClient bool is whether this node should run a web client,
|
||||||
|
// permitting access to peers according to the
|
||||||
|
// policies as configured by the Tailnet's admin(s).
|
||||||
|
RunWebClient bool
|
||||||
|
|
||||||
// WantRunning indicates whether networking should be active on
|
// WantRunning indicates whether networking should be active on
|
||||||
// this node.
|
// this node.
|
||||||
WantRunning bool
|
WantRunning bool
|
||||||
@ -236,6 +241,7 @@ type MaskedPrefs struct {
|
|||||||
ExitNodeAllowLANAccessSet bool `json:",omitempty"`
|
ExitNodeAllowLANAccessSet bool `json:",omitempty"`
|
||||||
CorpDNSSet bool `json:",omitempty"`
|
CorpDNSSet bool `json:",omitempty"`
|
||||||
RunSSHSet bool `json:",omitempty"`
|
RunSSHSet bool `json:",omitempty"`
|
||||||
|
RunWebClientSet bool `json:",omitempty"`
|
||||||
WantRunningSet bool `json:",omitempty"`
|
WantRunningSet bool `json:",omitempty"`
|
||||||
LoggedOutSet bool `json:",omitempty"`
|
LoggedOutSet bool `json:",omitempty"`
|
||||||
ShieldsUpSet bool `json:",omitempty"`
|
ShieldsUpSet bool `json:",omitempty"`
|
||||||
@ -350,6 +356,9 @@ func (p *Prefs) pretty(goos string) string {
|
|||||||
if p.RunSSH {
|
if p.RunSSH {
|
||||||
sb.WriteString("ssh=true ")
|
sb.WriteString("ssh=true ")
|
||||||
}
|
}
|
||||||
|
if p.RunWebClient {
|
||||||
|
sb.WriteString("webclient=true ")
|
||||||
|
}
|
||||||
if p.LoggedOut {
|
if p.LoggedOut {
|
||||||
sb.WriteString("loggedout=true ")
|
sb.WriteString("loggedout=true ")
|
||||||
}
|
}
|
||||||
@ -431,6 +440,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
|
|||||||
p.ExitNodeAllowLANAccess == p2.ExitNodeAllowLANAccess &&
|
p.ExitNodeAllowLANAccess == p2.ExitNodeAllowLANAccess &&
|
||||||
p.CorpDNS == p2.CorpDNS &&
|
p.CorpDNS == p2.CorpDNS &&
|
||||||
p.RunSSH == p2.RunSSH &&
|
p.RunSSH == p2.RunSSH &&
|
||||||
|
p.RunWebClient == p2.RunWebClient &&
|
||||||
p.WantRunning == p2.WantRunning &&
|
p.WantRunning == p2.WantRunning &&
|
||||||
p.LoggedOut == p2.LoggedOut &&
|
p.LoggedOut == p2.LoggedOut &&
|
||||||
p.NotepadURLs == p2.NotepadURLs &&
|
p.NotepadURLs == p2.NotepadURLs &&
|
||||||
@ -691,6 +701,18 @@ func (p *Prefs) ShouldSSHBeRunning() bool {
|
|||||||
return p.WantRunning && p.RunSSH
|
return p.WantRunning && p.RunSSH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldWebClientBeRunning reports whether the web client server should be running based on
|
||||||
|
// the prefs.
|
||||||
|
func (p PrefsView) ShouldWebClientBeRunning() bool {
|
||||||
|
return p.Valid() && p.ж.ShouldWebClientBeRunning()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldWebClientBeRunning reports whether the web client server should be running based on
|
||||||
|
// the prefs.
|
||||||
|
func (p *Prefs) ShouldWebClientBeRunning() bool {
|
||||||
|
return p.WantRunning && p.RunWebClient
|
||||||
|
}
|
||||||
|
|
||||||
// PrefsFromBytes deserializes Prefs from a JSON blob.
|
// PrefsFromBytes deserializes Prefs from a JSON blob.
|
||||||
func PrefsFromBytes(b []byte) (*Prefs, error) {
|
func PrefsFromBytes(b []byte) (*Prefs, error) {
|
||||||
p := NewPrefs()
|
p := NewPrefs()
|
||||||
|
@ -43,6 +43,7 @@ func TestPrefsEqual(t *testing.T) {
|
|||||||
"ExitNodeAllowLANAccess",
|
"ExitNodeAllowLANAccess",
|
||||||
"CorpDNS",
|
"CorpDNS",
|
||||||
"RunSSH",
|
"RunSSH",
|
||||||
|
"RunWebClient",
|
||||||
"WantRunning",
|
"WantRunning",
|
||||||
"LoggedOut",
|
"LoggedOut",
|
||||||
"ShieldsUp",
|
"ShieldsUp",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user