Skip to content

Commit a46a222

Browse files
committed
libtailscale,android: toggle remote client logging without restarting
Switch the remote-logging toggle from the one-way process-wide logtail.Disable kill switch to per-logger APIs added upstream: Logger.SetEnabled for runtime flips, and Config.Disabled so the first NewLogger call doesn't even emit the internal "logtail started" banner when the user has opted out. Bumps go.mod to tailscale.com 1e68a11721fd which includes both. The setting now takes effect immediately with no restart required. App.updateIsClientLoggingEnabled pushes the effective value (still forced on while MDM is configured) to the backend, and the MDM change receiver does the same on MDM transitions so an arriving MDM profile forces logging back on live. Also fix the inverted MDMSettings.isMDMConfigured check so it tracks "MDM restrictions are set" rather than its inverse; without this the prior commit silently did the opposite of what it claimed. Drop the "changes require restarting the app to take effect" wording from the settings subtitle and disable-confirmation dialog. Updates #13174 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
1 parent 9f66d46 commit a46a222

10 files changed

Lines changed: 38 additions & 22 deletions

File tree

android/src/main/java/com/tailscale/ipn/App.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ open class UninitializedApp : Application() {
716716

717717
fun updateIsClientLoggingEnabled(value: Boolean) {
718718
getUnencryptedPrefs().edit().putBoolean(IS_CLIENT_LOGGING_ENABLED_KEY, value).apply()
719+
App.get().getLibtailscaleApp().setClientLoggingEnabled(getIsClientLoggingEnabled())
719720
}
720721

721722
fun updateUserSelectedPackages(packageNames: List<String>) {

android/src/main/java/com/tailscale/ipn/mdm/MDMSettings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ object MDMSettings {
135135
fun loadFrom(preferences: Lazy<SharedPreferences>, restrictionsManager: RestrictionsManager?) {
136136
val bundle = restrictionsManager?.applicationRestrictions
137137
allSettings.forEach { it.setFrom(bundle, preferences) }
138-
isMDMConfigured = bundle?.isEmpty == true
138+
isMDMConfigured = bundle != null && !bundle.isEmpty
139139
}
140140

141141
fun update(app: App, restrictionsManager: RestrictionsManager?) {

android/src/main/java/com/tailscale/ipn/mdm/MDMSettingsChangedReceiver.kt

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@ class MDMSettingsChangedReceiver : BroadcastReceiver() {
1717
val restrictionsManager =
1818
context?.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
1919

20-
val previouslyIsMDMEnabled = MDMSettings.isMDMConfigured
21-
2220
MDMSettings.update(App.get(), restrictionsManager)
2321

24-
if (MDMSettings.isMDMConfigured && !previouslyIsMDMEnabled) {
25-
// async MDM settings updated from disabled -> enabled. restart to ensure
26-
// correctly applied (particularly forcing client logs on).
27-
// TODO: actually restart
28-
}
22+
// MDM state may have flipped the effective client-logging value
23+
// (getIsClientLoggingEnabled forces true under MDM); push the
24+
// current effective value so the backend toggles immediately.
25+
App.get().getLibtailscaleApp().setClientLoggingEnabled(App.get().getIsClientLoggingEnabled())
2926
}
3027
}
3128
}

android/src/main/res/values/strings.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,10 +355,10 @@
355355
<string name="use_tailscale_subnets_subtitle">Route traffic according to your network\'s rules. Some networks require this to access IP addresses that don\'t start with 100.x.y.z.</string>
356356
<string name="subnet_routing">Subnet routing</string>
357357
<string name="client_remote_logging_enabled">Remote client logging</string>
358-
<string name="client_remote_logging_enabled_subtitle">Whether debug &amp; opt-in flow logs are uploaded. Changes require restarting the app to take effect.</string>
358+
<string name="client_remote_logging_enabled_subtitle">Whether debug &amp; opt-in flow logs are uploaded.</string>
359359
<string name="client_remote_logging_enabled_subtitle_mdm">Client logging is always enabled for devices under remote management.</string>
360360
<string name="client_remote_logging_disable_confirm_title">Disable remote client logging?</string>
361-
<string name="client_remote_logging_disable_confirm_message">Disabling remote client logging will break Network Flow Logs if enabled and required by your tailnet admin, and will prevent Tailscale Support from being able to help debug problems with this device.\n\nChanges require restarting the app to take effect.</string>
361+
<string name="client_remote_logging_disable_confirm_message">Disabling remote client logging will break Network Flow Logs if enabled and required by your tailnet admin, and will prevent Tailscale Support from being able to help debug problems with this device.</string>
362362
<string name="client_remote_logging_disable_confirm_button">Yes, disable</string>
363363
<string name="specifies_a_device_name_to_be_used_instead_of_the_automatic_default">Specifies a device name to be used instead of the automatic default.</string>
364364
<string name="hostname">Hostname</string>

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.26.2
55
require (
66
github.com/tailscale/wireguard-go v0.0.0-20260304043104-4184faf59e56
77
golang.org/x/mobile v0.0.0-20240806205939-81131f6468ab
8-
tailscale.com v1.97.0-pre.0.20260408011054-a182b864ace4
8+
tailscale.com v1.97.0-pre.0.20260420203310-1e68a11721fd
99
)
1010

1111
require (
@@ -61,9 +61,9 @@ require (
6161
github.com/pires/go-proxyproto v0.8.1 // indirect
6262
github.com/prometheus-community/pro-bing v0.4.0 // indirect
6363
github.com/safchain/ethtool v0.3.0 // indirect
64-
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
64+
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
6565
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
66-
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
66+
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd // indirect
6767
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
6868
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
6969
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,16 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
157157
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
158158
github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
159159
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
160-
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
161-
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
160+
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
161+
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
162162
github.com/tailscale/gliderssh v0.3.4-0.20260330083525-c1389c70ff89 h1:glgVc1ZYMjwN1Q/ITWeuSQyl029uayagaR2sjsifehc=
163163
github.com/tailscale/gliderssh v0.3.4-0.20260330083525-c1389c70ff89/go.mod h1:wn16Km1EZOX4UEAyaZa3dBwfFGOJ7neck40NcwosJUw=
164164
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
165165
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
166166
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM=
167167
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
168-
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
169-
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
168+
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA=
169+
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
170170
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
171171
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
172172
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
@@ -247,5 +247,5 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
247247
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
248248
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
249249
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
250-
tailscale.com v1.97.0-pre.0.20260408011054-a182b864ace4 h1:EbBRCAjxEWU/61I/DDqMg4aMKw0uqYCtt8diNQ3DQjI=
251-
tailscale.com v1.97.0-pre.0.20260408011054-a182b864ace4/go.mod h1:J3yUifgjBmMBIylCvle8qVui/MDlsjP6aRPsZ9pjfUY=
250+
tailscale.com v1.97.0-pre.0.20260420203310-1e68a11721fd h1:D8jEUQOnUrOHF3D6hFXwApugaKZvjL2eaedZ02IgSA4=
251+
tailscale.com v1.97.0-pre.0.20260420203310-1e68a11721fd/go.mod h1:/8b6sKYTiJBP0X+uEOnjwYI0SnemeggQnOiRXi1MQbM=

libtailscale/backend.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ type App struct {
5959
backend *ipnlocal.LocalBackend
6060
ready sync.WaitGroup
6161
backendMu sync.Mutex
62+
63+
// logger is the logtail logger whose uploads follow the user's
64+
// IsClientLoggingEnabled preference. Populated once runBackend wires
65+
// up the backend; nil before then.
66+
logger atomic.Pointer[logtail.Logger]
6267
}
6368

6469
func start(dataDir, directFileRoot string, hwAttestationPref bool, appCtx AppContext) Application {
@@ -142,6 +147,7 @@ func (a *App) runBackend(ctx context.Context, hardwareAttestation bool) error {
142147
return err
143148
}
144149
a.logIDPublicAtomic.Store(&b.logIDPublic)
150+
a.logger.Store(b.logger)
145151
a.backend = b.backend
146152
if hardwareAttestation {
147153
a.backend.SetHardwareAttested()

libtailscale/interfaces.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ type Application interface {
138138
// so it can re-read it via the [syspolicyHandler].
139139
NotifyPolicyChanged()
140140

141+
// SetClientLoggingEnabled sets whether diagnostic logs are uploaded to
142+
// Tailscale's logging backend. Changes take effect immediately.
143+
SetClientLoggingEnabled(enabled bool)
144+
141145
// WatchNotifications provides a mechanism for subscribing to ipn.Notify
142146
// updates. The given NotificationCallback's OnNotify function is invoked
143147
// on every new ipn.Notify message. The returned NotificationManager

libtailscale/localapi.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ func (app *App) NotifyPolicyChanged() {
111111
app.policyStore.notifyChanged()
112112
}
113113

114+
func (app *App) SetClientLoggingEnabled(enabled bool) {
115+
if lg := app.logger.Load(); lg != nil {
116+
lg.SetEnabled(enabled)
117+
}
118+
}
119+
114120
func (app *App) EditPrefs(prefs ipn.MaskedPrefs) (LocalAPIResponse, error) {
115121
r, w := io.Pipe()
116122
go func() {

libtailscale/tailscale.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ func (b *backend) setupLogs(logDir string, logID logid.PrivateID, logf logger.Lo
130130
IncludeProcSequence: true,
131131
HTTPC: &http.Client{Transport: transport},
132132
CompressLogs: true,
133+
// Start the logger disabled if the user opted out, so not even
134+
// the internal "logtail started" banner reaches the server. The
135+
// SetClientLoggingEnabled path flips this at runtime.
136+
Disabled: !enableUpload,
133137
}
134138
logcfg.FlushDelayFn = func() time.Duration { return 2 * time.Minute }
135139

@@ -144,10 +148,8 @@ func (b *backend) setupLogs(logDir string, logID logid.PrivateID, logf logger.Lo
144148
}
145149

146150
b.logger = logtail.NewLogger(logcfg, logf)
147-
148151
if !enableUpload {
149-
log.Printf("disabling remote log upload")
150-
logtail.Disable()
152+
log.Printf("remote log upload disabled by user preference")
151153
}
152154

153155
log.SetFlags(0)

0 commit comments

Comments
 (0)