全部产品

iOS14原生加密DNS方案

本文档介绍了阿里云公共DNS 在 iOS 14原生加密DNS方案中的接入和开发方式。

概述

DNS解析是网络资源访问的第一跳,iOS 14 开始系统原生支持两种标准规范的 Encrypted DNS, 分别是 DNS over TLS 与 DNS over HTTPS,可以解决以下两个问题:

一、传统Local DNS的查询与回复均基于非加密UDP,发生我们常见的DNS劫持问题。

二、Local DNS Server本身不可信,或者本地Local DNS 服务不可用问题。

针对DNS解析过程中以上两个问题,阿里云公共DNS已经有了解决方案,即使用阿里云公共DNS SDK,但使用SDK会面临一些技术坑,比如302场景的IP直连处理、WebView下IP直连如何处理、以及iOS上的SNI问题等,而iOS 14 上的 Encrypted DNS 功能很好的解决了集成SDK的方案存在的问题,您可以参考Demo示例工程源码了解如何设置阿里云公共DNS为加密DNS默认解析器。

iOS 14原生加密DNS方案如何接入阿里云公共DNS

iOS 14 提供了两种设置加密DNS的方法。

第一种方式是选择一个DNS服务器作为系统全局所有App默认的DNS解析器,如果你提供的是一个公共DNS服务器,你可以使用NEDNSSettingsManager API编写一个NetworkExtension App完成系统全局加密DNS设置。

使用NetworkExtension设置系统域全局DNS服务器,使用DoH协议示例代码:

import NetworkExtension

NEDNSSettingsManager.shared().loadFromPreferences { loadError in
            if let loadError = loadError {
                // ...handle error...
                return
            }
            let dohSettings = NEDNSOverHTTPSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
            dohSettings.serverURL = URL(string: "https://您在控制台注册应用时分配的AccountID.alidns.com/dns-query")
            NEDNSSettingsManager.shared().dnsSettings = dohSettings
            NEDNSSettingsManager.shared().saveToPreferences { saveError in
                if let saveError = saveError {
                  // ...handle error...
                  return
                }
            }
        }

使用NetworkExtension设置系统域全局DNS服务器,使用DoT协议示例代码:

import NetworkExtension

NEDNSSettingsManager.shared().loadFromPreferences { loadError in
            if let loadError = loadError {
                // ...handle error...
                return
            }
            let dotSettings = NEDNSOverTLSSettings(servers: ["223.5.5.5","223.6.6.6","2400:3200:baba::1","2400:3200::1"])
            dotSettings.serverName = "您在控制台注册应用时分配的AccountID.alidns.com"
            NEDNSSettingsManager.shared().dnsSettings = dotSettings
            NEDNSSettingsManager.shared().saveToPreferences { saveError in
                if let saveError = saveError {
                  // ...handle error...
                  return
                }
            }
        }

一条DNS配置包括阿里公共DNS服务器地址、DoT/DoH协议、一组网络规则。网络规则确保DNS设置兼容不同的网络。

网络规则设置示例代码:

let workWiFi = NEOnDemandRuleEvaluateConnection()
     workWiFi.interfaceTypeMatch = .wiFi
     workWiFi.ssidMatch = ["MyWorkWiFi"]
     workWiFi.connectionRules = [NEEvaluateConnectionRule(matchDomains: ["enterprise.example"], andAction: .neverConnect)]
            
     let disableOnCell = NEOnDemandRuleDisconnect()
     disableOnCell.interfaceTypeMatch = .cellular
            
      let enableByDefault = NEOnDemandRuleConnect()
      NEDNSSettingsManager.shared().onDemandRules = [
                workWiFi,
                disableOnCell,
                enableByDefault
      ]

上述代码设置了三个网络规则,第一个规则表示DNS配置应该在SSID=“MyWorkWiFi”的WiFi网络生效,但对私有企业域名enterprise.example.net不开启;第二个规则表示规则在蜂窝网下应该被禁止使用;第三个NEOnDemandRuleConnect表示DNS配置应该默认开启;因为配置DNS是系统支持的,所以在编写NetworkExtension App时不需要实现Extension程序,只需要在Network Extensions中勾选DNS Settings选项。

setNetworkExtension

运行NetworkExtension App,DNS配置将会被安装到系统,为了让DNS配置生效,需要前往设置->通用->VPN & Network->DNS手动启用。

share

第二种方式是针对单个App的所有连接或部分连接启用加密DNS。

如果你只想为你的App使用加密DNS,而非涉及整个系统域。你可以适配Network.framework的PrivacyContext,对你的整个App开启加密DNS,或者仅对某一连接开启。

对单个连接使用加密DNS,使用DoH协议示例代码:

import Network

let privacyContext = NWParameters.PrivacyContext(description: "EncryptedDNS")
        if let url = URL(string: "https://您在控制台注册应用时分配的AccountID.alidns.com/dns-query") {
            let address1 = NWEndpoint.hostPort(host: "223.5.5.5", port: 443)
            let address2 = NWEndpoint.hostPort(host: "223.6.6.6", port: 443)
            let address3 = NWEndpoint.hostPort(host: "2400:3200::1", port: 443)
            let address4 = NWEndpoint.hostPort(host: "2400:3200:baba::1", port: 443)
            privacyContext.requireEncryptedNameResolution(true, fallbackResolver: .https(url, serverAddresses: [address1,address2,address3,address4]))
        }

对单个连接使用加密DNS,使用DoT协议示例代码:

import Network

 let privacyContext = NWParameters.PrivacyContext(description: "EncryptedDNS")
        let alidnsHost = NWEndpoint.hostPort(host: "您在控制台注册应用时分配的AccountID.alidns.com", port: 853)
        let address1 = NWEndpoint.hostPort(host: "223.5.5.5", port: 853)
        let address2 = NWEndpoint.hostPort(host: "223.6.6.6", port: 853)
        let address3 = NWEndpoint.hostPort(host: "2400:3200::1", port: 853)
        let address4 = NWEndpoint.hostPort(host: "2400:3200:baba::1", port: 853)
        privacyContext.requireEncryptedNameResolution(true, fallbackResolver: .tls(alidnsHost, serverAddresses: [address1,address2,address3,address4]))

如果你想在App范围内使用加密DNS,你可以配置默认的PrivacyContext;App内发起的每个DNS解析都会使用这个配置。

在App范围内使用加密DNS,使用DoH协议示例代码:

import Network

if let aliUrl = URL(string: "https://您在控制台注册应用时分配的AccountID.alidns.com/dns-query"){
            let address1 = NWEndpoint.hostPort(host: "223.5.5.5", port: 443)
            let address2 = NWEndpoint.hostPort(host: "223.6.6.6", port: 443)
            let address3 = NWEndpoint.hostPort(host: "2400:3200::1", port: 443)
            let address4 = NWEndpoint.hostPort(host: "2400:3200:baba::1", port: 443)
            NWParameters.PrivacyContext.default.requireEncryptedNameResolution(true, fallbackResolver: .https(aliUrl, serverAddresses: [address1,address2,address3,address4]))
            }

在App范围内使用加密DNS,使用DoT协议示例代码:

import Network

let alidnsHost = NWEndpoint.hostPort(host: "您在控制台注册应用时分配的AccountID.alidns.com", port: 853)
        let address1 = NWEndpoint.hostPort(host: "223.5.5.5", port: 853)
        let address2 = NWEndpoint.hostPort(host: "223.6.6.6", port: 853)
        let address3 = NWEndpoint.hostPort(host: "2400:3200::1", port: 853)
        let address4 = NWEndpoint.hostPort(host: "2400:3200:baba::1", port: 853)
        NWParameters.PrivacyContext.default.requireEncryptedNameResolution(true, fallbackResolver: .tls(alidnsHost, serverAddresses: [address1,address2,address3,address4]))