Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 32f26a7

Browse files
committed
cmd/tailscale/cli, ipn/ipnlocal: give SSH tips when off/unconfigured
Updates tailscale#3802 Change-Id: I6b9a3175f68a6daa670f912561f2c2ececc07770 Signed-off-by: Brad Fitzpatrick <[email protected]> (cherry picked from commit 467eb2e)
1 parent c1795c6 commit 32f26a7

File tree

2 files changed

+104
-24
lines changed

2 files changed

+104
-24
lines changed

cmd/tailscale/cli/up.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ func updatePrefs(prefs, curPrefs *ipn.Prefs, env upCheckEnv) (simpleUp bool, jus
404404
return simpleUp, justEditMP, nil
405405
}
406406

407-
func runUp(ctx context.Context, args []string) error {
407+
func runUp(ctx context.Context, args []string) (retErr error) {
408408
if len(args) > 0 {
409409
fatalf("too many non-flag arguments: %q", args)
410410
}
@@ -481,6 +481,12 @@ func runUp(ctx context.Context, args []string) error {
481481
}
482482
}
483483

484+
defer func() {
485+
if retErr == nil {
486+
checkSSHUpWarnings(ctx)
487+
}
488+
}()
489+
484490
simpleUp, justEditMP, err := updatePrefs(prefs, curPrefs, env)
485491
if err != nil {
486492
fatalf("%s", err)
@@ -676,6 +682,28 @@ func runUp(ctx context.Context, args []string) error {
676682
}
677683
}
678684

685+
func checkSSHUpWarnings(ctx context.Context) {
686+
if !upArgs.runSSH {
687+
return
688+
}
689+
st, err := localClient.Status(ctx)
690+
if err != nil {
691+
// Ignore. Don't spam more.
692+
return
693+
}
694+
if len(st.Health) == 0 {
695+
return
696+
}
697+
if len(st.Health) == 1 && strings.Contains(st.Health[0], "SSH") {
698+
printf("%s\n", st.Health[0])
699+
return
700+
}
701+
printf("# Health check:\n")
702+
for _, m := range st.Health {
703+
printf(" - %s\n", m)
704+
}
705+
}
706+
679707
func printUpDoneJSON(state ipn.State, errorString string) {
680708
js := &upOutputJSON{BackendState: state.String(), Error: errorString}
681709
data, err := json.MarshalIndent(js, "", " ")

ipn/ipnlocal/local.go

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
416416
s.Health = append(s.Health, err.Error())
417417
}
418418
}
419+
if m := b.sshOnButUnusableHealthCheckMessageLocked(); m != "" {
420+
s.Health = append(s.Health, m)
421+
}
419422
if b.netMap != nil {
420423
s.CertDomains = append([]string(nil), b.netMap.DNS.CertDomains...)
421424
s.MagicDNSSuffix = b.netMap.MagicDNSSuffix()
@@ -1826,39 +1829,88 @@ func (b *LocalBackend) CheckPrefs(p *ipn.Prefs) error {
18261829
}
18271830

18281831
func (b *LocalBackend) checkPrefsLocked(p *ipn.Prefs) error {
1832+
var errs []error
18291833
if p.Hostname == "badhostname.tailscale." {
18301834
// Keep this one just for testing.
1831-
return errors.New("bad hostname [test]")
1835+
errs = append(errs, errors.New("bad hostname [test]"))
18321836
}
1833-
if p.RunSSH {
1834-
switch runtime.GOOS {
1835-
case "linux":
1836-
if distro.Get() == distro.Synology && !envknob.UseWIPCode() {
1837-
return errors.New("The Tailscale SSH server does not run on Synology.")
1838-
}
1839-
// otherwise okay
1840-
case "darwin":
1841-
// okay only in tailscaled mode for now.
1842-
if version.IsSandboxedMacOS() {
1843-
return errors.New("The Tailscale SSH server does not run in sandboxed Tailscale GUI builds.")
1844-
}
1845-
if !envknob.UseWIPCode() {
1846-
return errors.New("The Tailscale SSH server is disabled on macOS tailscaled by default. To try, set env TAILSCALE_USE_WIP_CODE=1")
1847-
}
1848-
default:
1849-
return errors.New("The Tailscale SSH server is not supported on " + runtime.GOOS)
1837+
if err := b.checkSSHPrefsLocked(p); err != nil {
1838+
errs = append(errs, err)
1839+
}
1840+
return multierr.New(errs...)
1841+
}
1842+
1843+
func (b *LocalBackend) checkSSHPrefsLocked(p *ipn.Prefs) error {
1844+
if !p.RunSSH {
1845+
return nil
1846+
}
1847+
switch runtime.GOOS {
1848+
case "linux":
1849+
if distro.Get() == distro.Synology && !envknob.UseWIPCode() {
1850+
return errors.New("The Tailscale SSH server does not run on Synology.")
1851+
}
1852+
// otherwise okay
1853+
case "darwin":
1854+
// okay only in tailscaled mode for now.
1855+
if version.IsSandboxedMacOS() {
1856+
return errors.New("The Tailscale SSH server does not run in sandboxed Tailscale GUI builds.")
18501857
}
1851-
if !canSSH {
1852-
return errors.New("The Tailscale SSH server has been administratively disabled.")
1858+
if !envknob.UseWIPCode() {
1859+
return errors.New("The Tailscale SSH server is disabled on macOS tailscaled by default. To try, set env TAILSCALE_USE_WIP_CODE=1")
18531860
}
1854-
if b.netMap != nil && b.netMap.SSHPolicy == nil &&
1855-
envknob.SSHPolicyFile() == "" && !envknob.SSHIgnoreTailnetPolicy() {
1856-
return errors.New("Unable to enable local Tailscale SSH server; not enabled/configured on Tailnet.")
1861+
default:
1862+
return errors.New("The Tailscale SSH server is not supported on " + runtime.GOOS)
1863+
}
1864+
if !canSSH {
1865+
return errors.New("The Tailscale SSH server has been administratively disabled.")
1866+
}
1867+
if envknob.SSHIgnoreTailnetPolicy() || envknob.SSHPolicyFile() != "" {
1868+
return nil
1869+
}
1870+
if b.netMap != nil {
1871+
if !hasCapability(b.netMap, tailcfg.CapabilitySSH) {
1872+
if b.isDefaultServerLocked() {
1873+
return errors.New("Unable to enable local Tailscale SSH server; not enabled on Tailnet. See https://tailscale.com/s/ssh")
1874+
}
1875+
return errors.New("Unable to enable local Tailscale SSH server; not enabled on Tailnet.")
18571876
}
18581877
}
18591878
return nil
18601879
}
18611880

1881+
func (b *LocalBackend) sshOnButUnusableHealthCheckMessageLocked() (healthMessage string) {
1882+
if b.prefs == nil || !b.prefs.RunSSH {
1883+
return ""
1884+
}
1885+
if envknob.SSHIgnoreTailnetPolicy() || envknob.SSHPolicyFile() != "" {
1886+
return "development SSH policy in use"
1887+
}
1888+
nm := b.netMap
1889+
if nm == nil {
1890+
return ""
1891+
}
1892+
if nm.SSHPolicy != nil && len(nm.SSHPolicy.Rules) > 0 {
1893+
return ""
1894+
}
1895+
isDefault := b.isDefaultServerLocked()
1896+
isAdmin := hasCapability(nm, tailcfg.CapabilityAdmin)
1897+
1898+
if !isAdmin {
1899+
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Ask your admin to update your tailnet's ACLs to allow access."
1900+
}
1901+
if !isDefault {
1902+
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Update your tailnet's ACLs to allow access."
1903+
}
1904+
return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Update your tailnet's ACLs at https://tailscale.com/s/ssh-policy"
1905+
}
1906+
1907+
func (b *LocalBackend) isDefaultServerLocked() bool {
1908+
if b.prefs == nil {
1909+
return true // assume true until set otherwise
1910+
}
1911+
return b.prefs.ControlURLOrDefault() == ipn.DefaultControlURL
1912+
}
1913+
18621914
func (b *LocalBackend) EditPrefs(mp *ipn.MaskedPrefs) (*ipn.Prefs, error) {
18631915
b.mu.Lock()
18641916
p0 := b.prefs.Clone()

0 commit comments

Comments
 (0)