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" #1039

Merged
merged 25 commits into from
Nov 11, 2024

Conversation

diegoreymendez
Copy link
Contributor

@diegoreymendez diegoreymendez commented Oct 29, 2024

Task/Issue URL: https://app.asana.com/0/1206580121312550/1208686409805161/f
Tech Design URL: https://app.asana.com/0/481882893211075/1208643192597095/f

iOS PR: duckduckgo/iOS#3460
macOS PR: duckduckgo/macos-browser#3422
What kind of version bump will this require?: Major

Description

Remediate TunnelVision, TunnelCrack and fix "Exclude Local Networks".

Testing

Please refer to the platform-specific PRs for testing instructions.


Internal references:

Software Engineering Expectations
Technical Design Template

@diegoreymendez diegoreymendez changed the title Diego/fix tunnelvision 2 Fix TunnelVision using enforceRoutes Oct 29, 2024
import Combine
import Foundation

extension UserDefaults: InternalUserStoring {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

UserDefaults can now be used to store the internal user state so that agents and extensions can access said state.

@@ -19,7 +19,7 @@
import Foundation
import Network

public enum AnyIPAddress: IPAddress, Hashable, CustomDebugStringConvertible, @unchecked Sendable {
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 causing issues because some IP addresses contained by this were converting to empty strings. I'm removing the IPAddress protocol and consumers will instead need to call ipAddress on an instance of this class.

@@ -65,10 +65,8 @@ public protocol NetworkProtectionDeviceManagement {
typealias GenerateTunnelConfigurationResult = (tunnelConfiguration: TunnelConfiguration, server: NetworkProtectionServer)

func generateTunnelConfiguration(resolvedSelectionMethod: NetworkProtectionServerSelectionMethod,
includedRoutes: [IPAddressRange],
excludedRoutes: [IPAddressRange],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Included and excluded routes will no longer by passed by the client or by settings. They'll be calculated by the extension based on settings.

If we ever need to add support for custom IP ranges, we'll just add the appropriate overrides in settings but still calculate all the routing here.

includedRoutes: includedRoutes,
excludedRoutes: excludedRoutes,
dnsSettings: dnsSettings,
isKillSwitchEnabled: isKillSwitchEnabled)
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 bool is not necessary and not really useful. Enforce routes is sort of our kill switch now.


return TunnelConfiguration(name: "DuckDuckGo VPN", interface: interface, peers: [peerConfiguration])
Logger.networkProtection.log("Routing table information:\nL Included Routes: \(routingTableResolver.includedRoutes, privacy: .public)\nL Excluded Routes: \(routingTableResolver.excludedRoutes, privacy: .public)")
Copy link
Contributor Author

@diegoreymendez diegoreymendez Nov 4, 2024

Choose a reason for hiding this comment

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

I don't think this can contain PII - it'll mostly be mostly static IP ranges and DNS ip for inclusions, then static IP ranges and server IP for exclusions.

excludedRoutes: excludedRoutes,
dns: dns,
isKillSwitchEnabled: isKillSwitchEnabled)
let routingTableResolver = VPNRoutingTableResolver(
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 class takes care of calculating the appropriate routes. Makes it really easy to write unit tests as well.

I'll try to get some in before we merge this.

Comment on lines 787 to +794
Logger.networkProtection.log("Generating tunnel config")
Logger.networkProtection.log("Excluded ranges are: \(String(describing: self.settings.excludedRanges), privacy: .public)")
Logger.networkProtection.log("Server selection method: \(self.currentServerSelectionMethod.debugDescription, privacy: .public)")
Logger.networkProtection.log("DNS server: \(String(describing: self.settings.dnsSettings), privacy: .public)")
let tunnelConfiguration = try await generateTunnelConfiguration(serverSelectionMethod: currentServerSelectionMethod,
includedRoutes: includedRoutes ?? [],
excludedRoutes: settings.excludedRanges,
dnsSettings: settings.dnsSettings,
regenerateKey: true)
let tunnelConfiguration = try await generateTunnelConfiguration(
serverSelectionMethod: currentServerSelectionMethod,
dnsSettings: settings.dnsSettings,
regenerateKey: true)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Routes are no longer passed into the PacketTunnelProvider.

Comment on lines -1134 to -1139
case .setExcludeLocalNetworks:
if case .connected = connectionStatus {
try await updateTunnelConfiguration(
updateMethod: .selectServer(currentServerSelectionMethod),
reassert: false)
}
Copy link
Contributor Author

@diegoreymendez diegoreymendez Nov 4, 2024

Choose a reason for hiding this comment

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

Not sure if the extension or main app should decide when to restart the adapter. Maybe I should just do it here?

But doing it here prevents us from doing batch changes - although to be honest we don't do them in the main app either as settings are individual :)

Comment on lines +1231 to +1240
private func handleRestartAdapter() async throws {
let tunnelConfiguration = try await generateTunnelConfiguration(
serverSelectionMethod: currentServerSelectionMethod,
dnsSettings: settings.dnsSettings,
regenerateKey: false)

try await updateTunnelConfiguration(updateMethod: .useConfiguration(tunnelConfiguration),
reassert: false,
regenerateKey: false)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Convenience method for restarting the adapter very quickly and without going through server re-registration.


import Foundation

public enum RoutingRange {
Copy link
Contributor Author

@diegoreymendez diegoreymendez Nov 4, 2024

Choose a reason for hiding this comment

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

This whole doesn't make sense anymore. We don't need to offer to the user this level of granularity and if we ever do, it's likely to be using a different mechanism.

@diegoreymendez diegoreymendez marked this pull request as ready for review November 4, 2024 15:54
@diegoreymendez diegoreymendez changed the title Fix TunnelVision using enforceRoutes Remediate TunnelVision, TunnelCrack and fix "Exclude Local Networks" Nov 6, 2024
Copy link
Collaborator

@not-a-rootkit not-a-rootkit left a comment

Choose a reason for hiding this comment

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

LGTM, just some open questions on the 10.0.0.0 IP range and a nit with left-in commented-out code.

Not a blocker, but open question: do you think there is a way to test the routing table in isolation? I don't see an obvious way that doesn't involve quite a bit of work, but you know more than me :)

]

public static let localNetworkRange: [NetworkProtection.IPAddressRange] = [
// "10.0.0.0/8", /* 255.0.0.0 */
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this commented out because it clashes with our VPN tunnel IP range? Should we add ranges surrounding the tunnel range?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm matching Android here, where they exclude the 10.x.x.x range because it's used for DNS and generally reserved for out VPN.

I do think we should included everything but the DNS but since Android is doing this, I'm assuming it's good enough for us, for now.

@diegoreymendez
Copy link
Contributor Author

Not a blocker, but open question: do you think there is a way to test the routing table in isolation? I don't see an obvious way that doesn't involve quite a bit of work, but you know more than me :)

I think we should add tests to make sure the ranges are fine, but I also don't want to be blocking this PR for that now. I promise I'll push something soon to cover this.

Testing what the system does with the ranges is, IMO, a loosing battle - but we can test that our system is behaving well and delivering the correct routes to the OS.

@diegoreymendez diegoreymendez merged commit 614ea57 into main Nov 11, 2024
7 checks passed
@diegoreymendez diegoreymendez deleted the diego/fix-tunnelvision-2 branch November 11, 2024 20:05
diegoreymendez added a commit to duckduckgo/macos-browser that referenced this pull request Nov 11, 2024
diegoreymendez added a commit to duckduckgo/iOS that referenced this pull request Nov 11, 2024
samsymons added a commit that referenced this pull request Nov 12, 2024
* main:
  Remediate TunnelVision, TunnelCrack and fix "Exclude Local Networks" (#1039)
  Sync: Send pixels for account removal + decoding issues (#1065)
  Bump github.com/duckduckgo/content-scope-scripts from 6.31.0 to 6.32.0 (#1067)
  Behavioral toast updates (#1063)
  point to CSS branch (#1051)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants