@@ -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 ()
@@ -1840,39 +1843,88 @@ func (b *LocalBackend) CheckPrefs(p *ipn.Prefs) error {
1840
1843
}
1841
1844
1842
1845
func (b * LocalBackend ) checkPrefsLocked (p * ipn.Prefs ) error {
1846
+ var errs []error
1843
1847
if p .Hostname == "badhostname.tailscale." {
1844
1848
// Keep this one just for testing.
1845
- return errors .New ("bad hostname [test]" )
1849
+ errs = append ( errs , errors .New ("bad hostname [test]" ) )
1846
1850
}
1847
- if p .RunSSH {
1848
- switch runtime .GOOS {
1849
- case "linux" :
1850
- if distro .Get () == distro .Synology && ! envknob .UseWIPCode () {
1851
- return errors .New ("The Tailscale SSH server does not run on Synology." )
1852
- }
1853
- // otherwise okay
1854
- case "darwin" :
1855
- // okay only in tailscaled mode for now.
1856
- if version .IsSandboxedMacOS () {
1857
- return errors .New ("The Tailscale SSH server does not run in sandboxed Tailscale GUI builds." )
1858
- }
1859
- if ! envknob .UseWIPCode () {
1860
- return errors .New ("The Tailscale SSH server is disabled on macOS tailscaled by default. To try, set env TAILSCALE_USE_WIP_CODE=1" )
1861
- }
1862
- default :
1863
- return errors .New ("The Tailscale SSH server is not supported on " + runtime .GOOS )
1851
+ if err := b .checkSSHPrefsLocked (p ); err != nil {
1852
+ errs = append (errs , err )
1853
+ }
1854
+ return multierr .New (errs ... )
1855
+ }
1856
+
1857
+ func (b * LocalBackend ) checkSSHPrefsLocked (p * ipn.Prefs ) error {
1858
+ if ! p .RunSSH {
1859
+ return nil
1860
+ }
1861
+ switch runtime .GOOS {
1862
+ case "linux" :
1863
+ if distro .Get () == distro .Synology && ! envknob .UseWIPCode () {
1864
+ return errors .New ("The Tailscale SSH server does not run on Synology." )
1865
+ }
1866
+ // otherwise okay
1867
+ case "darwin" :
1868
+ // okay only in tailscaled mode for now.
1869
+ if version .IsSandboxedMacOS () {
1870
+ return errors .New ("The Tailscale SSH server does not run in sandboxed Tailscale GUI builds." )
1864
1871
}
1865
- if ! canSSH {
1866
- return errors .New ("The Tailscale SSH server has been administratively disabled. " )
1872
+ if ! envknob . UseWIPCode () {
1873
+ return errors .New ("The Tailscale SSH server is disabled on macOS tailscaled by default. To try, set env TAILSCALE_USE_WIP_CODE=1 " )
1867
1874
}
1868
- if b .netMap != nil && b .netMap .SSHPolicy == nil &&
1869
- envknob .SSHPolicyFile () == "" && ! envknob .SSHIgnoreTailnetPolicy () {
1870
- return errors .New ("Unable to enable local Tailscale SSH server; not enabled/configured on Tailnet." )
1875
+ default :
1876
+ return errors .New ("The Tailscale SSH server is not supported on " + runtime .GOOS )
1877
+ }
1878
+ if ! canSSH {
1879
+ return errors .New ("The Tailscale SSH server has been administratively disabled." )
1880
+ }
1881
+ if envknob .SSHIgnoreTailnetPolicy () || envknob .SSHPolicyFile () != "" {
1882
+ return nil
1883
+ }
1884
+ if b .netMap != nil {
1885
+ if ! hasCapability (b .netMap , tailcfg .CapabilitySSH ) {
1886
+ if b .isDefaultServerLocked () {
1887
+ return errors .New ("Unable to enable local Tailscale SSH server; not enabled on Tailnet. See https://tailscale.com/s/ssh" )
1888
+ }
1889
+ return errors .New ("Unable to enable local Tailscale SSH server; not enabled on Tailnet." )
1871
1890
}
1872
1891
}
1873
1892
return nil
1874
1893
}
1875
1894
1895
+ func (b * LocalBackend ) sshOnButUnusableHealthCheckMessageLocked () (healthMessage string ) {
1896
+ if b .prefs == nil || ! b .prefs .RunSSH {
1897
+ return ""
1898
+ }
1899
+ if envknob .SSHIgnoreTailnetPolicy () || envknob .SSHPolicyFile () != "" {
1900
+ return "development SSH policy in use"
1901
+ }
1902
+ nm := b .netMap
1903
+ if nm == nil {
1904
+ return ""
1905
+ }
1906
+ if nm .SSHPolicy != nil && len (nm .SSHPolicy .Rules ) > 0 {
1907
+ return ""
1908
+ }
1909
+ isDefault := b .isDefaultServerLocked ()
1910
+ isAdmin := hasCapability (nm , tailcfg .CapabilityAdmin )
1911
+
1912
+ if ! isAdmin {
1913
+ 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."
1914
+ }
1915
+ if ! isDefault {
1916
+ return "Tailscale SSH enabled, but access controls don't allow anyone to access this device. Update your tailnet's ACLs to allow access."
1917
+ }
1918
+ 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"
1919
+ }
1920
+
1921
+ func (b * LocalBackend ) isDefaultServerLocked () bool {
1922
+ if b .prefs == nil {
1923
+ return true // assume true until set otherwise
1924
+ }
1925
+ return b .prefs .ControlURLOrDefault () == ipn .DefaultControlURL
1926
+ }
1927
+
1876
1928
func (b * LocalBackend ) EditPrefs (mp * ipn.MaskedPrefs ) (* ipn.Prefs , error ) {
1877
1929
b .mu .Lock ()
1878
1930
p0 := b .prefs .Clone ()
0 commit comments