ipn/ipnlocal: shutdown sshServer on tailscale down

Also lazify SSHServer initialization to allow restarting the server on a
subsequent `tailscale up`

Updates #3802

Signed-off-by: Maisem Ali <maisem@tailscale.com>
pull/4765/head
Maisem Ali 3 years ago committed by Maisem Ali
parent 4d85cf586b
commit 928530a112

@ -125,8 +125,7 @@ type LocalBackend struct {
newDecompressor func() (controlclient.Decompressor, error) newDecompressor func() (controlclient.Decompressor, error)
varRoot string // or empty if SetVarRoot never called varRoot string // or empty if SetVarRoot never called
sshAtomicBool syncs.AtomicBool sshAtomicBool syncs.AtomicBool
sshServer SSHServer // or nil shutdownCalled bool // if Shutdown has been called
shutdownCalled bool // if Shutdown has been called
filterAtomic atomic.Value // of *filter.Filter filterAtomic atomic.Value // of *filter.Filter
containsViaIPFuncAtomic atomic.Value // of func(netaddr.IP) bool containsViaIPFuncAtomic atomic.Value // of func(netaddr.IP) bool
@ -136,6 +135,7 @@ type LocalBackend struct {
filterHash deephash.Sum filterHash deephash.Sum
httpTestClient *http.Client // for controlclient. nil by default, used by tests. httpTestClient *http.Client // for controlclient. nil by default, used by tests.
ccGen clientGen // function for producing controlclient; lazily populated ccGen clientGen // function for producing controlclient; lazily populated
sshServer SSHServer // or nil, initialized lazily.
notify func(ipn.Notify) notify func(ipn.Notify)
cc controlclient.Client cc controlclient.Client
stateKey ipn.StateKey // computed in part from user-provided value stateKey ipn.StateKey // computed in part from user-provided value
@ -228,12 +228,6 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, diale
gotPortPollRes: make(chan struct{}), gotPortPollRes: make(chan struct{}),
loginFlags: loginFlags, loginFlags: loginFlags,
} }
if newSSHServer != nil {
b.sshServer, err = newSSHServer(logf, b)
if err != nil {
return nil, fmt.Errorf("newSSHServer: %w", err)
}
}
// Default filter blocks everything and logs nothing, until Start() is called. // Default filter blocks everything and logs nothing, until Start() is called.
b.setFilter(filter.NewAllowNone(logf, &netaddr.IPSet{})) b.setFilter(filter.NewAllowNone(logf, &netaddr.IPSet{}))
@ -351,6 +345,7 @@ func (b *LocalBackend) Shutdown() {
cc := b.cc cc := b.cc
if b.sshServer != nil { if b.sshServer != nil {
b.sshServer.Shutdown() b.sshServer.Shutdown()
b.sshServer = nil
} }
b.closePeerAPIListenersLocked() b.closePeerAPIListenersLocked()
b.mu.Unlock() b.mu.Unlock()
@ -1932,6 +1927,12 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
} }
b.updateFilterLocked(netMap, newp) b.updateFilterLocked(netMap, newp)
if oldp.ShouldSSHBeRunning() && !newp.ShouldSSHBeRunning() {
if b.sshServer != nil {
go b.sshServer.Shutdown()
b.sshServer = nil
}
}
b.mu.Unlock() b.mu.Unlock()
if stateKey != "" { if stateKey != "" {
@ -1975,10 +1976,6 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
b.authReconfig() b.authReconfig()
} }
if oldp.RunSSH && !newp.RunSSH && b.sshServer != nil {
go b.sshServer.OnPolicyChange()
}
b.send(ipn.Notify{Prefs: newp}) b.send(ipn.Notify{Prefs: newp})
} }
@ -3367,11 +3364,28 @@ func (b *LocalBackend) DoNoiseRequest(req *http.Request) (*http.Response, error)
return cc.DoNoiseRequest(req) return cc.DoNoiseRequest(req)
} }
func (b *LocalBackend) HandleSSHConn(c net.Conn) error { func (b *LocalBackend) sshServerOrInit() (_ SSHServer, err error) {
if b.sshServer == nil { b.mu.Lock()
return errors.New("no SSH server") defer b.mu.Unlock()
if b.sshServer != nil {
return b.sshServer, nil
}
if newSSHServer == nil {
return nil, errors.New("no SSH server support")
}
b.sshServer, err = newSSHServer(b.logf, b)
if err != nil {
return nil, fmt.Errorf("newSSHServer: %w", err)
}
return b.sshServer, nil
}
func (b *LocalBackend) HandleSSHConn(c net.Conn) (err error) {
s, err := b.sshServerOrInit()
if err != nil {
return err
} }
return b.sshServer.HandleSSHConn(c) return s.HandleSSHConn(c)
} }
// HandleQuad100Port80Conn serves http://100.100.100.100/ on port 80 (and // HandleQuad100Port80Conn serves http://100.100.100.100/ on port 80 (and

@ -584,6 +584,12 @@ func (p *Prefs) SetExitNodeIP(s string, st *ipnstate.Status) error {
return err return err
} }
// ShouldSSHBeRunning reports whether the SSH server should be running based on
// the prefs.
func (p *Prefs) ShouldSSHBeRunning() bool {
return p.WantRunning && p.RunSSH
}
// 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()

@ -110,7 +110,7 @@ func (srv *server) Shutdown() {
srv.shutdownCalled = true srv.shutdownCalled = true
for _, s := range srv.activeSessionByH { for _, s := range srv.activeSessionByH {
s.ctx.CloseWithError(userVisibleError{ s.ctx.CloseWithError(userVisibleError{
fmt.Sprintf("Tailscale shutting down.\r\n"), fmt.Sprintf("Tailscale SSH is shutting down.\r\n"),
context.Canceled, context.Canceled,
}) })
} }
@ -876,7 +876,7 @@ func (ss *sshSession) run() {
if srv.shutdownCalled { if srv.shutdownCalled {
srv.mu.Unlock() srv.mu.Unlock()
// Do not start any new sessions. // Do not start any new sessions.
fmt.Fprintf(ss, "Tailscale is shutting down\r\n") fmt.Fprintf(ss, "Tailscale SSH is shutting down\r\n")
ss.Exit(1) ss.Exit(1)
return return
} }

Loading…
Cancel
Save