@@ -416,6 +416,9 @@ func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func
416
416
s .Health = append (s .Health , err .Error ())
417
417
}
418
418
}
419
+ if m := b .sshOnButUnusableHealthCheckMessageLocked (); m != "" {
420
+ s .Health = append (s .Health , m )
421
+ }
419
422
if b .netMap != nil {
420
423
s .CertDomains = append ([]string (nil ), b .netMap .DNS .CertDomains ... )
421
424
s .MagicDNSSuffix = b .netMap .MagicDNSSuffix ()
@@ -1826,39 +1829,88 @@ func (b *LocalBackend) CheckPrefs(p *ipn.Prefs) error {
1826
1829
}
1827
1830
1828
1831
func (b * LocalBackend ) checkPrefsLocked (p * ipn.Prefs ) error {
1832
+ var errs []error
1829
1833
if p .Hostname == "badhostname.tailscale." {
1830
1834
// Keep this one just for testing.
1831
- return errors .New ("bad hostname [test]" )
1835
+ errs = append ( errs , errors .New ("bad hostname [test]" ) )
1832
1836
}
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." )
1850
1857
}
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 " )
1853
1860
}
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." )
1857
1876
}
1858
1877
}
1859
1878
return nil
1860
1879
}
1861
1880
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
+
1862
1914
func (b * LocalBackend ) EditPrefs (mp * ipn.MaskedPrefs ) (* ipn.Prefs , error ) {
1863
1915
b .mu .Lock ()
1864
1916
p0 := b .prefs .Clone ()
0 commit comments