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直连处理、以及iOS上的SNI问题等,而iOS 14 上的 Encrypted DNS 功能很好的解决了集成SDK的方案存在的问题,您可以参考以下示例了解如何设置阿里云公共DNS为加密DNS默认解析器。

警告
  1. iOS14原生加密DNS方案仅支持真机运行且仅支持iOS14及其以上系统版本。

  2. 建议开发者使用DoH/DoT加密地址

  3. 为确保app正常运行,请开发者自实现降级Local DNS 兜底机制,避免因DoH/DoT服务异常导致域名解析失败。

  4. 该方案无法保障SLA(因为我们SDK中有多级兜底),但用户可以通过自己实现兜底逻辑来规避平台侧异常的风险。

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

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

  • 第一种方式是针对单个App的所有连接启用加密DNS。

如果您只想为您的App使用加密DNS,而非整个终端系统全部接入使用加密DNS。您可以适配NetworkExtensionnw_privacy_context_t,只对您的整个App开启加密DNS,App内发起的每个DNS解析都会使用这个配置。我们为您提供了针对单个App的所有连接启用DoHdemo示例供您参考。

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

#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
        nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
        nw_endpoint_t dohResolverEndpoint = nw_endpoint_create_url("https://*****-************.alidns.com/dns-query");//DoH加密地址
        nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "443");
        nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "443");
        nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "443");
        nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "443");
        nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_https(dohResolverEndpoint);
        nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
        nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
        nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
        nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
        nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
    }

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

#import <NetworkExtension/NetworkExtension.h>
if (@available(iOS 14.0, *)){
        nw_privacy_context_t defaultPrivacyContext = NW_DEFAULT_PRIVACY_CONTEXT;
        nw_endpoint_t dotResolverEndpoint = nw_endpoint_create_host("******-************.alidns.com", "853");//DoT加密地址
        nw_endpoint_t v4ResolverEndpoint1 = nw_endpoint_create_host("223.5.5.5", "853");
        nw_endpoint_t v4ResolverEndpoint2 = nw_endpoint_create_host("223.6.6.6", "853");
        nw_endpoint_t v6ResolverEndpoint1 = nw_endpoint_create_host("2400:3200::1", "853");
        nw_endpoint_t v6ResolverEndpoint2 = nw_endpoint_create_host("2400:3200:baba::1", "853");
        nw_resolver_config_t fallbackResolvers = nw_resolver_config_create_tls(dotResolverEndpoint);
        nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint1);
        nw_resolver_config_add_server_address(fallbackResolvers, v4ResolverEndpoint2);
        nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint1);
        nw_resolver_config_add_server_address(fallbackResolvers, v6ResolverEndpoint2);
        nw_privacy_context_require_encrypted_name_resolution(defaultPrivacyContext, true, fallbackResolvers);
    }

⽤户可以通过以下代码实现降级Local DNS 兜底机制,通过 URLSessionDelegate 获取 NSURLSessionTaskTransactionMetrics ,在请求失败后关闭 DoH/DoT。

#pragma mark URLSession Delegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
    if ([metrics.transactionMetrics count] <= 0) return;
    [metrics.transactionMetrics enumerateObjectsUsingBlock:^(NSURLSessionTaskTransactionMetrics *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
        if (obj.resourceFetchType == NSURLSessionTaskMetricsResourceFetchTypeNetworkLoad) {
            if (@available(iOS 14.0, *)) {
                NSURLSessionTaskMetricsDomainResolutionProtocol dnsProtocol = obj.domainResolutionProtocol;
                NSLog(@"%@",[NSString stringWithFormat:@"DNS类型为 %ld", (long)dnsProtocol]);
                NSLog(@"%@",[NSString stringWithFormat:@"0:未知,1:UDP,2:TCP,3:TLS,4:HTTPS"]);
                if (dnsProtocol == NSURLSessionTaskMetricsDomainResolutionProtocolUnknown) {
                    NSLog(@"%@",[NSString stringWithFormat:@"dns来源未知,关闭Doh"]);
                    nw_privacy_context_require_encrypted_name_resolution(NW_DEFAULT_PRIVACY_CONTEXT, false, nil);
                }
            }
        }
    }];
}
  • 第二种方式是针对整个终端内所有应用的连接启用加密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://*****-************.alidns.com/dns-query")//DoH加密地址
            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 = "******-************.alidns.com"//DoT加密地址
            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