diff --git a/cmd/tailscale/cli/status.go b/cmd/tailscale/cli/status.go index a414e95d5..a4f5c4233 100644 --- a/cmd/tailscale/cli/status.go +++ b/cmd/tailscale/cli/status.go @@ -238,7 +238,11 @@ func runStatus(ctx context.Context, args []string) error { } printFunnelStatus(ctx) if cv := st.ClientVersion; cv != nil && !cv.RunningLatest && cv.LatestVersion != "" { - printf("# Update available: %v -> %v, run `tailscale update` or `tailscale set --auto-update` to update.\n", version.Short(), cv.LatestVersion) + if cv.UrgentSecurityUpdate { + printf("# Security update available: %v -> %v, run `tailscale update` or `tailscale set --auto-update` to update.\n", version.Short(), cv.LatestVersion) + } else { + printf("# Update available: %v -> %v, run `tailscale update` or `tailscale set --auto-update` to update.\n", version.Short(), cv.LatestVersion) + } } return nil } diff --git a/cmd/tailscale/cli/up.go b/cmd/tailscale/cli/up.go index 8dab79e9f..ffc75814f 100644 --- a/cmd/tailscale/cli/up.go +++ b/cmd/tailscale/cli/up.go @@ -533,7 +533,11 @@ func runUp(ctx context.Context, cmd string, args []string, upArgs upArgsT) (retE // Only need to print an update if we printed the "please click" message earlier. fmt.Fprintf(Stderr, "Success.\n") if cv != nil && !cv.RunningLatest && cv.LatestVersion != "" { - fmt.Fprintf(Stderr, "\nUpdate available: %v -> %v\n", version.Short(), cv.LatestVersion) + if cv.UrgentSecurityUpdate { + fmt.Fprintf(Stderr, "\nSecurity update available: %v -> %v\n", version.Short(), cv.LatestVersion) + } else { + fmt.Fprintf(Stderr, "\nUpdate available: %v -> %v\n", version.Short(), cv.LatestVersion) + } fmt.Fprintln(Stderr, "Changelog: https://tailscale.com/changelog/#client") fmt.Fprintln(Stderr, "Run `tailscale update` or `tailscale set --auto-update` to update") } diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index e7eac4a25..c7031c2d1 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -119,7 +119,8 @@ type CapabilityVersion int // - 76: 2023-09-20: Client understands ExitNodeDNSResolvers for IsWireGuardOnly nodes // - 77: 2023-10-03: Client understands Peers[].SelfNodeV6MasqAddrForThisPeer // - 78: 2023-10-05: can handle c2n Wake-on-LAN sending -const CurrentCapabilityVersion CapabilityVersion = 78 +// - 79: 2023-10-05: Client understands UrgentSecurityUpdate in ClientVersion +const CurrentCapabilityVersion CapabilityVersion = 79 type StableID string @@ -1859,10 +1860,13 @@ type ClientVersion struct { // LatestVersion is the latest version.Short ("1.34.2") version available // for download for the client's platform and packaging type. // It won't be populated if RunningLatest is true. - // The primary purpose of the LatestVersion value is to invalidate the client's - // cache update check value, if any. This primarily applies to Windows. LatestVersion string `json:",omitempty"` + // UrgentSecurityUpdate is set when the client is missing an important + // security update. That update may be in LatestVersion or earlier. + // UrgentSecurityUpdate should not be set if RunningLatest is false. + UrgentSecurityUpdate bool `json:",omitempty"` + // Notify is whether the client should do an OS-specific notification about // a new version being available. This should not be populated if // RunningLatest is true. The client should not notify multiple times for