Merge pull request #44 from passepartoutvpn/diagnose-debug-masking

Diagnose debug masking
This commit is contained in:
Davide De Rosa 2019-03-22 19:28:10 +01:00 committed by GitHub
commit 57f8104f27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 163 additions and 51 deletions

View File

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Added
- Configure masking in debug log for improved diagnostics.
## 1.1.0 (2019-03-22) ## 1.1.0 (2019-03-22)
### Added ### Added

View File

@ -109,16 +109,16 @@ class OrganizerViewController: UITableViewController, TableModelHost {
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
if !didShowSubreddit && !TransientStore.shared.didHandleSubreddit { if !didShowSubreddit && !TransientStore.didHandleSubreddit {
didShowSubreddit = true didShowSubreddit = true
let alert = Macros.alert(L10n.Reddit.title, L10n.Reddit.message) let alert = Macros.alert(L10n.Reddit.title, L10n.Reddit.message)
alert.addDefaultAction(L10n.Reddit.Buttons.subscribe) { alert.addDefaultAction(L10n.Reddit.Buttons.subscribe) {
TransientStore.shared.didHandleSubreddit = true TransientStore.didHandleSubreddit = true
self.subscribeSubreddit() self.subscribeSubreddit()
} }
alert.addAction(L10n.Reddit.Buttons.never) { alert.addAction(L10n.Reddit.Buttons.never) {
TransientStore.shared.didHandleSubreddit = true TransientStore.didHandleSubreddit = true
} }
alert.addCancelAction(L10n.Reddit.Buttons.remind) alert.addCancelAction(L10n.Reddit.Buttons.remind)
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)

View File

@ -48,6 +48,8 @@ class ServiceViewController: UIViewController, TableModelHost {
private var lastInfrastructureUpdate: Date? private var lastInfrastructureUpdate: Date?
private var shouldDeleteLogOnDisconnection = false
// MARK: Table // MARK: Table
var model: TableModel<SectionType, RowType> = TableModel() var model: TableModel<SectionType, RowType> = TableModel()
@ -358,7 +360,7 @@ class ServiceViewController: UIViewController, TableModelHost {
private func testInternetConnectivity() { private func testInternetConnectivity() {
let hud = HUD() let hud = HUD()
Utils.checkConnectivityURL(AppConstants.VPN.connectivityURL, timeout: AppConstants.VPN.connectivityTimeout) { Utils.checkConnectivityURL(AppConstants.Web.connectivityURL, timeout: AppConstants.Web.connectivityTimeout) {
hud.hide() hud.hide()
let V = L10n.Service.Alerts.TestConnectivity.Messages.self let V = L10n.Service.Alerts.TestConnectivity.Messages.self
@ -398,6 +400,34 @@ class ServiceViewController: UIViewController, TableModelHost {
} }
} }
private func togglePrivateDataMasking(cell: ToggleTableViewCell) {
let handler = {
TransientStore.masksPrivateData = cell.isOn
self.service.baseConfiguration = TransientStore.baseVPNConfiguration.build()
}
guard vpn.status == .disconnected else {
let alert = Macros.alert(
L10n.Service.Cells.MasksPrivateData.caption,
L10n.Service.Alerts.MasksPrivateData.Messages.mustReconnect
)
alert.addDestructiveAction(L10n.Service.Alerts.Buttons.reconnect) {
handler()
self.shouldDeleteLogOnDisconnection = true
self.vpn.reconnect(completionHandler: nil)
}
alert.addCancelAction(L10n.Global.cancel) {
cell.setOn(!cell.isOn, animated: true)
}
present(alert, animated: true, completion: nil)
return
}
handler()
service.eraseVpnLog()
shouldDeleteLogOnDisconnection = false
}
private func postSupportRequest() { private func postSupportRequest() {
UIApplication.shared.open(AppConstants.URLs.subreddit, options: [:], completionHandler: nil) UIApplication.shared.open(AppConstants.URLs.subreddit, options: [:], completionHandler: nil)
} }
@ -412,8 +442,21 @@ class ServiceViewController: UIViewController, TableModelHost {
@objc private func vpnDidUpdate() { @objc private func vpnDidUpdate() {
reloadVpnStatus() reloadVpnStatus()
if vpn.status == .connected { guard let status = vpn.status else {
return
}
switch status {
case .connected:
Reviewer.shared.reportEvent() Reviewer.shared.reportEvent()
case .disconnected:
if shouldDeleteLogOnDisconnection {
service.eraseVpnLog()
shouldDeleteLogOnDisconnection = false
}
default:
break
} }
} }
@ -493,6 +536,8 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
case debugLog case debugLog
case masksPrivateData
case joinCommunity case joinCommunity
case reportIssue case reportIssue
@ -705,6 +750,12 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
cell.leftText = L10n.Service.Cells.DebugLog.caption cell.leftText = L10n.Service.Cells.DebugLog.caption
return cell return cell
case .masksPrivateData:
let cell = Cells.toggle.dequeue(from: tableView, for: indexPath, tag: row.rawValue, delegate: self)
cell.caption = L10n.Service.Cells.MasksPrivateData.caption
cell.isOn = TransientStore.masksPrivateData
return cell
// feedback // feedback
case .joinCommunity: case .joinCommunity:
@ -859,6 +910,9 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
case .trustedPolicy: case .trustedPolicy:
toggleTrustedConnectionPolicy(cell.isOn, sender: cell) toggleTrustedConnectionPolicy(cell.isOn, sender: cell)
case .masksPrivateData:
togglePrivateDataMasking(cell: cell)
default: default:
break break
} }
@ -922,6 +976,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
} }
model.setFooter(L10n.Service.Sections.VpnSurvivesSleep.footer, for: .vpnSurvivesSleep) model.setFooter(L10n.Service.Sections.VpnSurvivesSleep.footer, for: .vpnSurvivesSleep)
model.setFooter(L10n.Service.Sections.Trusted.footer, for: .trustedPolicy) model.setFooter(L10n.Service.Sections.Trusted.footer, for: .trustedPolicy)
model.setFooter(L10n.Service.Sections.Diagnostics.footer, for: .diagnostics)
} }
// rows // rows
@ -947,7 +1002,7 @@ extension ServiceViewController: UITableViewDataSource, UITableViewDelegate, Tog
} }
model.set([.vpnSurvivesSleep], in: .vpnSurvivesSleep) model.set([.vpnSurvivesSleep], in: .vpnSurvivesSleep)
model.set([.trustedPolicy], in: .trustedPolicy) model.set([.trustedPolicy], in: .trustedPolicy)
model.set([.dataCount, .debugLog], in: .diagnostics) model.set([.dataCount, .debugLog, .masksPrivateData], in: .diagnostics)
model.set([.joinCommunity, .reportIssue], in: .feedback) model.set([.joinCommunity, .reportIssue], in: .feedback)
} }

View File

@ -79,6 +79,7 @@
"service.sections.trusted.header" = "Trusted networks"; "service.sections.trusted.header" = "Trusted networks";
"service.sections.trusted.footer" = "When entering a trusted network, the VPN is normally shut down and kept disconnected. Disable this option to not enforce such behavior."; "service.sections.trusted.footer" = "When entering a trusted network, the VPN is normally shut down and kept disconnected. Disable this option to not enforce such behavior.";
"service.sections.diagnostics.header" = "Diagnostics"; "service.sections.diagnostics.header" = "Diagnostics";
"service.sections.diagnostics.footer" = "Masking status will be effective after reconnecting. Network data is hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless.";
//"service.sections.destruction.footer" = "Delete configuration from device settings."; //"service.sections.destruction.footer" = "Delete configuration from device settings.";
"service.cells.use_profile.caption" = "Use this profile"; "service.cells.use_profile.caption" = "Use this profile";
@ -106,6 +107,7 @@
"service.cells.test_connectivity.caption" = "Test connectivity"; "service.cells.test_connectivity.caption" = "Test connectivity";
"service.cells.data_count.caption" = "Exchanged bytes count"; "service.cells.data_count.caption" = "Exchanged bytes count";
"service.cells.debug_log.caption" = "Debug log"; "service.cells.debug_log.caption" = "Debug log";
"service.cells.masks_private_data.caption" = "Mask network data";
"service.cells.report_issue.caption" = "Report connectivity issue"; "service.cells.report_issue.caption" = "Report connectivity issue";
"service.alerts.rename.title" = "Rename profile"; "service.alerts.rename.title" = "Rename profile";
@ -119,6 +121,8 @@
"service.alerts.test_connectivity.messages.failure" = "Your device has no Internet connectivity, please review your profile parameters."; "service.alerts.test_connectivity.messages.failure" = "Your device has no Internet connectivity, please review your profile parameters.";
"service.alerts.data_count.messages.current" = "Received: %llu\nSent: %llu"; "service.alerts.data_count.messages.current" = "Received: %llu\nSent: %llu";
"service.alerts.data_count.messages.not_available" = "Information not available, are you connected?"; "service.alerts.data_count.messages.not_available" = "Information not available, are you connected?";
"service.alerts.masks_private_data.messages.must_reconnect" = "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.";
"service.alerts.buttons.reconnect" = "Reconnect";
"account.cells.username.caption" = "Username"; "account.cells.username.caption" = "Username";
"account.cells.username.placeholder" = "username"; "account.cells.username.placeholder" = "username";

View File

@ -46,31 +46,6 @@ public class AppConstants {
public static let hostsDirectory = "Hosts" public static let hostsDirectory = "Hosts"
} }
public class VPN {
public static func baseConfiguration() -> TunnelKitProvider.Configuration {
let sessionBuilder = SessionProxy.ConfigurationBuilder(ca: CryptoContainer(pem: ""))
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1250
builder.shouldDebug = true
// builder.debugLogFormat = "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
// builder.debugLogFormat = "$DHH:mm:ss$d $N.$F:$l - $M"
builder.debugLogFormat = Log.debugFormat
return builder.build()
}
private static let connectivityStrings: [String] = [
"https://www.amazon.com",
"https://www.google.com",
"https://www.twitter.com",
"https://www.facebook.com",
"https://www.instagram.com"
]
public static let connectivityURL = URL(string: connectivityStrings.customRandomElement())!
public static let connectivityTimeout: TimeInterval = 10.0
}
public class Web { public class Web {
private static let version = "v1" private static let version = "v1"
@ -83,6 +58,18 @@ public class AppConstants {
public static let timeout: TimeInterval = 3.0 public static let timeout: TimeInterval = 3.0
public static let minimumUpdateInterval: TimeInterval = 600.0 // 10 minutes public static let minimumUpdateInterval: TimeInterval = 600.0 // 10 minutes
private static let connectivityStrings: [String] = [
"https://www.amazon.com",
"https://www.google.com",
"https://www.twitter.com",
"https://www.facebook.com",
"https://www.instagram.com"
]
public static let connectivityURL = URL(string: connectivityStrings.customRandomElement())!
public static let connectivityTimeout: TimeInterval = 10.0
} }
public class Log { public class Log {

View File

@ -510,7 +510,11 @@ public class ConnectionService: Codable {
baseConfiguration.clearLastError(in: appGroup) baseConfiguration.clearLastError(in: appGroup)
} }
// public func eraseVpnLog() { public func eraseVpnLog() {
// defaults.removeObject(forKey: Keys.vpnLog) log.info("Erasing VPN log...")
// } guard let url = baseConfiguration.urlForLog(in: appGroup) else {
return
}
try? FileManager.default.removeItem(at: url)
}
} }

View File

@ -62,6 +62,7 @@ public class HostConnectionProfile: ConnectionProfile, Codable, Equatable {
builder.mtu = configuration.mtu builder.mtu = configuration.mtu
builder.shouldDebug = configuration.shouldDebug builder.shouldDebug = configuration.shouldDebug
builder.debugLogFormat = configuration.debugLogFormat builder.debugLogFormat = configuration.debugLogFormat
builder.masksPrivateData = configuration.masksPrivateData
return builder.build() return builder.build()
} }

View File

@ -119,6 +119,7 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
builder.mtu = configuration.mtu builder.mtu = configuration.mtu
builder.shouldDebug = configuration.shouldDebug builder.shouldDebug = configuration.shouldDebug
builder.debugLogFormat = configuration.debugLogFormat builder.debugLogFormat = configuration.debugLogFormat
builder.masksPrivateData = configuration.masksPrivateData
if let address = manualAddress { if let address = manualAddress {
builder.prefersResolvedAddresses = true builder.prefersResolvedAddresses = true

View File

@ -24,6 +24,7 @@
// //
import Foundation import Foundation
import TunnelKit
import SwiftyBeaver import SwiftyBeaver
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
@ -31,6 +32,8 @@ private let log = SwiftyBeaver.self
public class TransientStore { public class TransientStore {
private struct Keys { private struct Keys {
static let didHandleSubreddit = "DidHandleSubreddit" static let didHandleSubreddit = "DidHandleSubreddit"
static let masksPrivateData = "MasksPrivateData"
} }
public static let shared = TransientStore() public static let shared = TransientStore()
@ -41,7 +44,7 @@ public class TransientStore {
public let service: ConnectionService public let service: ConnectionService
public var didHandleSubreddit: Bool { public static var didHandleSubreddit: Bool {
get { get {
return UserDefaults.standard.bool(forKey: Keys.didHandleSubreddit) return UserDefaults.standard.bool(forKey: Keys.didHandleSubreddit)
} }
@ -50,13 +53,39 @@ public class TransientStore {
} }
} }
public static var masksPrivateData: Bool {
get {
return UserDefaults.standard.bool(forKey: Keys.masksPrivateData)
}
set {
UserDefaults.standard.set(newValue, forKey: Keys.masksPrivateData)
}
}
public static var baseVPNConfiguration: TunnelKitProvider.ConfigurationBuilder {
let sessionBuilder = SessionProxy.ConfigurationBuilder(ca: CryptoContainer(pem: ""))
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
builder.mtu = 1250
builder.shouldDebug = true
// builder.debugLogFormat = "$Dyyyy-MM-dd HH:mm:ss.SSS$d $L $N.$F:$l - $M"
// builder.debugLogFormat = "$DHH:mm:ss$d $N.$F:$l - $M"
builder.debugLogFormat = AppConstants.Log.debugFormat
builder.masksPrivateData = masksPrivateData
return builder
}
private init() { private init() {
UserDefaults.standard.register(defaults: [
Keys.didHandleSubreddit: false,
Keys.masksPrivateData: true
])
TransientStore.migrateDocumentsToAppGroup() TransientStore.migrateDocumentsToAppGroup()
// this must be graceful // this must be graceful
ConnectionService.migrateJSON(from: TransientStore.serviceURL, to: TransientStore.serviceURL) ConnectionService.migrateJSON(from: TransientStore.serviceURL, to: TransientStore.serviceURL)
let cfg = AppConstants.VPN.baseConfiguration() let cfg = TransientStore.baseVPNConfiguration.build()
do { do {
let data = try Data(contentsOf: TransientStore.serviceURL) let data = try Data(contentsOf: TransientStore.serviceURL)
if let content = String(data: data, encoding: .utf8) { if let content = String(data: data, encoding: .utf8) {

View File

@ -454,6 +454,10 @@ public enum L10n {
public enum Service { public enum Service {
public enum Alerts { public enum Alerts {
public enum Buttons {
/// Reconnect
public static let reconnect = L10n.tr("Localizable", "service.alerts.buttons.reconnect")
}
public enum CredentialsNeeded { public enum CredentialsNeeded {
/// You need to enter account credentials first. /// You need to enter account credentials first.
public static let message = L10n.tr("Localizable", "service.alerts.credentials_needed.message") public static let message = L10n.tr("Localizable", "service.alerts.credentials_needed.message")
@ -468,6 +472,12 @@ public enum L10n {
public static let notAvailable = L10n.tr("Localizable", "service.alerts.data_count.messages.not_available") public static let notAvailable = L10n.tr("Localizable", "service.alerts.data_count.messages.not_available")
} }
} }
public enum MasksPrivateData {
public enum Messages {
/// In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.
public static let mustReconnect = L10n.tr("Localizable", "service.alerts.masks_private_data.messages.must_reconnect")
}
}
public enum ReconnectVpn { public enum ReconnectVpn {
/// Do you want to reconnect to the VPN? /// Do you want to reconnect to the VPN?
public static let message = L10n.tr("Localizable", "service.alerts.reconnect_vpn.message") public static let message = L10n.tr("Localizable", "service.alerts.reconnect_vpn.message")
@ -546,6 +556,10 @@ public enum L10n {
} }
} }
} }
public enum MasksPrivateData {
/// Mask network data
public static let caption = L10n.tr("Localizable", "service.cells.masks_private_data.caption")
}
public enum Provider { public enum Provider {
public enum Pool { public enum Pool {
/// Location /// Location
@ -613,6 +627,8 @@ public enum L10n {
public static let header = L10n.tr("Localizable", "service.sections.configuration.header") public static let header = L10n.tr("Localizable", "service.sections.configuration.header")
} }
public enum Diagnostics { public enum Diagnostics {
/// Masking status will be effective after reconnecting. Network data is hostnames, IP addresses, routing, SSID. Credentials and private keys are not logged regardless.
public static let footer = L10n.tr("Localizable", "service.sections.diagnostics.footer")
/// Diagnostics /// Diagnostics
public static let header = L10n.tr("Localizable", "service.sections.diagnostics.header") public static let header = L10n.tr("Localizable", "service.sections.diagnostics.header")
} }

View File

@ -3,10 +3,10 @@ platform :ios, '11.0'
use_frameworks! use_frameworks!
def shared_pods def shared_pods
pod 'TunnelKit', '~> 1.5.0' #pod 'TunnelKit', '~> 1.5.0'
pod 'TunnelKit/LZO', '~> 1.5.0' #pod 'TunnelKit/LZO', '~> 1.5.0'
#pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => '2f17e30' pod 'TunnelKit', :git => 'https://github.com/keeshux/tunnelkit', :commit => '04fbbb1'
#pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => '2f17e30' pod 'TunnelKit/LZO', :git => 'https://github.com/keeshux/tunnelkit', :commit => '04fbbb1'
#pod 'TunnelKit', :path => '../../personal/tunnelkit' #pod 'TunnelKit', :path => '../../personal/tunnelkit'
#pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit' #pod 'TunnelKit/LZO', :path => '../../personal/tunnelkit'
end end

View File

@ -2,35 +2,44 @@ PODS:
- MBProgressHUD (1.1.0) - MBProgressHUD (1.1.0)
- OpenSSL-Apple (1.1.0i.2) - OpenSSL-Apple (1.1.0i.2)
- SwiftyBeaver (1.6.2) - SwiftyBeaver (1.6.2)
- TunnelKit (1.5.0): - TunnelKit (1.5.1):
- TunnelKit/AppExtension (= 1.5.0) - TunnelKit/AppExtension (= 1.5.1)
- TunnelKit/Core (= 1.5.0) - TunnelKit/Core (= 1.5.1)
- TunnelKit/AppExtension (1.5.0): - TunnelKit/AppExtension (1.5.1):
- SwiftyBeaver - SwiftyBeaver
- TunnelKit/Core - TunnelKit/Core
- TunnelKit/Core (1.5.0): - TunnelKit/Core (1.5.1):
- OpenSSL-Apple (~> 1.1.0i.2) - OpenSSL-Apple (~> 1.1.0i.2)
- SwiftyBeaver - SwiftyBeaver
- TunnelKit/LZO (1.5.0) - TunnelKit/LZO (1.5.1)
DEPENDENCIES: DEPENDENCIES:
- MBProgressHUD - MBProgressHUD
- TunnelKit (~> 1.5.0) - TunnelKit (from `https://github.com/keeshux/tunnelkit`, commit `04fbbb1`)
- TunnelKit/LZO (~> 1.5.0) - TunnelKit/LZO (from `https://github.com/keeshux/tunnelkit`, commit `04fbbb1`)
SPEC REPOS: SPEC REPOS:
https://github.com/cocoapods/specs.git: https://github.com/cocoapods/specs.git:
- MBProgressHUD - MBProgressHUD
- OpenSSL-Apple - OpenSSL-Apple
- SwiftyBeaver - SwiftyBeaver
- TunnelKit
EXTERNAL SOURCES:
TunnelKit:
:commit: 04fbbb1
:git: https://github.com/keeshux/tunnelkit
CHECKOUT OPTIONS:
TunnelKit:
:commit: 04fbbb1
:git: https://github.com/keeshux/tunnelkit
SPEC CHECKSUMS: SPEC CHECKSUMS:
MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9
OpenSSL-Apple: 37a8c0b04df4bb8971deef4671cc29222861319c OpenSSL-Apple: 37a8c0b04df4bb8971deef4671cc29222861319c
SwiftyBeaver: 8e67ab3cd94389cbbb7a9c7cc02748d98bfee68e SwiftyBeaver: 8e67ab3cd94389cbbb7a9c7cc02748d98bfee68e
TunnelKit: bfe0960fc09d9e69cc66a141084887c5abf986d4 TunnelKit: fb4db4d4c4b36af0ca8e973e3345a1c3ef5ac85c
PODFILE CHECKSUM: 5cab612f9d6b322c13311ce6b0aa1f7939a6e818 PODFILE CHECKSUM: 1b84c62ece95cae765c64491d65dab9333db2381
COCOAPODS: 1.6.1 COCOAPODS: 1.6.1