侧边栏壁纸
博主头像
汪洋

即使慢,驰而不息,纵会落后,纵会失败,但一定可以达到他所向的目标。 - 鲁迅

  • 累计撰写 212 篇文章
  • 累计创建 81 个标签
  • 累计收到 160 条评论

DoH ( DNS over HTTPS) 服务

汪洋
2023-02-20 / 0 评论 / 0 点赞 / 886 阅读 / 4,108 字

一、前言

DoH(DNS over HTTPS),顾名思义。除了最常用的UDP外,还有DoT(DNS over TLS),DNS over HTTP(服务提供商自定义)等方案,对比如下:

image.png

移动端的 DNS 优化已经有很多实践,最常见的是 DNS over HTTP,通过加密的 HTTP 请求规避运营商对 DNS 的 UDP 包劫持,从而优化 App 访问服务器的延迟。但这个方案并没有形成统一的标准,通常需要内嵌 DNS 服务提供商的 SDK,通过访问固定的 BGP 或任播 IP 获取 DNS 响应。

大概是意识到 DNS 在移动互联网中的扮演越来越重要的角色,在 DoT 和 DoH 的规范相继推出后,许多 DNS 服务提供商都跟进了部署,国内的阿里云、 DNSPod,国外的谷歌、Cloudflare 等目前已经推出了免费的 DoT 和 DoH 服务。

客户端方面,常用的 Chrome、FireFox 已经支持了自定义 DoH 服务器,macOS、iOS 也可通过配置文件设置系统范围的默认 DoH 服务器。

二、部署方案

DoH 本质上就是一个 HTTP 请求,只是目前协议定义要求启用 TLS 与 HTTP/2。最初没有跑通 coredns 的 DoH 时,使用了 nginx 作为前端转发 DoH 请求到 doh-server,然后 doh-server 使用本地的 coredns 服务作为上游。

最近再仔细研究了下文档,发现 coredns 已经支持了 DoH 服务,可直接对外暴露服务,或者通过 nginx 转发来复用已经部署好的 web 服务。

2.1 nginx + doh-server + coredns

https://github.com/m13253/dns-over-https 是一个提供 DNS over HTTP 的服务,需要一个 web 前端和一个 DNS 后端,可用的 docker 镜像地址为:[satishweb/doh-server](https://hub.docker.com/r/satishweb/ doh-server),使用 doh-server 时,DNS 请求流转如下:

HTTP Service -> doh-server -> DNS Server

RFC8484 中指定使用 /dns-query 路径作为默认查询路径,因此只需要将该路径前缀的请求转发到 doh-server 即可,如下:

nginx 配置(已配置好 TLS 与 HTTP2 )

server {
    listen 443 ssl http2 fastopen=256 reuseport;
    listen [::]:443 ssl http2 fastopen=256 reuseport;
    server_name doh.wbuntu.com
    ...
    location /dns-query {
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    # show real IP
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8053;
  }
}

doh-server
使用 hostNetwork 模式启动服务,监听 8053 端口

docker run -d --restart unless-stopped --network host --name doh-server \
  -e UPSTREAM_DNS_SERVER="udp:127.0.0.1:53" \
  -e DOH_HTTP_PREFIX="/dns-query" \
  -e DOH_SERVER_LISTEN="127.0.0.1:8053" \
  -e DOH_SERVER_TIMEOUT="10" \
  -e DOH_SERVER_TRIES="3" \
  -e DOH_SERVER_VERBOSE="true" \
  satishweb/doh-server

coredns
coredns 配置文件如下

➜  ~ tree /etc/coredns/
/etc/coredns/
└── Corefile

0 directories, 1 files
➜  cat /etc/coredns/Corefile
.:53 {
    bind 127.0.0.1
    forward . 1.1.1.1 1.0.0.1
    log
    errors
    cache
}

使用 hostNetwork 模式启动服务,监听 53 端口

docker run -d --restart unless-stopped --network host --name coredns \
  -v /etc/coredns:/etc/coredns \
  coredns/coredns \
  -conf /etc/coredns/Corefile

服务启动后,我们可以得到一个自定义的 DoH 服务:https://doh.wbuntu.com/dns-query

2.2 coredns

目前 coredns 支持作为 DoH 服务端,不支持连接上游 DoH 服务器,上游服务器可使用 UDP 和 DoT。

直接对外暴露服务需要使用有效的 TLS 证书,coredns 配置文件及证书位置如下:

➜  ~ tree /etc/coredns/
/etc/coredns/
├── Corefile
├── tls.crt
└── tls.key

0 directories, 3 files
➜  cat /etc/coredns/Corefile
https://.:443 {
    tls /etc/coredns/tls.crt /etc/coredns/tls.key
    bind 0.0.0.0
    forward . 1.1.1.1 1.0.0.1
    log
    errors
    cache
}

使用 hostNetwork 模式启动服务,监听 443 端口

docker run -d --restart unless-stopped --network host --name coredns \
  -v /etc/coredns:/etc/coredns \
  coredns/coredns \
  -conf /etc/coredns/Corefile

服务启动后,我们可以得到一个自定义的 DoH 服务:https://doh.wbuntu.com/dns-query

2.3 nginx + coredns

直接暴露 coredns 服务到公网需要占用端口,coredns 在未配置 TLS 证书时,可使用 nginx 作为前端来复用 web 服务,如下:

nginx 配置(已配置好 TLS 与 HTTP2 )

server {
    listen 443 ssl http2 fastopen=256 reuseport;
    listen [::]:443 ssl http2 fastopen=256 reuseport;
    server_name doh.wbuntu.com
    ...
    location /dns-query {
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Host $http_host;
    # show real IP
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_pass http://127.0.0.1:8053;
  }
}

coredns
coredns配置文件如下

➜  ~ tree /etc/coredns/
/etc/coredns/
└── Corefile

0 directories, 1 files
➜  cat /etc/coredns/Corefile
https://.:8053 {
    bind 127.0.0.1
    forward . 1.1.1.1 1.0.0.1
    log
    errors
    cache
}

使用 hostNetwork 模式启动服务,监听 8053 端口

docker run -d --restart unless-stopped --network host --name coredns \
  -v /etc/coredns:/etc/coredns \
  coredns/coredns \
  -conf /etc/coredns/Corefile

服务启动后,我们可以得到一个自定义的 DoH 服务:https://doh.wbuntu.com/dns-query

三、测试

使用谷歌浏览器配置DoH服务:Settings -> Secutiry and Privacy -> Secutiry -> Advanced -> Use secure DNS

image.png

使用 Go 代码测试:

package main

import (
        "encoding/base64"
        "fmt"
        "github.com/miekg/dns"
        "io/ioutil"
        "net/http"
        "os"
)

func main() {
       query := dns.Msg{}
       query.SetQuestion("www.google.com.", dns.TypeA)
       msg, _ := query.Pack()
       b64 := base64.RawURLEncoding.EncodeToString(msg)
       resp, err := http.Get("https://doh.wbuntu.com/dns-query?dns=" + b64)
       if err != nil {
            fmt.Printf("Send query error, err:%v\n", err)
            os.Exit(1)
       }
       defer resp.Body.Close()
       bodyBytes, _ := ioutil.ReadAll(resp.Body)
       response := dns.Msg{}
       response.Unpack(bodyBytes)
       fmt.Printf("Dns answer is :%v\n", response.String())
}
0

评论区