Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remediate TunnelVision, TunnelCrack and fix "Exclude Local Networks" #3422

Merged
merged 37 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b874763
Fix TunnelVision
diegoreymendez Oct 20, 2024
bbd92a6
Merge branch 'main' into diego/fix-tunnelvision-2
diegoreymendez Oct 24, 2024
073a413
WIP
diegoreymendez Oct 24, 2024
ba7d70f
Adds support for internal user flagging in the VPN menu app
diegoreymendez Oct 25, 2024
a74fe70
Some changes so that the debug options that require restarting the ad…
diegoreymendez Oct 26, 2024
9bb0d46
Updates BSK
diegoreymendez Oct 28, 2024
0287acb
Wip
diegoreymendez Oct 30, 2024
5392154
WIP
diegoreymendez Oct 31, 2024
18bbb6a
WIP
diegoreymendez Oct 31, 2024
008b527
Code cleanup
diegoreymendez Nov 4, 2024
50a4d95
Merges the latest from main
diegoreymendez Nov 4, 2024
4cfd920
Fixes Package.resolved
diegoreymendez Nov 4, 2024
a165f63
Adds support for internal feature flag
diegoreymendez Nov 4, 2024
6b0f462
Adds support for debugging the new enforceRoutes changes
diegoreymendez Nov 4, 2024
967c7f6
Rolls back an unintentional change
diegoreymendez Nov 4, 2024
b85e77f
Updates BSK
diegoreymendez Nov 6, 2024
96461cd
Updates BSK
diegoreymendez Nov 6, 2024
5b6a0e7
Updates BSK
diegoreymendez Nov 6, 2024
037c71c
Merges the latest from main
diegoreymendez Nov 6, 2024
eeb2ee5
Updates BSK
diegoreymendez Nov 6, 2024
43c9f3f
Updates BSK
diegoreymendez Nov 6, 2024
6237f99
Updates BSK
diegoreymendez Nov 6, 2024
f241a15
Updates BSK
diegoreymendez Nov 7, 2024
83fdae3
Improves feature flags and VPN enforce routes code
diegoreymendez Nov 8, 2024
6049a95
Updates BSK
diegoreymendez Nov 8, 2024
8f916df
Updates BSK
diegoreymendez Nov 8, 2024
8cf37b6
Updates BSK
diegoreymendez Nov 8, 2024
8c244fd
Updates BSK
diegoreymendez Nov 8, 2024
651d977
Update LocalPackages/FeatureFlags/Sources/FeatureFlags/FeatureFlag.swift
diegoreymendez Nov 8, 2024
d916a8f
Rolls back an unintentional change
diegoreymendez Nov 8, 2024
e70c16d
Merge branch 'diego/fix-tunnelvision-2' of github.com:duckduckgo/maco…
diegoreymendez Nov 8, 2024
905c646
Addresses review feedback
diegoreymendez Nov 8, 2024
8af4626
Updates BSK
diegoreymendez Nov 11, 2024
d318da4
Updates BSK and merges the latest from main
diegoreymendez Nov 11, 2024
7a187df
Udpates BSK and fixes some tests
diegoreymendez Nov 11, 2024
08f890a
Updates BSK
diegoreymendez Nov 11, 2024
ad8fc82
Fixes a build issue
diegoreymendez Nov 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14683,8 +14683,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 203.1.0;
kind = revision;
revision = 432b5481e48671e2e644fe98e46cbeb512df0903;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "19f1e5c945aa92562ad2d087e8d6c99801edf656",
"version" : "203.1.0"
"revision" : "432b5481e48671e2e644fe98e46cbeb512df0903"
}
},
{
Expand Down
9 changes: 9 additions & 0 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private(set) var stateRestorationManager: AppStateRestorationManager!
private var grammarFeaturesManager = GrammarFeaturesManager()
let internalUserDecider: InternalUserDecider
private var isInternalUserSharingCancellable: AnyCancellable?
let featureFlagger: FeatureFlagger
private var appIconChanger: AppIconChanger!
private var autoClearHandler: AutoClearHandler!
Expand Down Expand Up @@ -429,6 +430,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {

subscribeToEmailProtectionStatusNotifications()
subscribeToDataImportCompleteNotification()
subscribeToInternalUserChanges()

fireFailedCompilationsPixelIfNeeded()

Expand Down Expand Up @@ -742,6 +744,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(dataImportCompleteNotification(_:)), name: .dataImportComplete, object: nil)
}

private func subscribeToInternalUserChanges() {
UserDefaults.appConfiguration.isInternalUser = internalUserDecider.isInternalUser

isInternalUserSharingCancellable = internalUserDecider.isInternalUserPublisher
.assign(to: \.isInternalUser, onWeaklyHeld: UserDefaults.appConfiguration)
samsymons marked this conversation as resolved.
Show resolved Hide resolved
}

private func emailDidSignInNotification(_ notification: Notification) {
PixelKit.fire(NonStandardEvent(NonStandardPixel.emailEnabled))
if AppDelegate.isNewUser {
Expand Down
5 changes: 5 additions & 0 deletions DuckDuckGo/FeatureFlagging/Model/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public enum FeatureFlag: String {

/// https://app.asana.com/0/72649045549333/1208231259093710/f
case networkProtectionUserTips

/// /// https://app.asana.com/0/72649045549333/1208617860225199/f
case networkProtectionEnforceRoutes
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New remote feature flag for the enforce routes changes.

}

extension FeatureFlag: FeatureFlagSourceProviding {
Expand All @@ -66,6 +69,8 @@ extension FeatureFlag: FeatureFlagSourceProviding {
return .remoteReleasable(.subfeature(AutofillSubfeature.credentialsImportPromotionForExistingUsers))
case .networkProtectionUserTips:
return .remoteDevelopment(.subfeature(NetworkProtectionSubfeature.userTips))
case .networkProtectionEnforceRoutes:
return .remoteDevelopment(.subfeature(NetworkProtectionSubfeature.enforceRoutes))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,23 @@ final class NetworkProtectionDebugMenu: NSMenu {
NSMenuItem(title: "Reset All State", action: #selector(NetworkProtectionDebugMenu.resetAllState))
.targetting(self)

NSMenuItem(title: "Reset Site Issue Alert", action: #selector(NetworkProtectionDebugMenu.resetSiteIssuesAlert(_:)))
NSMenuItem.separator() // Resetting single components should go below this point

NSMenuItem(title: "Remove Network Extension and Login Items", action: #selector(NetworkProtectionDebugMenu.removeSystemExtensionAndAgents))
.targetting(self)

NSMenuItem(title: "Remove VPN configuration", action: #selector(NetworkProtectionDebugMenu.removeVPNConfiguration(_:)))
.targetting(self)

resetToDefaults
.targetting(self)

NSMenuItem.separator()
NSMenuItem.separator() // Resetting VPN subfeatures should go below this point

NSMenuItem(title: "Remove Network Extension and Login Items", action: #selector(NetworkProtectionDebugMenu.removeSystemExtensionAndAgents))
NSMenuItem(title: "Reset Enforce Routes force-enabled flag", action: #selector(NetworkProtectionDebugMenu.resetEnforceRoutesForceEnabledFlag(_:)))
diegoreymendez marked this conversation as resolved.
Show resolved Hide resolved
.targetting(self)

NSMenuItem(title: "Remove VPN configuration", action: #selector(NetworkProtectionDebugMenu.removeVPNConfiguration(_:)))
NSMenuItem(title: "Reset Site Issue Alert", action: #selector(NetworkProtectionDebugMenu.resetSiteIssuesAlert(_:)))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was moved to organize the reset options: first go the general reset options, then single components, then subfeatures.

.targetting(self)
}

Expand Down Expand Up @@ -185,6 +190,10 @@ final class NetworkProtectionDebugMenu: NSMenu {
}
}

@objc func resetEnforceRoutesForceEnabledFlag(_ sender: Any?) {
settings.enforceRoutesForceEnabledOnce = false
}

@objc func resetSiteIssuesAlert(_ sender: Any?) {
debugUtilities.resetSiteIssuesAlert()
}
Expand Down Expand Up @@ -310,14 +319,29 @@ final class NetworkProtectionDebugMenu: NSMenu {

@objc func toggleEnforceRoutesAction(_ sender: Any?) {
settings.enforceRoutes.toggle()

Task {
try await Task.sleep(interval: 0.1)
try await debugUtilities.restartAdapter()
}
Comment on lines +316 to +319
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait a bit then restart the adapter so changes are applied. If we try to reset immediately the changes don't make it to the system extension in time.

}

@objc func toggleIncludeAllNetworks(_ sender: Any?) {
settings.includeAllNetworks.toggle()

Task {
try await Task.sleep(interval: 0.1)
try await debugUtilities.restartAdapter()
}
}

@objc func toggleShouldExcludeLocalRoutes(_ sender: Any?) {
settings.excludeLocalNetworks.toggle()

Task {
try await Task.sleep(interval: 0.1)
try await debugUtilities.restartAdapter()
}
}

@objc func openAppContainerInFinder(_ sender: Any?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,17 @@ import SystemExtensions
#endif

import Subscription
import BrowserServicesKit

typealias NetworkProtectionStatusChangeHandler = (NetworkProtection.ConnectionStatus) -> Void
typealias NetworkProtectionConfigChangeHandler = () -> Void

final class NetworkProtectionTunnelController: TunnelController, TunnelSessionProvider {

// MARK: - Settings
// MARK: - Configuration

private let internalUserDecider: InternalUserDecider
let settings: VPNSettings

// MARK: - Defaults

let defaults: UserDefaults

// MARK: - Combine Cancellables
Expand Down Expand Up @@ -154,11 +153,13 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
///
init(networkExtensionBundleID: String,
networkExtensionController: NetworkExtensionController,
internalUserDecider: InternalUserDecider,
settings: VPNSettings,
defaults: UserDefaults,
notificationCenter: NotificationCenter = .default,
accessTokenStorage: SubscriptionTokenKeychainStorage) {

self.internalUserDecider = internalUserDecider
self.networkExtensionBundleID = networkExtensionBundleID
self.networkExtensionController = networkExtensionController
self.notificationCenter = notificationCenter
Expand Down Expand Up @@ -295,8 +296,7 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
}

private func handleSetExcludeLocalNetworks(_ excludeLocalNetworks: Bool) async throws {
guard let tunnelManager = await manager,
tunnelManager.protocolConfiguration?.excludeLocalNetworks == !excludeLocalNetworks else {
guard let tunnelManager = await manager else {
samsymons marked this conversation as resolved.
Show resolved Hide resolved
return
}

Expand Down Expand Up @@ -349,23 +349,19 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
protocolConfiguration.providerBundleIdentifier = Bundle.tunnelExtensionBundleID
protocolConfiguration.providerConfiguration = [
NetworkProtectionOptionKey.defaultPixelHeaders: APIRequest.Headers().httpHeaders,
NetworkProtectionOptionKey.includedRoutes: includedRoutes().map(\.stringRepresentation) as NSArray
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're no longer going to pass routes. The extension will calculate the appropriate routes based on all the settings.

]

// always-on
protocolConfiguration.disconnectOnSleep = false

// kill switch
protocolConfiguration.enforceRoutes = settings.enforceRoutes
protocolConfiguration.enforceRoutes = enforceRoutes
samsymons marked this conversation as resolved.
Show resolved Hide resolved

// this setting breaks Connection Tester
protocolConfiguration.includeAllNetworks = settings.includeAllNetworks

// This is intentionally not used but left here for documentation purposes.
// The reason for this is that we want to have full control of the routes that
// are excluded, so instead of using this setting we're just configuring the
// excluded routes through our VPNSettings class, which our extension reads directly.
// protocolConfiguration.excludeLocalNetworks = settings.excludeLocalNetworks
// This messes up the routing, so please keep it always disabled
protocolConfiguration.excludeLocalNetworks = false

return protocolConfiguration
}()
Expand Down Expand Up @@ -618,6 +614,8 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
options[NetworkProtectionOptionKey.selectedEnvironment] = settings.selectedEnvironment.rawValue as NSString
options[NetworkProtectionOptionKey.selectedServer] = settings.selectedServer.stringValue as? NSString

options[NetworkProtectionOptionKey.excludeLocalNetworks] = NSNumber(value: settings.excludeLocalNetworks)
samsymons marked this conversation as resolved.
Show resolved Hide resolved

#if NETP_SYSTEM_EXTENSION
if let data = try? JSONEncoder().encode(settings.selectedLocation) {
options[NetworkProtectionOptionKey.selectedLocation] = NSData(data: data)
Expand Down Expand Up @@ -732,54 +730,25 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
try await tunnelManager.saveToPreferences()
}

/* Temporarily disabled until we fix this menu: https://app.asana.com/0/0/1205766100762904/f
samsymons marked this conversation as resolved.
Show resolved Hide resolved
@MainActor
private func excludedRoutes() -> [NetworkProtection.IPAddressRange] {
settings.excludedRoutes.compactMap { [excludedRoutesPreferences] item -> NetworkProtection.IPAddressRange? in
guard case .exclusion(range: let range, description: _, default: let defaultValue) = item,
excludedRoutesPreferences[range.stringRepresentation, default: defaultValue] == true
else { return nil }
// TO BE fixed:
// when 10.11.12.1 DNS is used 10.0.0.0/8 should be included (not excluded)
// but marking 10.11.12.1 as an Included Route breaks tunnel (probably these routes are conflicting)
if settings.enforceRoutes && range == "10.0.0.0/8" {
return nil
}
// MARK: - Routing

return range
}
}*/
private var enforceRoutes: Bool {

/// extra Included Routes appended to 0.0.0.0, ::/0 (peers) and interface.addresses
@MainActor
private func includedRoutes() -> [NetworkProtection.IPAddressRange] {
[]
}

/* Temporarily disabled - https://app.asana.com/0/0/1205766100762904/f
@MainActor
func setExcludedRoute(_ route: String, enabled: Bool) {
excludedRoutesPreferences[route] = enabled
}

@MainActor
func isExcludedRouteEnabled(_ route: String) -> Bool {
guard let range = IPAddressRange(from: route),
let exclusionListItem = settings.exclusionList.first(where: {
if case .exclusion(range: range, description: _, default: _) = $0 { return true }
return false
}),
case .exclusion(range: _, description: _, default: let defaultValue) = exclusionListItem else {

assertionFailure("Invalid route \(route)")
guard internalUserDecider.isInternalUser else {
return false
}
// TO BE fixed: see excludedRoutes()
if settings.enforceRoutes && route == "10.0.0.0/8" {
return false

/// Even though there's a remote feature flag, we still want to allow internal users to disable enforceRoutes
/// manually in case they have trouble. To achieve this we force the setting to ON once, but otherwise
/// allow the internal user to disable it again.
if !settings.enforceRoutesForceEnabledOnce {
settings.enforceRoutesForceEnabledOnce = true
settings.excludeLocalNetworks = true
settings.enforceRoutes = true
diegoreymendez marked this conversation as resolved.
Show resolved Hide resolved
}
return excludedRoutesPreferences[route, default: defaultValue]
}*/

return settings.enforceRoutes
}

struct TunnelFailureError: LocalizedError {
let errorDescription: String?
Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ final class VPNPreferencesModel: ObservableObject {
@Published var excludeLocalNetworks: Bool {
didSet {
settings.excludeLocalNetworks = excludeLocalNetworks

Task {
// We need to allow some time for the setting to propagate
// But ultimately this should actually be a user choice
try await Task.sleep(interval: 0.1)
try await vpnXPCClient.command(.restartAdapter)
}
}
Expand Down
3 changes: 3 additions & 0 deletions DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,13 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate {
return controller
}()

private let internalUserDecider = DefaultInternalUserDecider(store: UserDefaults.appConfiguration)
diegoreymendez marked this conversation as resolved.
Show resolved Hide resolved

@MainActor
private lazy var tunnelController = NetworkProtectionTunnelController(
networkExtensionBundleID: tunnelExtensionBundleID,
networkExtensionController: networkExtensionController,
internalUserDecider: internalUserDecider,
settings: tunnelSettings,
defaults: userDefaults,
accessTokenStorage: accessTokenStorage)
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging
return privacyConfig
}

public var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: InternalUserDeciderStoreMock())
public var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(
store: UserDefaults.appConfiguration)

@discardableResult
public func reload(etag: String?, data: Data?) -> PrivacyConfigurationManager.ReloadResult {
Expand Down
Loading