开发私有网络连接 iOS 应用:探索 OpenVPN 协议





5.00/5 (4投票s)
使用 OpenVPN 协议为 iOS 平台开发私有网络连接移动应用程序
1. 引言与动机
由于近期居家办公模式需求的增长,使用 VPN(虚拟专用网络)连接已变得流行,以安全地确保用户与封闭企业网络的集成。在这类连接中,OpenVPN 协议是最受欢迎的协议之一。然而,并非所有平台都完全支持连接到实现该协议的服务器。iOS 移动平台就是这种情况,其目录中只有一款工具可以建立此类连接。并且,根据服务器设置的定制级别,使用该应用程序连接可能会变得不可能。正是这种缺陷,加上 iOS 平台的普及性,促使了开发一种在当前市场上现有解决方案中缺乏定制功能的工具。
2. 目标与方法
本工作的目标是为需要建立设备与私有网络之间 VPN 连接的 iOS 平台用户提供一款新工具。为了创建此应用程序,使用了 Apple 的 IDE Xcode。该软件基于 MVVM(模型-视图-视图模型)设计模式的一种变体构建,并在 iPhone 7 上进行调试。为了测试,使用 Google Cloud Platform 环境创建了一个虚拟云服务器(VPS)。该服务器运行 Debian 10 系统发行版 Linux 操作系统。并在其上安装和配置了 OpenVPN 网络。为了补充测试,创建了第二个服务器,连接到与第一个服务器相同的内网,但没有外部访问权限。第二个 VPS 运行一个仅提供说明页面的 Web 服务器。
3. 开发过程
3.1 规范与设计
原型编码使用了 Swift 编程语言的 5.0 版本。用于组织代码的设计模式是 MVVM,并进行了一些改编。图形界面创建使用的框架是 SwiftUI。为了模拟真实用例,在 Google Cloud Platform 平台上设置了一个虚拟网络,包含两台服务器。一台是 Web 服务器,另一台是 OpenVPN 服务器。两者都运行 Debian 操作系统(Linux)并可相互通信。应用程序开发和测试环境创建中使用了以下支持工具:
- Xcode:用于在 Apple 环境中开发应用程序的 IDE(集成开发环境)
- OpenVPNAdapter:基于 OpenVPN3 库(最初用 C++ 编写)的 Objective-C 框架,用于配置 OpenVPN 协议连接
- CocoaPods:Swift 和 Objective-C 项目的依赖管理器
- Visual Studio Code:用于编辑 Markdown、Pod 文件和其他 Xcode 未管理的文件的文本编辑器
- Google Cloud Console:用于管理 Google Cloud 平台服务的界面
- GitHub:基于 Git 版本控制系统的源代码托管平台
- LucidChart:用于创建 UML 用例图的平台
最初,对 iOS 操作系统上可用于处理 OpenVPN 连接的可能框架和库进行了调研。调研后得出结论,最安全的方法是使用协议的官方库,该库用 C++ 编写,因为它有可靠的文档。但由于语言障碍,需要 OpenVPNAdapter
框架(代码片段 1)的帮助,该框架将官方库适配为 Objective-C 语言。这使得将其整合到项目中成为可能,因为 Xcode IDE 管理 Objective-C 与 Swift 语言结合进行 iOS 项目开发。
def shared_pods
use_frameworks!
pod 'OpenVPNAdapter', :git => 'https://github.com/deneraraujo/OpenVPNAdapter.git'
end
target 'VPNClient' do
shared_pods
end
target 'TunnelProvider' do
shared_pods
end
下一步是了解操作系统如何处理虚拟网络接口。与其他系统(如 Windows 和 Android)不同,iOS 不为每个 VPN 连接使用单独的网络接口。相反,为主连接定义了配置配置文件。因此,该项目旨在根据库提供的信息创建网络配置配置文件。
确定了开发客户端所需的元素后,下一步是创建一个模拟网络的环境,其中包含一个 OpenVPN 服务器。然后,在 Google Cloud Platform 上设置了一个虚拟网络,包含两台服务器。其中一台是 Web 服务器,它返回一个 HTML(超文本标记语言)页面以证明访问,另一台是 OpenVPN 服务器。第一台代表企业网络中的任何服务器,其端口被阻止外部访问。第二台是 VPN 服务器,应用程序将连接到它以访问第一台以及网络中的其他可能服务器。
成功建立客户端与服务器之间的连接后,使用 SwiftUI 框架开发了一个图形界面,能够创建一个适应多种 Apple 设备的布局。该界面的工作方式与其他操作系统中现有的 OpenVPN 客户端相同:应用程序加载“.opvn”扩展文件中预定义的设置,然后为用户提供自定义选项。作为原型,该应用程序仅限于单个用户配置文件,并允许自定义连接中使用的 DNS(域名系统)服务器以及不同的身份验证形式。
图 1 显示了用户在 UML 用例图中与界面的交互。
下一个图(图 2)显示了通过适配器连接的组件所执行的过程。从该序列图可以看出,设计时考虑了提供单一且灵活的活动,以配置并启动获取连接的过程。
此类 (OpenVPNClient
Prototype) 的第一个技术任务是导入 OpenVPNAdapter
,并从这种协作中调用设置提供者和连接,使用收到的凭据。如果出现问题,该过程会从重新连接命令再次尝试。
在 PacketTunnelProvider
类中,有 startTunnel
方法。此时,应用程序已经收集了所有来自界面的信息,然后调用库,传递这些信息。这实际上是 iOS 框架中已有的 abstract
方法的实现(代码片段 2)。但在这种实现中,它最终会调用 OpenVPNAdapter
库。
override func startTunnel(options: [String : NSObject]?, completionHandler:
@escaping (Error?) -> Void) {
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"]
as ? Data else { return }
configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
configuration.privateKeyPassword = profile?.privateKeyPassword
applyConfiguration(completionHandler: completionHandler)
if !evaluation.autologin {
if let username: String = providerConfiguration["username"]
as ? String, let password: String = providerConfiguration["password"]
as ? String {
let credentials = OpenVPNCredentials()
credentials.username = username
credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
}
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
vpnAdapter.connect(using: packetFlow)
}
3.2 结果
下一步是了解操作系统如何处理虚拟网络接口。与其他系统(如 Windows 和 Android)不同,iOS 不为每个 VPN 连接使用单独的网络接口。相反,为主连接定义了配置配置文件。因此,该项目旨在根据库提供的信息创建网络配置配置文件。
![]() | ![]() | ![]() |
对于这个项目,文件是在云测试服务器上生成的。文件加载后,会进行验证。如果文件正常,则显示 OpenVPN 服务器地址。否则,会提醒用户。
OpenVPN 服务器允许分发给用户的配置文件使用私有安全密钥进行保护。因此,如果用户加载的文件是这种情况,则会显示一个名为“私钥”的必填字段。然后,用户必须选择如何向服务器进行身份验证:匿名或使用用户名和密码。匿名身份验证的可能性是必要的,因为网络管理员可能只选择私钥文件作为安全方法,并且不需要通过用户和密码进行身份验证。
输出日志信息显示在主视图上,通过单个操作即可访问连接过程的逐步说明(图 5)。
加载文件并输入身份验证数据后,用户的下一步是选择是否输入额外的 DNS 服务器地址以用于连接。内部网络通常有专用的 DNS 服务器,尤其是在企业环境中。在这些情况下,任何地址都将提供给用户。
填写完字段后,用户只需单击“连接”按钮。必填字段会经过验证,如果存在任何不规范之处,会在继续之前提醒用户。
然后进行 ping 以检查服务器可用性。一旦服务器可访问,凭据将被验证,如果它们不规范,用户将被警告并中断连接尝试。否则,连接协议开始。
最后,图 4 展示了以前无法访问的内部网络地址的可访问性。
客户端和服务器之间的整个协商过程显示在日志(图 5)中,该日志在运行时填充并显示在屏幕上。任何连接失败都会在日志中以红色突出显示,并导致向用户显示失败消息。
成功连接后,用户将能够访问现在可用的互联网和内网地址。
3.3 源代码和依赖链接
- 本项目开发的可用源代码:https://github.com/deneraraujo/openvpnclient-ios
- OpenVPNAdapter (2020) 依赖库:https://github.com/ss-abramchuk/OpenVPNAdapter
4. 结论
通过本项目开发过程中收集的研究和信息,得以更广泛地理解虚拟专用网络在某些操作系统中的运作方式。在过程中获得的知识,例如软件工程和需求收集,对于本项目开发以及一些网络运作概念都是不可或缺的。
设计模式的使用也得到了显著改进,以及现代移动应用语言和技术的使用。由于 GitHub 在管理应用程序开发中发挥了重要作用,因此也获得了更好的版本控制和任务管理概念。
除了获得的知识范围之外,该项目还旨在为移动开发社区做出贡献,重点关注 iOS 操作系统,因为与其他平台相比,iOS 的内容较少。
迄今为止获得的知识和结果令人满意,并为改进原型以开发一个完整的应用程序开创了先例,该应用程序在不久的将来可以为那些通过移动设备难以连接到私有网络的真实用户提供帮助。
5. 参考资料
- Apple (2020);Swift 编程语言。Swift 语言官方文档 [在线] 可访问:https://swiftlang.cn documentation/#the-swift-programming-language,2020 年 12 月访问。
- Hoffman, J. (2019);《精通 Swift 5》,第 5 版,Packt Publishing。2019。Apple (2020);SwiftUI 框架官方文档 [在线] 可访问:https://developer.apple.com/documentation/swiftui/,2020 年 12 月访问。
- OpenVPNAdapter (2020);“基于 OpenVPN3 库的 Objective-C 框架,用于配置 OpenVPN 协议连接”[在线] 可访问:https://github.com/ss-abramchuk/OpenVPNAdapter,2020 年 9 月访问。
- OpenVPN3 (2020);“用 C++ 编写的库,用于配置 OpenVPN 协议连接”[在线] 可访问:https://github.com/OpenVPN/openvpn3,2020 年 9 月访问。
- Ankit Sinhal (2020);“MVC、MVP 和 MVVM 设计模式”。已发表的技术文章 [在线] 可访问:https://medium.com/@ankit.sinhal/mvc-mvp-and-mvvm-design-pattern- 6e169567bbad,2020 年 9 月访问。
- Kouraklis, J. (2016);《Delphi 中的 MVVM》,Apress。2016。Jan Just Keijser, J. J. (2017);《OpenVPN 食谱》,第二版,Packt Publishing。2017。
- Google (2021);“Google Cloud 官方文档”[在线] 可访问:https://cloud.google.com/docs,2021 年 2 月访问。
历史
- 2021 年 6 月 11 日 - 文档创建
- 2021 年 6 月 13 日 - 文本修订,初始版本
- 2021 年 6 月 15 日 - 修订和验证
- 2021 年 6 月 16 日 - 文档提交
- 2021 年 6 月 24 日 - 文档修订,错误修复和改进
- 2021 年 6 月 25 日 - 文档提交
- 2021 年 6 月 25 日 - 图像细节改进
- 2021 年 6 月 26 日 - 小幅文本修正
- 2021 年 6 月 27 日 - 图像更新