From 182260703db7938f23fc2811d3bf936c89258a33 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Fri, 15 May 2026 16:43:15 +0200 Subject: [PATCH 1/8] Harden stealth logging and telemetry defaults --- lantern-core/core.go | 8 ++- lantern-core/ffi/ffi.go | 2 +- lantern-core/logging.go | 2 +- lantern-core/mobile/mobile.go | 8 +-- lantern-core/stealth.go | 99 +++++++++++++++++++++++++++++++++++ lantern-core/stealth_test.go | 86 ++++++++++++++++++++++++++++++ 6 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 lantern-core/stealth.go create mode 100644 lantern-core/stealth_test.go diff --git a/lantern-core/core.go b/lantern-core/core.go index 7b2cf30f21..f86ee2ee18 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -35,8 +35,7 @@ type EventType = string const ( EventTypeServerLocation EventType = "server-location" EventTypeConfig EventType = "config" - EventTypeCountryCode EventType = "country-code" - DefaultLogLevel = "trace" + EventTypeCountryCode EventType = "country-code" ) // LanternCore wraps an IPC client and provides the interface expected by the FFI and mobile layers. @@ -181,9 +180,8 @@ func New(opts *utils.Opts, eventEmitter utils.FlutterEventEmitter) (Core, error) } core.initOnce.Do(func() { - if opts.LogLevel == "" { - opts.LogLevel = DefaultLogLevel - } + opts.LogLevel = EffectiveLogLevel(opts.LogLevel) + opts.TelemetryConsent = EffectiveTelemetryConsent(opts.TelemetryConsent) slog.Debug("Initializing LanternCore with opts: ", "opts", opts) if err := core.initialize(opts, eventEmitter); err != nil { initError.Store(&err) diff --git a/lantern-core/ffi/ffi.go b/lantern-core/ffi/ffi.go index d10403cb72..4bb0129372 100644 --- a/lantern-core/ffi/ffi.go +++ b/lantern-core/ffi/ffi.go @@ -115,7 +115,7 @@ func setup(_logDir, _dataDir, _locale, _env *C.char, logP, appsP, statusP, priva Locale: locale, Env: env, Deviceid: "", - LogLevel: lanterncore.DefaultLogLevel, + LogLevel: lanterncore.EffectiveLogLevel(""), TelemetryConsent: consent == 1, }, &ffiFlutterEventEmitter{}) diff --git a/lantern-core/logging.go b/lantern-core/logging.go index ca73c0ff82..bd2b2cd8e7 100644 --- a/lantern-core/logging.go +++ b/lantern-core/logging.go @@ -28,7 +28,7 @@ func setupAppLogging(logDir, level string) { return } if level == "" { - level = DefaultLogLevel + level = EffectiveLogLevel("") } logger := rlog.NewLogger(rlog.Config{ LogPath: filepath.Join(logDir, AppLogFileName), diff --git a/lantern-core/mobile/mobile.go b/lantern-core/mobile/mobile.go index 48f6427995..b35a1c27a8 100644 --- a/lantern-core/mobile/mobile.go +++ b/lantern-core/mobile/mobile.go @@ -124,7 +124,7 @@ func SetQAEnvOverrides(outboundSocks, tz string) error { // the early values silently override. func InitLogging(dataDir, logDir, logLevel string) error { _, err := utils.RunOffCgoStack(func() (struct{}, error) { - return struct{}{}, common.Init(dataDir, logDir, logLevel) + return struct{}{}, common.Init(dataDir, logDir, lanterncore.EffectiveLogLevel(logLevel)) }) return err } @@ -288,13 +288,15 @@ func StartIPCServer(platform utils.PlatformInterface, opts *utils.Opts) error { if ipcServer != nil { return struct{}{}, nil } + logLevel := lanterncore.EffectiveLogLevel(opts.LogLevel) + telemetryConsent := lanterncore.EffectiveTelemetryConsent(opts.TelemetryConsent) bopts := backend.Options{ DataDir: opts.DataDir, LogDir: opts.LogDir, Locale: opts.Locale, - LogLevel: opts.LogLevel, + LogLevel: logLevel, DeviceID: opts.Deviceid, - TelemetryConsent: opts.TelemetryConsent, + TelemetryConsent: telemetryConsent, PlatformInterface: platform, } be, err := backend.NewLocalBackend(context.Background(), bopts) diff --git a/lantern-core/stealth.go b/lantern-core/stealth.go new file mode 100644 index 0000000000..ff8f8826db --- /dev/null +++ b/lantern-core/stealth.go @@ -0,0 +1,99 @@ +package lanterncore + +import ( + "os" + "strings" +) + +const ( + // DefaultLogLevel preserves the existing non-stealth production behavior. + DefaultLogLevel = "trace" + + safeStealthLogLevel = "warn" +) + +var ( + // StealthBuild is set by release tooling with: + // + // -X github.com/getlantern/lantern/lantern-core.StealthBuild=true + // + // Runtime env fallbacks are kept for local validation until the full + // native stealth profile plumbing lands in getlantern/lantern#8763. + StealthBuild = "false" + + // StealthLogLevel is the Go/Radiance fallback log level for stealth builds. + // Empty and trace-level defaults are deliberately replaced by + // EffectiveLogLevel. + StealthLogLevel = safeStealthLogLevel + + // StealthTelemetryDefaultEnabled lets an explicit stealth build opt back in + // to telemetry. The default remains false. + StealthTelemetryDefaultEnabled = "false" +) + +func IsStealthBuild() bool { + return buildBool(firstNonEmpty( + os.Getenv("LANTERN_STEALTH_BUILD"), + os.Getenv("STEALTH_BUILD"), + StealthBuild, + )) +} + +func EffectiveLogLevel(configured string) string { + configured = strings.ToLower(strings.TrimSpace(configured)) + if !IsStealthBuild() { + if configured == "" { + return DefaultLogLevel + } + return configured + } + + if configured == "" || configured == "trace" { + return effectiveStealthLogLevel() + } + return configured +} + +func EffectiveTelemetryConsent(configured bool) bool { + if configured { + return true + } + if !IsStealthBuild() { + return false + } + return buildBool(firstNonEmpty( + os.Getenv("LANTERN_STEALTH_TELEMETRY_DEFAULT_ENABLED"), + os.Getenv("STEALTH_TELEMETRY_DEFAULT_ENABLED"), + StealthTelemetryDefaultEnabled, + )) +} + +func effectiveStealthLogLevel() string { + level := strings.ToLower(strings.TrimSpace(firstNonEmpty( + os.Getenv("LANTERN_STEALTH_LOG_LEVEL"), + os.Getenv("STEALTH_LOG_LEVEL"), + StealthLogLevel, + ))) + if level == "" || level == "trace" { + return safeStealthLogLevel + } + return level +} + +func firstNonEmpty(values ...string) string { + for _, value := range values { + if strings.TrimSpace(value) != "" { + return value + } + } + return "" +} + +func buildBool(value string) bool { + switch strings.ToLower(strings.TrimSpace(value)) { + case "1", "true", "yes", "y", "on": + return true + default: + return false + } +} diff --git a/lantern-core/stealth_test.go b/lantern-core/stealth_test.go new file mode 100644 index 0000000000..5460d8c8c3 --- /dev/null +++ b/lantern-core/stealth_test.go @@ -0,0 +1,86 @@ +package lanterncore + +import "testing" + +func TestEffectiveLogLevelNonStealthPreservesDefaultTrace(t *testing.T) { + t.Setenv("LANTERN_STEALTH_BUILD", "") + t.Setenv("STEALTH_BUILD", "") + origStealthBuild := StealthBuild + StealthBuild = "false" + t.Cleanup(func() { StealthBuild = origStealthBuild }) + + if got := EffectiveLogLevel(""); got != DefaultLogLevel { + t.Fatalf("EffectiveLogLevel(\"\") = %q, want %q", got, DefaultLogLevel) + } + if got := EffectiveLogLevel("debug"); got != "debug" { + t.Fatalf("EffectiveLogLevel(\"debug\") = %q, want debug", got) + } +} + +func TestEffectiveLogLevelStealthReplacesTraceDefault(t *testing.T) { + t.Setenv("STEALTH_BUILD", "true") + t.Setenv("STEALTH_LOG_LEVEL", "") + origStealthBuild := StealthBuild + origStealthLogLevel := StealthLogLevel + StealthBuild = "false" + StealthLogLevel = "warn" + t.Cleanup(func() { + StealthBuild = origStealthBuild + StealthLogLevel = origStealthLogLevel + }) + + for _, configured := range []string{"", "trace", "TRACE"} { + if got := EffectiveLogLevel(configured); got != safeStealthLogLevel { + t.Fatalf("EffectiveLogLevel(%q) = %q, want %q", configured, got, safeStealthLogLevel) + } + } + if got := EffectiveLogLevel("error"); got != "error" { + t.Fatalf("EffectiveLogLevel(\"error\") = %q, want error", got) + } +} + +func TestEffectiveLogLevelStealthRejectsTraceFallback(t *testing.T) { + t.Setenv("STEALTH_BUILD", "true") + t.Setenv("STEALTH_LOG_LEVEL", "trace") + origStealthBuild := StealthBuild + origStealthLogLevel := StealthLogLevel + StealthBuild = "false" + StealthLogLevel = "trace" + t.Cleanup(func() { + StealthBuild = origStealthBuild + StealthLogLevel = origStealthLogLevel + }) + + if got := EffectiveLogLevel(""); got != safeStealthLogLevel { + t.Fatalf("EffectiveLogLevel(\"\") = %q, want %q", got, safeStealthLogLevel) + } +} + +func TestEffectiveTelemetryConsent(t *testing.T) { + origStealthBuild := StealthBuild + origTelemetryDefault := StealthTelemetryDefaultEnabled + t.Cleanup(func() { + StealthBuild = origStealthBuild + StealthTelemetryDefaultEnabled = origTelemetryDefault + }) + + StealthBuild = "false" + t.Setenv("STEALTH_BUILD", "") + if got := EffectiveTelemetryConsent(false); got { + t.Fatal("non-stealth false consent should remain false") + } + if got := EffectiveTelemetryConsent(true); !got { + t.Fatal("explicit true consent should remain true") + } + + StealthBuild = "true" + StealthTelemetryDefaultEnabled = "false" + if got := EffectiveTelemetryConsent(false); got { + t.Fatal("stealth false consent should default to false") + } + + StealthTelemetryDefaultEnabled = "true" + if got := EffectiveTelemetryConsent(false); !got { + t.Fatal("stealth telemetry build default should allow explicit opt-in default") + } +} From 001b747936f40f0f410a453c7aa192fe0b49f064 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Fri, 15 May 2026 17:44:05 +0200 Subject: [PATCH 2/8] Address review feedback for stealth defaults --- lantern-core/stealth.go | 23 +++++++-------------- lantern-core/stealth_test.go | 39 +++++++++++++++++++++++++++--------- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lantern-core/stealth.go b/lantern-core/stealth.go index ff8f8826db..b01671f1a5 100644 --- a/lantern-core/stealth.go +++ b/lantern-core/stealth.go @@ -25,17 +25,15 @@ var ( // Empty and trace-level defaults are deliberately replaced by // EffectiveLogLevel. StealthLogLevel = safeStealthLogLevel - - // StealthTelemetryDefaultEnabled lets an explicit stealth build opt back in - // to telemetry. The default remains false. - StealthTelemetryDefaultEnabled = "false" ) func IsStealthBuild() bool { + if buildBool(StealthBuild) { + return true + } return buildBool(firstNonEmpty( os.Getenv("LANTERN_STEALTH_BUILD"), os.Getenv("STEALTH_BUILD"), - StealthBuild, )) } @@ -55,17 +53,10 @@ func EffectiveLogLevel(configured string) string { } func EffectiveTelemetryConsent(configured bool) bool { - if configured { - return true - } - if !IsStealthBuild() { - return false - } - return buildBool(firstNonEmpty( - os.Getenv("LANTERN_STEALTH_TELEMETRY_DEFAULT_ENABLED"), - os.Getenv("STEALTH_TELEMETRY_DEFAULT_ENABLED"), - StealthTelemetryDefaultEnabled, - )) + // The current platform callers pass a stored consent bool, not a tri-state + // value. Preserve explicit user opt-out until callers can distinguish + // "unset" from "disabled" before applying any build default. + return configured } func effectiveStealthLogLevel() string { diff --git a/lantern-core/stealth_test.go b/lantern-core/stealth_test.go index 5460d8c8c3..f077a83f39 100644 --- a/lantern-core/stealth_test.go +++ b/lantern-core/stealth_test.go @@ -2,9 +2,17 @@ package lanterncore import "testing" -func TestEffectiveLogLevelNonStealthPreservesDefaultTrace(t *testing.T) { +func clearStealthEnv(t *testing.T) { t.Setenv("LANTERN_STEALTH_BUILD", "") t.Setenv("STEALTH_BUILD", "") + t.Setenv("LANTERN_STEALTH_LOG_LEVEL", "") + t.Setenv("STEALTH_LOG_LEVEL", "") + t.Setenv("LANTERN_STEALTH_TELEMETRY_DEFAULT_ENABLED", "") + t.Setenv("STEALTH_TELEMETRY_DEFAULT_ENABLED", "") +} + +func TestEffectiveLogLevelNonStealthPreservesDefaultTrace(t *testing.T) { + clearStealthEnv(t) origStealthBuild := StealthBuild StealthBuild = "false" t.Cleanup(func() { StealthBuild = origStealthBuild }) @@ -18,6 +26,7 @@ func TestEffectiveLogLevelNonStealthPreservesDefaultTrace(t *testing.T) { } func TestEffectiveLogLevelStealthReplacesTraceDefault(t *testing.T) { + clearStealthEnv(t) t.Setenv("STEALTH_BUILD", "true") t.Setenv("STEALTH_LOG_LEVEL", "") origStealthBuild := StealthBuild @@ -40,6 +49,7 @@ func TestEffectiveLogLevelStealthReplacesTraceDefault(t *testing.T) { } func TestEffectiveLogLevelStealthRejectsTraceFallback(t *testing.T) { + clearStealthEnv(t) t.Setenv("STEALTH_BUILD", "true") t.Setenv("STEALTH_LOG_LEVEL", "trace") origStealthBuild := StealthBuild @@ -56,16 +66,27 @@ func TestEffectiveLogLevelStealthRejectsTraceFallback(t *testing.T) { } } +func TestIsStealthBuildLinkedValueCannotBeDisabledByEnv(t *testing.T) { + clearStealthEnv(t) + t.Setenv("LANTERN_STEALTH_BUILD", "false") + t.Setenv("STEALTH_BUILD", "false") + origStealthBuild := StealthBuild + StealthBuild = "true" + t.Cleanup(func() { StealthBuild = origStealthBuild }) + + if !IsStealthBuild() { + t.Fatal("linked stealth build should remain enabled despite false env fallbacks") + } +} + func TestEffectiveTelemetryConsent(t *testing.T) { + clearStealthEnv(t) origStealthBuild := StealthBuild - origTelemetryDefault := StealthTelemetryDefaultEnabled t.Cleanup(func() { StealthBuild = origStealthBuild - StealthTelemetryDefaultEnabled = origTelemetryDefault }) StealthBuild = "false" - t.Setenv("STEALTH_BUILD", "") if got := EffectiveTelemetryConsent(false); got { t.Fatal("non-stealth false consent should remain false") } @@ -74,13 +95,13 @@ func TestEffectiveTelemetryConsent(t *testing.T) { } StealthBuild = "true" - StealthTelemetryDefaultEnabled = "false" if got := EffectiveTelemetryConsent(false); got { - t.Fatal("stealth false consent should default to false") + t.Fatal("stealth false consent should preserve explicit opt-out") } - StealthTelemetryDefaultEnabled = "true" - if got := EffectiveTelemetryConsent(false); !got { - t.Fatal("stealth telemetry build default should allow explicit opt-in default") + t.Setenv("LANTERN_STEALTH_TELEMETRY_DEFAULT_ENABLED", "true") + t.Setenv("STEALTH_TELEMETRY_DEFAULT_ENABLED", "true") + if got := EffectiveTelemetryConsent(false); got { + t.Fatal("stealth telemetry env defaults must not override explicit opt-out") } } From b0b11bf8cd546c661eacb8fbd60f07d8a1e9684c Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Sat, 16 May 2026 13:43:38 +0200 Subject: [PATCH 3/8] fix: pass stealth flags to Go builds --- Makefile | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 1c964e7dbd..a8861ff042 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,10 @@ ifeq ($(strip $(APP_VERSION_PUBSPEC)),) $(error APP_VERSION_PUBSPEC is empty; export APP_VERSION (e.g. "9.0.25+459") or ensure pubspec.yaml contains a `version:` line) endif EXTRA_LDFLAGS ?= -X '$(RADIANCE_REPO)/common.Version=$(APP_VERSION_PUBSPEC)' +STEALTH_GO_IMPORT_PATH := github.com/getlantern/lantern/lantern-core +STEALTH_GO_LOG_LEVEL ?= warn +STEALTH_GO_LDFLAGS := $(if $(filter stealth stealth-%,$(BUILD_TYPE)),-X '$(STEALTH_GO_IMPORT_PATH).StealthBuild=true' -X '$(STEALTH_GO_IMPORT_PATH).StealthLogLevel=$(STEALTH_GO_LOG_LEVEL)') +GO_EXTRA_LDFLAGS := $(strip $(EXTRA_LDFLAGS) $(STEALTH_GO_LDFLAGS)) DARWIN_APP_NAME := $(CAPITALIZED_APP).app DARWIN_LIB := $(LANTERN_LIB_NAME).dylib @@ -205,7 +209,7 @@ endif desktop-lib: $(SETENV) go build -v -trimpath -buildmode=c-shared \ -tags="$(TAGS)" \ - -ldflags="-w -s $(EXTRA_LDFLAGS)" \ + -ldflags="-w -s $(GO_EXTRA_LDFLAGS)" \ -o $(LIB_NAME) ./$(FFI_DIR) @echo "Built desktop library: $(LIB_NAME)" @@ -229,7 +233,7 @@ $(MACOS_FRAMEWORK_BUILD): $(GO_SOURCES) -tags=$(TAGS),netgo -trimpath \ -target=macos \ -o $(MACOS_FRAMEWORK_BUILD) \ - -ldflags="-w -s -checklinkname=0 $(EXTRA_LDFLAGS)" \ + -ldflags="-w -s -checklinkname=0 $(GO_EXTRA_LDFLAGS)" \ $(GOMOBILE_REPOS) @echo "Built macOS Framework: $(MACOS_FRAMEWORK_BUILD)" rm -rf $(MACOS_FRAMEWORK_DIR)/$(MACOS_FRAMEWORK) @@ -331,7 +335,7 @@ lanternd-linux-amd64: $(GO_SOURCES) $(call MKDIR_P,$(dir $(LANTERND_LINUX_AMD64))) GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "-w -s $(EXTRA_LDFLAGS)" \ + -ldflags "-w -s $(GO_EXTRA_LDFLAGS)" \ -o $(LANTERND_LINUX_AMD64) $(LANTERND_SRC) @echo "Built lanternd (linux-amd64): $(LANTERND_LINUX_AMD64)" @@ -339,7 +343,7 @@ lanternd-linux-arm64: $(GO_SOURCES) $(call MKDIR_P,$(dir $(LANTERND_LINUX_ARM64))) GOOS=linux GOARCH=arm64 CGO_ENABLED=1 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "-w -s $(EXTRA_LDFLAGS)" \ + -ldflags "-w -s $(GO_EXTRA_LDFLAGS)" \ -o $(LANTERND_LINUX_ARM64) $(LANTERND_SRC) @echo "Built lanternd (linux-arm64): $(LANTERND_LINUX_ARM64)" @@ -411,7 +415,7 @@ $(LANTERND_WINDOWS_AMD64): $(call MKDIR_P,$(dir $(LANTERND_WINDOWS_AMD64))) GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "$(EXTRA_LDFLAGS)" \ + -ldflags "$(GO_EXTRA_LDFLAGS)" \ -o $(LANTERND_WINDOWS_AMD64) $(LANTERND_SRC) @echo "Built lanternd (windows-amd64): $(LANTERND_WINDOWS_AMD64)" @@ -419,7 +423,7 @@ $(LANTERND_WINDOWS_ARM64): $(call MKDIR_P,$(dir $(LANTERND_WINDOWS_ARM64))) GOOS=windows GOARCH=arm64 CGO_ENABLED=0 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "$(EXTRA_LDFLAGS)" \ + -ldflags "$(GO_EXTRA_LDFLAGS)" \ -o $(LANTERND_WINDOWS_ARM64) $(LANTERND_SRC) @echo "Built lanternd (windows-arm64): $(LANTERND_WINDOWS_ARM64)" @@ -504,7 +508,7 @@ build-android: check-android-sdk check-gomobile -javapkg=lantern.io \ -tags=$(TAGS) -trimpath \ -o=$(ANDROID_LIB_BUILD) \ - -ldflags="$(ANDROID_GOMOBILE_LDFLAGS) $(EXTRA_LDFLAGS)" \ + -ldflags="$(ANDROID_GOMOBILE_LDFLAGS) $(GO_EXTRA_LDFLAGS)" \ $(GOMOBILE_REPOS) cp $(ANDROID_LIB_BUILD) $(ANDROID_LIBS_DIR) @@ -573,7 +577,7 @@ build-ios: -tags=$(TAGS),with_low_memory, -trimpath \ -target=ios \ -o $(IOS_FRAMEWORK_BUILD) \ - -ldflags="-w -s -checklinkname=0 $(EXTRA_LDFLAGS)" \ + -ldflags="-w -s -checklinkname=0 $(GO_EXTRA_LDFLAGS)" \ $(GOMOBILE_REPOS) @echo "Built iOS Framework: $(IOS_FRAMEWORK_BUILD)" mv $(IOS_FRAMEWORK_BUILD) $(IOS_FRAMEWORK_DIR) From 6ac22ee912826ceea3972da2a6ad5f05e8743348 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Sat, 16 May 2026 13:52:45 +0200 Subject: [PATCH 4/8] fix: let Linux stealth daemon use log default --- linux/packaging/arch/postinstall.sh | 2 +- linux/packaging/deb/scripts/postinst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/linux/packaging/arch/postinstall.sh b/linux/packaging/arch/postinstall.sh index 07625d8018..2b869746cb 100755 --- a/linux/packaging/arch/postinstall.sh +++ b/linux/packaging/arch/postinstall.sh @@ -1,4 +1,4 @@ #!/bin/sh set -e -/usr/lib/lantern/lanternd install --log-level=trace >/dev/null 2>&1 || true +/usr/lib/lantern/lanternd install >/dev/null 2>&1 || true diff --git a/linux/packaging/deb/scripts/postinst b/linux/packaging/deb/scripts/postinst index 439d5a950d..2b9726996f 100755 --- a/linux/packaging/deb/scripts/postinst +++ b/linux/packaging/deb/scripts/postinst @@ -7,7 +7,7 @@ warn() { case "$1" in configure) - if ! /usr/lib/lantern/lanternd install --log-level=trace; then + if ! /usr/lib/lantern/lanternd install; then warn "failed to install lanternd service" exit 1 fi From 01672963ff0455a077066668bc2a9c59cb13ef09 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Sat, 16 May 2026 14:01:47 +0200 Subject: [PATCH 5/8] fix: keep daemon ldflags scoped --- Makefile | 9 +++++---- linux/packaging/arch/postinstall.sh | 2 +- linux/packaging/deb/scripts/postinst | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a8861ff042..aa6729ac7f 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,7 @@ STEALTH_GO_IMPORT_PATH := github.com/getlantern/lantern/lantern-core STEALTH_GO_LOG_LEVEL ?= warn STEALTH_GO_LDFLAGS := $(if $(filter stealth stealth-%,$(BUILD_TYPE)),-X '$(STEALTH_GO_IMPORT_PATH).StealthBuild=true' -X '$(STEALTH_GO_IMPORT_PATH).StealthLogLevel=$(STEALTH_GO_LOG_LEVEL)') GO_EXTRA_LDFLAGS := $(strip $(EXTRA_LDFLAGS) $(STEALTH_GO_LDFLAGS)) +LANTERND_EXTRA_LDFLAGS := $(EXTRA_LDFLAGS) DARWIN_APP_NAME := $(CAPITALIZED_APP).app DARWIN_LIB := $(LANTERN_LIB_NAME).dylib @@ -335,7 +336,7 @@ lanternd-linux-amd64: $(GO_SOURCES) $(call MKDIR_P,$(dir $(LANTERND_LINUX_AMD64))) GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "-w -s $(GO_EXTRA_LDFLAGS)" \ + -ldflags "-w -s $(LANTERND_EXTRA_LDFLAGS)" \ -o $(LANTERND_LINUX_AMD64) $(LANTERND_SRC) @echo "Built lanternd (linux-amd64): $(LANTERND_LINUX_AMD64)" @@ -343,7 +344,7 @@ lanternd-linux-arm64: $(GO_SOURCES) $(call MKDIR_P,$(dir $(LANTERND_LINUX_ARM64))) GOOS=linux GOARCH=arm64 CGO_ENABLED=1 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "-w -s $(GO_EXTRA_LDFLAGS)" \ + -ldflags "-w -s $(LANTERND_EXTRA_LDFLAGS)" \ -o $(LANTERND_LINUX_ARM64) $(LANTERND_SRC) @echo "Built lanternd (linux-arm64): $(LANTERND_LINUX_ARM64)" @@ -415,7 +416,7 @@ $(LANTERND_WINDOWS_AMD64): $(call MKDIR_P,$(dir $(LANTERND_WINDOWS_AMD64))) GOOS=windows GOARCH=amd64 CGO_ENABLED=0 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "$(GO_EXTRA_LDFLAGS)" \ + -ldflags "$(LANTERND_EXTRA_LDFLAGS)" \ -o $(LANTERND_WINDOWS_AMD64) $(LANTERND_SRC) @echo "Built lanternd (windows-amd64): $(LANTERND_WINDOWS_AMD64)" @@ -423,7 +424,7 @@ $(LANTERND_WINDOWS_ARM64): $(call MKDIR_P,$(dir $(LANTERND_WINDOWS_ARM64))) GOOS=windows GOARCH=arm64 CGO_ENABLED=0 \ go build -mod=mod -v -trimpath -tags "$(TAGS)" \ - -ldflags "$(GO_EXTRA_LDFLAGS)" \ + -ldflags "$(LANTERND_EXTRA_LDFLAGS)" \ -o $(LANTERND_WINDOWS_ARM64) $(LANTERND_SRC) @echo "Built lanternd (windows-arm64): $(LANTERND_WINDOWS_ARM64)" diff --git a/linux/packaging/arch/postinstall.sh b/linux/packaging/arch/postinstall.sh index 2b869746cb..07625d8018 100755 --- a/linux/packaging/arch/postinstall.sh +++ b/linux/packaging/arch/postinstall.sh @@ -1,4 +1,4 @@ #!/bin/sh set -e -/usr/lib/lantern/lanternd install >/dev/null 2>&1 || true +/usr/lib/lantern/lanternd install --log-level=trace >/dev/null 2>&1 || true diff --git a/linux/packaging/deb/scripts/postinst b/linux/packaging/deb/scripts/postinst index 2b9726996f..439d5a950d 100755 --- a/linux/packaging/deb/scripts/postinst +++ b/linux/packaging/deb/scripts/postinst @@ -7,7 +7,7 @@ warn() { case "$1" in configure) - if ! /usr/lib/lantern/lanternd install; then + if ! /usr/lib/lantern/lanternd install --log-level=trace; then warn "failed to install lanternd service" exit 1 fi From 73b80a2cdfb84e43f3446e54415e062f594f9ce9 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Sat, 16 May 2026 14:09:25 +0200 Subject: [PATCH 6/8] fix: pass stealth log level to daemon installers --- Makefile | 3 +++ linux/packaging/arch/postinstall.sh | 3 ++- linux/packaging/deb/scripts/postinst | 3 ++- windows/packaging/exe/inno_setup.iss | 14 +++++++++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index aa6729ac7f..1560fed31e 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,7 @@ LINUX_INSTALLER_RPM := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYP LINUX_INSTALLER_ARCH := $(INSTALLER_NAME)$(if $(filter-out production,$(BUILD_TYPE)),-$(BUILD_TYPE))$(LINUX_PACKAGE_ARCH_SUFFIX).pkg.tar.zst LANTERND := lanternd LANTERND_SRC := $(RADIANCE_REPO)/cmd/lanternd +LANTERND_SERVICE_LOG_LEVEL ?= $(if $(filter stealth stealth-%,$(BUILD_TYPE)),warn,trace) LANTERND_LINUX_AMD64 := $(BIN_DIR)/linux-amd64/$(LANTERND) LANTERND_LINUX_ARM64 := $(BIN_DIR)/linux-arm64/$(LANTERND) LINUX_BUNDLE_DIR_X64 := build/linux/x64/release/bundle @@ -373,6 +374,7 @@ linux-release-ci: linux pubget gen echo "Using Linux bundle dir: $$BUNDLE_DIR"; \ cp "$(LINUX_LIB_BUILD)" "$$BUNDLE_DIR"; \ cp "$(BIN_DIR)/linux-$(LINUX_TARGET_ARCH)/$(LANTERND)" "$$BUNDLE_DIR"; \ + printf '%s\n' "$(LANTERND_SERVICE_LOG_LEVEL)" > "$$BUNDLE_DIR/lanternd-log-level"; \ patchelf --set-rpath '$$ORIGIN/lib' "$$BUNDLE_DIR/lantern" || true; \ VERSION=$(APP_VERSION) GOARCH=$(LINUX_TARGET_ARCH) LINUX_BUNDLE_SRC="$$BUNDLE_DIR/" \ nfpm package -f $(LINUX_PKG_ROOT)/nfpm.yaml -p deb -t $(LINUX_INSTALLER_DEB); \ @@ -444,6 +446,7 @@ copy-lanternd-debug: $(LANTERND_WINDOWS_AMD64) prepare-windows-release: lanternd-windows-amd64 lanternd-windows-arm64 $(MAKE) copy-lanternd-release $(MAKE) copy-lanternd-release-arm64 + printf '%s\n' "$(LANTERND_SERVICE_LOG_LEVEL)" > "$(WINDOWS_RELEASE_DIR)/lanternd-log-level" .PHONY: windows-debug windows-debug: windows diff --git a/linux/packaging/arch/postinstall.sh b/linux/packaging/arch/postinstall.sh index 07625d8018..c7c0e84611 100755 --- a/linux/packaging/arch/postinstall.sh +++ b/linux/packaging/arch/postinstall.sh @@ -1,4 +1,5 @@ #!/bin/sh set -e -/usr/lib/lantern/lanternd install --log-level=trace >/dev/null 2>&1 || true +LOG_LEVEL="$(cat /usr/lib/lantern/lanternd-log-level 2>/dev/null || printf '%s' trace)" +/usr/lib/lantern/lanternd install --log-level="$LOG_LEVEL" >/dev/null 2>&1 || true diff --git a/linux/packaging/deb/scripts/postinst b/linux/packaging/deb/scripts/postinst index 439d5a950d..c51ef2df73 100755 --- a/linux/packaging/deb/scripts/postinst +++ b/linux/packaging/deb/scripts/postinst @@ -7,7 +7,8 @@ warn() { case "$1" in configure) - if ! /usr/lib/lantern/lanternd install --log-level=trace; then + LOG_LEVEL="$(cat /usr/lib/lantern/lanternd-log-level 2>/dev/null || printf '%s' trace)" + if ! /usr/lib/lantern/lanternd install --log-level="$LOG_LEVEL"; then warn "failed to install lanternd service" exit 1 fi diff --git a/windows/packaging/exe/inno_setup.iss b/windows/packaging/exe/inno_setup.iss index 6fb9351682..24d4b99c76 100644 --- a/windows/packaging/exe/inno_setup.iss +++ b/windows/packaging/exe/inno_setup.iss @@ -309,7 +309,7 @@ Name: "{autodesktop}\\{{DISPLAY_NAME}}"; Filename: "{app}\\{{EXECUTABLE_NAME}}"; [Run] ; Install LanternSvc service (creates Windows service, sets recovery actions, starts it) -Filename: "{code:LanterndExecutablePath}"; Parameters: "install"; Flags: runhidden +Filename: "{code:LanterndExecutablePath}"; Parameters: "install --log-level={code:LanterndInstallLogLevel}"; Flags: runhidden ; Launch Lantern app UI Filename: "{app}\{{EXECUTABLE_NAME}}"; Description: "{cm:LaunchProgram,{{DISPLAY_NAME}}}"; \ @@ -483,6 +483,18 @@ begin Result := ExpandConstant('{app}\lanternd.exe'); end; +function LanterndInstallLogLevel(_Param: String): String; +var + LogLevel: String; +begin + Result := 'trace'; + if LoadStringFromFile(ExpandConstant('{app}\lanternd-log-level'), LogLevel) then begin + LogLevel := Trim(LogLevel); + if LogLevel <> '' then + Result := LogLevel; + end; +end; + function InitializeSetup: Boolean; begin RemoveStaleUninstallEntry(HKLM, 'HKLM'); From 6b2ee80709bb41664a5107598eaa839d73706643 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Sat, 16 May 2026 15:11:39 +0200 Subject: [PATCH 7/8] fix: write Windows daemon log level portably --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1560fed31e..c7d285f176 100644 --- a/Makefile +++ b/Makefile @@ -87,10 +87,12 @@ ifeq ($(OS),Windows_NT) MKDIR_P = $(PS) "New-Item -ItemType Directory -Force -Path '$(1)' | Out-Null" COPY_FILE = $(PS) "Copy-Item -Force -LiteralPath '$(1)' -Destination '$(2)'" RM_RF = $(PS) "Remove-Item -Recurse -Force -LiteralPath '$(1)'" + WRITE_TEXT_FILE = $(PS) "Set-Content -LiteralPath '$(2)' -Value '$(1)' -Encoding ASCII" else MKDIR_P = mkdir -p -- '$(1)' COPY_FILE = cp -f -- '$(1)' '$(2)' RM_RF = rm -rf -- '$(1)' + WRITE_TEXT_FILE = printf '%s\n' '$(1)' > '$(2)' endif LANTERND_WINDOWS_AMD64 := $(BIN_DIR)/windows-amd64/$(LANTERND).exe @@ -446,7 +448,7 @@ copy-lanternd-debug: $(LANTERND_WINDOWS_AMD64) prepare-windows-release: lanternd-windows-amd64 lanternd-windows-arm64 $(MAKE) copy-lanternd-release $(MAKE) copy-lanternd-release-arm64 - printf '%s\n' "$(LANTERND_SERVICE_LOG_LEVEL)" > "$(WINDOWS_RELEASE_DIR)/lanternd-log-level" + $(call WRITE_TEXT_FILE,$(LANTERND_SERVICE_LOG_LEVEL),$(WINDOWS_RELEASE_DIR)/lanternd-log-level) .PHONY: windows-debug windows-debug: windows From 541e2a89071de4cfa5a6bf4127469a71dfe557d2 Mon Sep 17 00:00:00 2001 From: Ilya Yakelzon Date: Thu, 11 Jun 2026 12:59:31 +0200 Subject: [PATCH 8/8] fix: harden telemetry consent and fix gofmt alignment EffectiveTelemetryConsent now hard-disables telemetry for stealth builds regardless of any prior user opt-in. Telemetry is a deanonymization surface and must not be emitted from stealth artifacts. Also fix gofmt alignment in core.go const block (EventTypeCountryCode tab alignment was off after rebase). Co-Authored-By: Claude Opus 4.8 --- lantern-core/core.go | 2 +- lantern-core/stealth.go | 13 ++++++++++--- lantern-core/stealth_test.go | 13 ++++++------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lantern-core/core.go b/lantern-core/core.go index f86ee2ee18..2a61b3a89b 100644 --- a/lantern-core/core.go +++ b/lantern-core/core.go @@ -35,7 +35,7 @@ type EventType = string const ( EventTypeServerLocation EventType = "server-location" EventTypeConfig EventType = "config" - EventTypeCountryCode EventType = "country-code" + EventTypeCountryCode EventType = "country-code" ) // LanternCore wraps an IPC client and provides the interface expected by the FFI and mobile layers. diff --git a/lantern-core/stealth.go b/lantern-core/stealth.go index b01671f1a5..9a710bf08a 100644 --- a/lantern-core/stealth.go +++ b/lantern-core/stealth.go @@ -52,10 +52,17 @@ func EffectiveLogLevel(configured string) string { return configured } +// EffectiveTelemetryConsent returns the telemetry consent that should be used +// after applying stealth-build policy. +// +// For stealth builds telemetry is unconditionally disabled regardless of any +// prior user opt-in: telemetry is a deanonymization surface and must not be +// emitted from a stealth artifact. For normal builds the caller-supplied +// consent value is returned unchanged. func EffectiveTelemetryConsent(configured bool) bool { - // The current platform callers pass a stored consent bool, not a tri-state - // value. Preserve explicit user opt-out until callers can distinguish - // "unset" from "disabled" before applying any build default. + if IsStealthBuild() { + return false + } return configured } diff --git a/lantern-core/stealth_test.go b/lantern-core/stealth_test.go index f077a83f39..e1eef8454a 100644 --- a/lantern-core/stealth_test.go +++ b/lantern-core/stealth_test.go @@ -86,22 +86,21 @@ func TestEffectiveTelemetryConsent(t *testing.T) { StealthBuild = origStealthBuild }) + // Non-stealth: caller value passes through unchanged. StealthBuild = "false" if got := EffectiveTelemetryConsent(false); got { t.Fatal("non-stealth false consent should remain false") } if got := EffectiveTelemetryConsent(true); !got { - t.Fatal("explicit true consent should remain true") + t.Fatal("non-stealth true consent should remain true") } + // Stealth: telemetry hard-disabled regardless of caller value. StealthBuild = "true" if got := EffectiveTelemetryConsent(false); got { - t.Fatal("stealth false consent should preserve explicit opt-out") + t.Fatal("stealth false consent should remain false") } - - t.Setenv("LANTERN_STEALTH_TELEMETRY_DEFAULT_ENABLED", "true") - t.Setenv("STEALTH_TELEMETRY_DEFAULT_ENABLED", "true") - if got := EffectiveTelemetryConsent(false); got { - t.Fatal("stealth telemetry env defaults must not override explicit opt-out") + if got := EffectiveTelemetryConsent(true); got { + t.Fatal("stealth prior opt-in must be overridden to false (telemetry is a deanonymization surface)") } }