亚马逊AWS官方博客

基于亚马逊云科技无服务器服务构建 DNS over Https 解决方案

概述

DNS over HTTPS(DoH)是一种加密的 DNS 通讯协议,使用 HTTPS 协议来封装和传输 DNS 查询请求和响应,相比于传统的明文 DNS 协议,DoH 可以保护用户的隐私和安全,避免被中间人攻击或窃听。作为一种现代化的 DNS 解决方案,DoH 可以广泛应用于游戏、互联网、企业等多种场景。在本文中,我们将介绍如何基于亚马逊云科技无服务器服务(Serverless)构建一个高可用、可扩展、安全的 DoH 解决方案。借助亚马逊云科技的无服务服务优势,整个系统架构将充分利用云计算的按需付费和自动伸缩等特性,实现更高的性能和成本效益。

技术挑战

  • 域名防劫持,使用 HTTP(HTTPS)协议进行域名解析,域名解析请求直接发送至 HTTPDNS 服务器,绕过运营商 Local DNS,避免域名劫持问题。
  • 就近接入,由于运营商策略的多样性,其 Local DNS 的解析结果可能不是最近、最优的节点,HTTPDNS 能直接获取客户端 IP 地址,基于客户端 IP 获得最精准的解析结果,让客户端就近接入。
  • 全球覆盖,全球多节点覆盖,可以手动指定 Route53、CloudFlare 等国际权威 DNS,实现全球精准解析。
  • 高可用,提供稳定可靠的解析服务,同时免维护成本。

使用到的核心产品介绍

  • AWS Global Accelerator 是一项网络服务,旨在提高应用程序的可用性和网络性能。它通过优化网络路径,将流量路由到最接近用户的 AWS 区域或虚拟私有云(VPC),从而减少网络延迟,非常适合需要低延迟和高可用性的全球分布式应用程序,例如网站、API、游戏服务器等。它可以显著提高应用程序的性能和可靠性,同时简化网络配置和管理。
  • AWS Lambda 是亚马逊云科技提供的一种无服务器计算服务。它允许您运行代码,而无需预先配置或管理服务器。您只需上传您的代码,Lambda 会自动运行并根据需求分配计算资源。AWS Lambda 广泛应用于构建无服务器应用程序、数据处理管道、Web 应用程序后端、移动应用程序后端、物联网(IoT)应用程序等场景。它可以帮助您构建更加敏捷、高度可扩展和经济高效的应用程序。
  • AWS Application Load Balancer(ALB)应用程序负载均衡器运行于请求级别(第 7 层),可根据请求的内容将流量路由至目标(EC2 实例、容器、IP 地址和 Lambda 函数)。应用程序负载均衡器最适合 HTTP 和 HTTPS 流量的高级负载均衡,面向交付包括微服务和基于容器的应用程序在内的现代应用程序架构,提供高级请求路由功能。应用程序负载均衡器通过确保始终使用最新的 SSL/TLS 密码和协议,简化并提高应用程序的安全性。

系统结构

本方案基于无服务器服务(Serverless)构建 DoH 系统,使用 AWS Global Accelerator + ALB + Lambda 实现,完全 Serverless 的架构免去运维成本,Lambda 可以在 AWS 区域中的多个可用区中运行,为无服务器应用程序提供了高度的可靠性和容错能力;考虑到部署的便携性,本方案可通过 CDK 快速一键部署。

核心功能说明:

  • 采用 AWS Global Accelerator 实现全球就近接入,利用 AWS 全球基础设施的性能、安全性和可用性,在其中一个 Global Accelerator 边缘站点就近接入,用户就可以通过 anycast IP 静态地址访问,享受独立于 DNS 的确定性路由。
  • Global Accelerator 每个监听器可绑定不同 Region 的 Endpoint Group。根据用户来源位置,AGA 将请求发送给就近的 AWS Region 中的目标。AGA 提供 Endpoint 健康检查功能, 并防止把请求发送到不健康的目标。
  • ALB+Lambda 提供 httpProxy,通过解析 X-Forwarded-For(XFF)客户端真实 IP 地址进行地理位置的 DNS 解析策略。
  • Lambda 本身是多 AZ 高可用的托管服务,多 Region 部署(可选)Lambda 提供了更高的可靠性并降低全球用户访问延时。
  • 支持 IPv4、IPv6 Dual-Stack 解析。

方案部署

前提条件

  1. 通过如下地址下载部署代码 http://github.com/aws-samples/serverless-httpdns
  2. 部署使用的 IAM user 或者 role 有 IAM/CloudFormation/AGA/ALB/Lambda 的权限。

如果选择在您的本地常用电脑上进行部署请完成以下准备工作:

  1. 安装 Python,请确保在您的本地开发机或部署机上有高于 3.8 的 python 版本,如果没有请参考 https://www.python.org/downloads/ 进行下载和安装。
  2. 安装 AWS CLI,请参考 https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html 进行安装,快速设置账号权限请参考 https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html
  3. 安装 AWS CDK
    # 安装 nvm node cdk
    curl -o nvm_install.sh https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh
    sh nvm_install.sh
    . ~/.nvm/nvm.sh
    nvm install 16
    node -v 
    # 16.18.0
    npm -v
    npm install -g aws-cdk
    cdk --version
    

如果选择在 AWS Cloud9 上进行部署(推荐),请在 AWS 控制台上启动一个用户部署的 Cloud9 环境,具体步骤如下:

  1. 在 AWS console 上使用搜索栏搜索 “Cloud9″,或者在 “服务” 下拉菜单的 “开发人员工具” 部分找到 “Cloud9” 服务。
  2. 单击 “创建环境” 创建一个新的 Cloud9 环境。
  3. 在 “创建环境” 向导中,为您的环境提供一个名称,填写在 “名称” 字段中。
  4. 选择您喜欢的环境类型。您可以创建一个全新的环境。
  5. 选择 Cloud9 环境的实例类型,这里我们选择 small 即可。
  6. 平台保持默认 Amazon Linux2 即可,下方的超时自动停止实例时间保持 30mins 即可,在您没有在 IDE 上操作 30mins 之后,这个实例将会被停止,以免产生不必要的费用。
  7. 如果需要打 Tags,可以进行填写,其他部分保持默认,点击“创建”即可。
  8. Cloud9 环境将被创建,这可能需要一些时间。一旦准备就绪,可以打开 Cloud9 集成开发环境(IDE),在这里您可以开始工作。

部署步骤

  1. 在 Cloud9 的 IDE 中打开一个 Terminal,使用以下命令下载代码到当前目录
    git clone http://github.com/aws-samples/serverless-httpdns.git
    cd serverless-httpdns/
    
  2. 构建 cdk 环境并进行部署,整个过程大约 12mins
    npm install -g aws-cdk
    npm install
    cdk bootstrap aws://${YOUR_ACCOUNT_ID}/${AWS_REGION_CODE}
    cdk deploy --all
    

将会部署两个 stack:

  • 创建一个新的 vpc 以及一个 Lambda Function,最终通过 ALB 将服务暴露出来
  • 创建一个 AGA Endpoint,通过 AGA 提供的全球唯一 Anycast IP 作为整个服务的全球接入点

验证

部署结束后,可以在控制台的输出中看到此次我们部署的 stack 的一些输出,后面我们可以根据输出中的 GLOBAL_ACCELERATOR_ARN 字段来获取 AGA 的 Static IP set address,通过控制台或者 CLI 获取 IP 地址。

  • CLI 获取方式:
    aws globalaccelerator describe-accelerator \
        --accelerator-arn ${GLOBAL_ACCELERATOR_ARN} \
        --query 'Accelerator.IpSets[*].IpAddresses' \
        --region us-west-2 --output table
    
  • 控制台获取地址方式:
  1. 通过 curl 命令进行验证
    curl -XPOST http://${GA_STATIC_IP_ADDRESS} \
    -H 'Content-Type: application/json' \
    -d '{"recordName":"www.amazon.com","recordType":"A"}'
    
    # 结果返回
    "d3ag4hukkh62yn.cloudfront.net. 60 IN A 3.163.18.236"%
    
  2. 代码方式调用 Doh 服务

将要查询的 recordName和recordType 参数通过 Post 方式提交查询请求,即可获得 DNS 应答报文。

下面是一段 python 代码,演示如何进行域名查询:

import requests

# 定义要发送的数据
data = {'recordName': 'www.amazon.com', 'recordType': 'A'}

# 定义请求头
headers = {'Content-Type': 'application/json'}

# 发送POST请求
url = 'http://${GA_STATIC_IP_ADDRESS}'
response = requests.post(url, json=data, headers=headers)

# 检查请求是否成功
if response.status_code == 200:
    # 输出响应体
    print(response.text)
else:
    print(f'Request failed with status code: {response.status_code}')

运行结果如下:

python test_doh.py 
"d3ag4hukkh62yn.cloudfront.net. 60 IN A 3.163.18.236"

方案成本分析

成本计算按照部署在 us-east-1 区域,请求 Doh 服务 100 万次/每天来计算成本。

结论

本方案可以做为客户端/APP 进行正常 UDP DNS 解析失败后的 Backup,在遇到 LocalDNS 劫持情况下保障可以得到正确的解析结果,提高客户系统的可用性。基于无服务器服务部署 DoH 服务,对于客户来讲降低了部署和维护难度的同时也利用 Lambda 本身的特点实现了高可用。同时 Serverless 完全按照使用计费,不产生查询就不会带来成本,多 Region 部署 Lambda 既能就近加速解析速度,也提供了近乎零成本的 HA 容灾方式。

附录

  1. https://aws.amazon.com/cn/lambda/
  2. https://aws.amazon.com/cn/global-accelerator
  3. https://aws.amazon.com/cn/cdk/
  4. https://www.dnspython.org/

本篇作者

陈汉卿

亚马逊云科技解决方案架构师,负责基于亚马逊云科技云计算方案的咨询、架构设计及落地,拥有多年移动互联网研发及技术团队管理经验,在云原生微服务以及云迁移等方向有丰富的实践经验。

薛佳庆

亚马逊云科技解决方案架构师,负责为互联网出海客户提供云上服务的解决方案,在无服务器、容器技术、数据分析等方面有丰富的经验。

秦镜高

亚马逊云科技资深解决方案架构师,负责基于亚马逊云科技云计算方案的架构设计,帮助客户利用领先的云服务技术构建更具创新性的应用。加入亚马逊云科技之前,有 10 多年丰富的游戏开发和架构设计实践经验。

王骏兴

亚马逊云科技边缘产品架构师,负责亚马逊云科技 Edge 服务领域在中国的技术推广。在 CDN 内容分发以及 WAF 领域拥有多年实战经验,专注于边缘服务设计以及体验优化。