mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
fix(apple): Persist Firezone ID to disk instead of using hardware tracking methods (#3244)
Fixes #2981
This commit is contained in:
@@ -32,7 +32,6 @@
|
||||
79756C6629704A7A0018E2D5 /* FirezoneKit in Frameworks */ = {isa = PBXBuildFile; productRef = 79756C6529704A7A0018E2D5 /* FirezoneKit */; };
|
||||
8D28EB992B35FBD70083621C /* Resolv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D28EB982B35FBD70083621C /* Resolv.swift */; };
|
||||
8D28EB9A2B35FBD70083621C /* Resolv.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D28EB982B35FBD70083621C /* Resolv.swift */; };
|
||||
8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */; };
|
||||
8DC08BCB2B296C4500675F46 /* libresolv.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BCA2B296C4500675F46 /* libresolv.9.tbd */; };
|
||||
8DC08BCD2B296C5900675F46 /* libresolv.9.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BCC2B296C5900675F46 /* libresolv.9.tbd */; };
|
||||
8DC08BD22B297B7B00675F46 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC08BD12B297B7B00675F46 /* libresolv.tbd */; };
|
||||
@@ -118,7 +117,6 @@
|
||||
6FE93AFA2A738D7E002D278A /* NetworkSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettings.swift; sourceTree = "<group>"; };
|
||||
6FFECD5B2AD6998400E00273 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
|
||||
8D28EB982B35FBD70083621C /* Resolv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resolv.swift; sourceTree = "<group>"; };
|
||||
8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimaryMacAddress.swift; sourceTree = "<group>"; };
|
||||
8DC08BCA2B296C4500675F46 /* libresolv.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.9.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.2.sdk/usr/lib/libresolv.9.tbd; sourceTree = DEVELOPER_DIR; };
|
||||
8DC08BCC2B296C5900675F46 /* libresolv.9.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.9.tbd; path = usr/lib/libresolv.9.tbd; sourceTree = SDKROOT; };
|
||||
8DC08BD12B297B7B00675F46 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
||||
@@ -188,7 +186,6 @@
|
||||
6FE455082A5D110D006549B1 /* CallbackHandler.swift */,
|
||||
6FE4550B2A5D111D006549B1 /* SwiftBridgeCore.swift */,
|
||||
6FE4550E2A5D112C006549B1 /* connlib-client-apple.swift */,
|
||||
8D2F64EC2A97336C00B6176A /* PrimaryMacAddress.swift */,
|
||||
6FE455112A5D13A2006549B1 /* FirezoneNetworkExtension-Bridging-Header.h */,
|
||||
8D28EB982B35FBD70083621C /* Resolv.swift */,
|
||||
);
|
||||
@@ -516,7 +513,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8DC08BD62B297DA400675F46 /* FirezoneNetworkExtension-Bridging-Header.h in Sources */,
|
||||
8D2F64EF2A973F7000B6176A /* PrimaryMacAddress.swift in Sources */,
|
||||
6FE4550A2A5D110D006549B1 /* CallbackHandler.swift in Sources */,
|
||||
8D28EB9A2B35FBD70083621C /* Resolv.swift in Sources */,
|
||||
6FE455102A5D112C006549B1 /* connlib-client-apple.swift in Sources */,
|
||||
|
||||
@@ -91,6 +91,7 @@ class Adapter {
|
||||
|
||||
private let logFilter: String
|
||||
private let connlibLogFolderPath: String
|
||||
private let firezoneIdFileURL: URL
|
||||
|
||||
init(
|
||||
controlPlaneURLString: String, token: String,
|
||||
@@ -103,6 +104,7 @@ class Adapter {
|
||||
self.state = .stoppedTunnel
|
||||
self.logFilter = logFilter
|
||||
self.connlibLogFolderPath = SharedAccess.connlibLogFolderURL?.path ?? ""
|
||||
self.firezoneIdFileURL = SharedAccess.baseFolderURL.appendingPathComponent("firezone-id")
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -151,7 +153,7 @@ class Adapter {
|
||||
session: try WrappedSession.connect(
|
||||
self.controlPlaneURLString,
|
||||
self.token,
|
||||
self.getDeviceId(),
|
||||
self.getOrCreateFirezoneId(from: self.firezoneIdFileURL),
|
||||
self.getDeviceName(),
|
||||
self.getOSVersion(),
|
||||
self.connlibLogFolderPath,
|
||||
@@ -257,28 +259,27 @@ extension Adapter {
|
||||
#endif
|
||||
}
|
||||
|
||||
// uuidString and copyMACAddress() *should* reliably return valid Strings, but if
|
||||
// for whatever reason they're nil, return a random UUID instead to prevent
|
||||
// upsert collisions in the portal.
|
||||
func getDeviceId() -> String {
|
||||
#if os(iOS)
|
||||
guard let extId = UIDevice.current.identifierForVendor?.uuidString else {
|
||||
// FIXME: We should store this random deviceID fallback in the AppStore
|
||||
// to use for upsert instead of generating a random UUID each time.
|
||||
return UUID().uuidString
|
||||
// Returns the Firezone ID as cached by the application or generates and persists a new one
|
||||
// if that doesn't exist. The Firezone ID is a UUIDv4 that is used to dedup this device
|
||||
// for upsert and identification in the admin portal.
|
||||
func getOrCreateFirezoneId(from fileURL: URL) -> String {
|
||||
do {
|
||||
return try String(contentsOf: fileURL, encoding: .utf8)
|
||||
} catch {
|
||||
// Handle the error if the file doesn't exist or isn't readable
|
||||
// Recreate the file, save a new UUIDv4, and return it
|
||||
let newUUIDString = UUID().uuidString
|
||||
|
||||
do {
|
||||
try newUUIDString.write(to: fileURL, atomically: true, encoding: .utf8)
|
||||
} catch {
|
||||
self.logger.error(
|
||||
"Adapter.getOrCreateFirezoneId: Could not save \(fileURL, privacy: .public)! Error: \(error, privacy: .public)"
|
||||
)
|
||||
}
|
||||
|
||||
#elseif os(macOS)
|
||||
guard let extId = PrimaryMacAddress.copyMACAddress() as? String else {
|
||||
// FIXME: We should store this random deviceID fallback in the AppStore
|
||||
// to use for upsert instead of generating a random UUID each time.
|
||||
return UUID().uuidString
|
||||
}
|
||||
#else
|
||||
#error("Unsupported platform")
|
||||
#endif
|
||||
|
||||
return extId
|
||||
return newUUIDString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,7 +349,7 @@ extension Adapter {
|
||||
session: try WrappedSession.connect(
|
||||
controlPlaneURLString,
|
||||
token,
|
||||
self.getDeviceId(),
|
||||
self.getOrCreateFirezoneId(from: self.firezoneIdFileURL),
|
||||
self.getDeviceName(),
|
||||
self.getOSVersion(),
|
||||
self.connlibLogFolderPath,
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
//
|
||||
// PrimaryMacAddress.swift
|
||||
// (c) 2023 Firezone, Inc.
|
||||
// LICENSE: Apache-2.0
|
||||
//
|
||||
// Contains convenience methods for getting a device ID for macOS.
|
||||
|
||||
// Believe it or not, this is Apple's recommended way of doing things for macOS
|
||||
// See https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device#//apple_ref/doc/uid/TP40010573-CH1-SW14
|
||||
|
||||
import Foundation
|
||||
import IOKit
|
||||
import OSLog
|
||||
|
||||
public class PrimaryMacAddress {
|
||||
// Returns an object with a +1 retain count; the caller needs to release.
|
||||
private static func ioService(named name: String, wantBuiltIn: Bool) -> io_service_t? {
|
||||
let defaultPort = kIOMainPortDefault
|
||||
var iterator = io_iterator_t()
|
||||
defer {
|
||||
if iterator != IO_OBJECT_NULL {
|
||||
IOObjectRelease(iterator)
|
||||
}
|
||||
}
|
||||
|
||||
guard let matchingDict = IOBSDNameMatching(defaultPort, 0, name),
|
||||
IOServiceGetMatchingServices(
|
||||
defaultPort,
|
||||
matchingDict as CFDictionary,
|
||||
&iterator) == KERN_SUCCESS,
|
||||
iterator != IO_OBJECT_NULL
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var candidate = IOIteratorNext(iterator)
|
||||
while candidate != IO_OBJECT_NULL {
|
||||
if let cftype = IORegistryEntryCreateCFProperty(
|
||||
candidate,
|
||||
"IOBuiltin" as CFString,
|
||||
kCFAllocatorDefault,
|
||||
0)
|
||||
{
|
||||
let isBuiltIn = cftype.takeRetainedValue() as! CFBoolean
|
||||
if wantBuiltIn == CFBooleanGetValue(isBuiltIn) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
|
||||
IOObjectRelease(candidate)
|
||||
candidate = IOIteratorNext(iterator)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func copyMACAddress() -> CFData? {
|
||||
// Prefer built-in network interfaces.
|
||||
// For example, an external Ethernet adaptor can displace
|
||||
// the built-in Wi-Fi as en0.
|
||||
guard
|
||||
let service = ioService(named: "en0", wantBuiltIn: true)
|
||||
?? ioService(named: "en1", wantBuiltIn: true)
|
||||
?? ioService(named: "en0", wantBuiltIn: false)
|
||||
else { return nil }
|
||||
defer { IOObjectRelease(service) }
|
||||
|
||||
if let cftype = IORegistryEntrySearchCFProperty(
|
||||
service,
|
||||
kIOServicePlane,
|
||||
"IOMACAddress" as CFString,
|
||||
kCFAllocatorDefault,
|
||||
IOOptionBits(kIORegistryIterateRecursively | kIORegistryIterateParents))
|
||||
{
|
||||
return (cftype as! CFData)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user