envknob, ipn/ipnlocal: add SSH admin kill switch
Updates #3802 Change-Id: I6127907446d1a6be1b097d9ba3b534f2b8eb707f Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>pull/4273/head
parent
77b4fe0afa
commit
d2f3ec8a63
|
@ -183,6 +183,7 @@ func send(ctx context.Context, method, path string, wantStatus int, body io.Read
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if res.StatusCode != wantStatus {
|
if res.StatusCode != wantStatus {
|
||||||
|
err = fmt.Errorf("%v: %s", res.Status, bytes.TrimSpace(slurp))
|
||||||
return nil, bestError(err, slurp)
|
return nil, bestError(err, slurp)
|
||||||
}
|
}
|
||||||
return slurp, nil
|
return slurp, nil
|
||||||
|
|
|
@ -142,3 +142,10 @@ func LookupInt(envVar string) (v int, ok bool) {
|
||||||
// UseWIPCode is whether TAILSCALE_USE_WIP_CODE is set to permit use
|
// UseWIPCode is whether TAILSCALE_USE_WIP_CODE is set to permit use
|
||||||
// of Work-In-Progress code.
|
// of Work-In-Progress code.
|
||||||
func UseWIPCode() bool { return Bool("TAILSCALE_USE_WIP_CODE") }
|
func UseWIPCode() bool { return Bool("TAILSCALE_USE_WIP_CODE") }
|
||||||
|
|
||||||
|
// CanSSHD is whether the Tailscale SSH server is allowed to run.
|
||||||
|
//
|
||||||
|
// If disabled, the SSH server won't start (won't intercept port 22)
|
||||||
|
// if already enabled and any attempt to re-enable it will result in
|
||||||
|
// an error.
|
||||||
|
func CanSSHD() bool { return !Bool("TS_DISABLE_SSH_SERVER") }
|
||||||
|
|
|
@ -65,6 +65,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var controlDebugFlags = getControlDebugFlags()
|
var controlDebugFlags = getControlDebugFlags()
|
||||||
|
var canSSH = envknob.CanSSHD()
|
||||||
|
|
||||||
func getControlDebugFlags() []string {
|
func getControlDebugFlags() []string {
|
||||||
if e := envknob.String("TS_DEBUG_CONTROL_FLAGS"); e != "" {
|
if e := envknob.String("TS_DEBUG_CONTROL_FLAGS"); e != "" {
|
||||||
|
@ -1564,7 +1565,7 @@ func (b *LocalBackend) loadStateLocked(key ipn.StateKey, prefs *ipn.Prefs) (err
|
||||||
|
|
||||||
b.logf("using backend prefs for %q: %s", key, b.prefs.Pretty())
|
b.logf("using backend prefs for %q: %s", key, b.prefs.Pretty())
|
||||||
|
|
||||||
b.sshAtomicBool.Set(b.prefs != nil && b.prefs.RunSSH)
|
b.sshAtomicBool.Set(b.prefs != nil && b.prefs.RunSSH && canSSH)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1703,6 +1704,11 @@ func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (*ipn.Prefs, error) {
|
||||||
p0 := b.prefs.Clone()
|
p0 := b.prefs.Clone()
|
||||||
p1 := b.prefs.Clone()
|
p1 := b.prefs.Clone()
|
||||||
p1.ApplyEdits(mp)
|
p1.ApplyEdits(mp)
|
||||||
|
if p1.RunSSH && !canSSH {
|
||||||
|
b.mu.Unlock()
|
||||||
|
b.logf("EditPrefs requests SSH, but disabled by envknob; returning error")
|
||||||
|
return nil, errors.New("Tailscale SSH server administratively disabled.")
|
||||||
|
}
|
||||||
if p1.Equals(p0) {
|
if p1.Equals(p0) {
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
return p1, nil
|
return p1, nil
|
||||||
|
@ -1732,7 +1738,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
|
||||||
netMap := b.netMap
|
netMap := b.netMap
|
||||||
stateKey := b.stateKey
|
stateKey := b.stateKey
|
||||||
|
|
||||||
b.sshAtomicBool.Set(newp.RunSSH)
|
b.sshAtomicBool.Set(newp.RunSSH && canSSH)
|
||||||
|
|
||||||
oldp := b.prefs
|
oldp := b.prefs
|
||||||
newp.Persist = oldp.Persist // caller isn't allowed to override this
|
newp.Persist = oldp.Persist // caller isn't allowed to override this
|
||||||
|
@ -2462,7 +2468,7 @@ func (b *LocalBackend) applyPrefsToHostinfo(hi *tailcfg.Hostinfo, prefs *ipn.Pre
|
||||||
hi.ShieldsUp = prefs.ShieldsUp
|
hi.ShieldsUp = prefs.ShieldsUp
|
||||||
|
|
||||||
var sshHostKeys []string
|
var sshHostKeys []string
|
||||||
if prefs.RunSSH {
|
if prefs.RunSSH && canSSH {
|
||||||
// TODO(bradfitz): this is called with b.mu held. Not ideal.
|
// TODO(bradfitz): this is called with b.mu held. Not ideal.
|
||||||
// If the filesystem gets wedged or something we could block for
|
// If the filesystem gets wedged or something we could block for
|
||||||
// a long time. But probably fine.
|
// a long time. But probably fine.
|
||||||
|
@ -2679,7 +2685,7 @@ func (b *LocalBackend) ResetForClientDisconnect() {
|
||||||
b.sshAtomicBool.Set(false)
|
b.sshAtomicBool.Set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Get() }
|
func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Get() && canSSH }
|
||||||
|
|
||||||
// Logout tells the controlclient that we want to log out, and
|
// Logout tells the controlclient that we want to log out, and
|
||||||
// transitions the local engine to the logged-out state without
|
// transitions the local engine to the logged-out state without
|
||||||
|
|
Loading…
Reference in New Issue