使用 AdGuard Home + Mosdns 搭建支持 DoH、去广告和国内外分流的 DNS 服务器

使用 AdGuard Home + Mosdns 搭建支持 DoH、去广告和国内外分流的 DNS 服务器

最近给自己的 VPS 搭了一套 DNS/DoH 服务,目标很明确:

  • 对外提供 DoH:https://dns.example.com/dns-query
  • 支持广告过滤
  • 支持国内外 DNS 分流
  • 不暴露公网 53 端口,降低被滥用风险
  • 可以访问被墙网站(比如 linuxdo)

🧭最终方案:

客户端
  -> https://dns.example.com/dns-query
  -> Nginx
  -> AdGuard Home
  -> Mosdns
  -> 国内/国外上游 DNS

架构说明

🧭这套方案里,各组件职责如下:

Nginx:负责 HTTPS/DoH 入口,反代到 AdGuard Home
AdGuard Home:负责广告过滤、查询日志、缓存、DNS 入口
Mosdns:负责国内外分流

请求链路:

客户端 DoH 请求
  -> Nginx 443
  -> 127.0.0.1:3000/dns-query
  -> AdGuard Home
  -> 127.0.0.1:5335
  -> Mosdns
  -> 国内域名走腾讯/阿里 DoH
  -> 国外域名走 Cloudflare/Google DoH

准备工作

服务器环境:

Ubuntu 24.04
Docker / Docker Compose
Nginx
一个已解析到服务器的域名:dns.example.com

安装基础工具:

apt update
apt install -y ca-certificates curl gnupg lsb-release dnsutils certbot

部署目录

我把 DNS 服务放在:

/opt/dns-stack

创建目录:

mkdir -p /opt/dns-stack/adguard/conf
mkdir -p /opt/dns-stack/adguard/work
mkdir -p /opt/dns-stack/mosdns

Docker Compose

创建 /opt/dns-stack/docker-compose.yml

services:
  mosdns:
    image: irinesistiana/mosdns:v5.3.4
    container_name: mosdns
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./mosdns:/etc/mosdns:ro
    command: ["mosdns", "start", "-c", "/etc/mosdns/config.yaml"]

  adguardhome:
    image: adguard/adguardhome:latest
    container_name: adguardhome
    restart: unless-stopped
    network_mode: host
    depends_on:
      - mosdns
    volumes:
      - ./adguard/work:/opt/adguardhome/work
      - ./adguard/conf:/opt/adguardhome/conf

这里使用 network_mode: host,但服务都绑定到 127.0.0.1,不会直接暴露公网 DNS 端口。

配置 Mosdns 国内外分流

先下载国内域名规则:

curl -fsSL "https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/direct-list.txt" \
  -o /opt/dns-stack/mosdns/geosite_cn.txt

创建 /opt/dns-stack/mosdns/config.yaml

log:
  level: info

plugins:
  - tag: forward_cn
    type: forward
    args:
      concurrent: 2
      upstreams:
        - addr: "https://doh.pub/dns-query"
        - addr: "https://dns.alidns.com/dns-query"

  - tag: forward_global
    type: forward
    args:
      concurrent: 2
      upstreams:
        - addr: "https://cloudflare-dns.com/dns-query"
        - addr: "https://dns.google/dns-query"

  - tag: cache
    type: cache
    args:
      size: 32768
      lazy_cache_ttl: 86400

  - tag: main_sequence
    type: sequence
    args:
      - exec: $cache
      - matches:
          - has_resp
        exec: accept
      - matches:
          - qname &/etc/mosdns/geosite_cn.txt
        exec: $forward_cn
      - matches:
          - has_resp
        exec: accept
      - exec: $forward_global

  - tag: udp_server
    type: udp_server
    args:
      entry: main_sequence
      listen: 127.0.0.1:5335

  - tag: tcp_server
    type: tcp_server
    args:
      entry: main_sequence
      listen: 127.0.0.1:5335

这个逻辑很简单:

命中国内域名列表 -> 走腾讯 DNSPod / 阿里 AliDNS
未命中 -> 走 Cloudflare / Google

配置 AdGuard Home

AdGuard Home 负责广告过滤和 DNS 查询入口。

创建 /opt/dns-stack/adguard/conf/AdGuardHome.yaml,核心配置如下:

http:
  address: 127.0.0.1:3000
  session_ttl: 720h

users: []

dns:
  bind_hosts:
    - 127.0.0.1
  port: 5353
  protection_enabled: true
  ratelimit: 20
  refuse_any: true
  upstream_dns:
    - 127.0.0.1:5335
  bootstrap_dns:
    - 223.5.5.5
    - 119.29.29.29
    - 1.1.1.1
  cache_enabled: true
  cache_size: 4194304
  cache_optimistic: true
  trusted_proxies:
    - 127.0.0.0/8
    - ::1/128

tls:
  enabled: false
  allow_unencrypted_doh: true

filters:
  - enabled: true
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
    name: AdGuard DNS filter
    id: 1

  - enabled: true
    url: https://filters.adtidy.org/extension/chromium/filters/224.txt
    name: AdGuard Chinese filter
    id: 2

whitelist_filters: []

schema_version: 34

这里用了两条广告过滤规则:

AdGuard DNS filter
AdGuard Chinese filter

前者负责通用广告、追踪和恶意域名,后者针对中文互联网广告场景。

启动服务

cd /opt/dns-stack
docker compose up -d

✅检查状态:

docker compose ps
ss -tulpn | grep -E "3000|5335|5353"

预期监听:

127.0.0.1:3000  AdGuard Home Web/DoH 后端
127.0.0.1:5353  AdGuard Home DNS
127.0.0.1:5335  Mosdns

本机测试:

dig @127.0.0.1 -p 5353 baidu.com
dig @127.0.0.1 -p 5353 google.com

申请 HTTPS 证书

假设域名是:

dns.example.com

先确保它解析到服务器公网 IP。

使用 webroot 申请证书:

certbot certonly \
  --webroot \
  -w /home/web/letsencrypt \
  -d dns.example.com \
  --agree-tos \
  --register-unsafely-without-email \
  --non-interactive

然后把证书复制到 Nginx 使用的目录:

cp -L /etc/letsencrypt/live/dns.example.com/fullchain.pem /home/web/certs/dns.example.com_cert.pem
cp -L /etc/letsencrypt/live/dns.example.com/privkey.pem /home/web/certs/dns.example.com_key.pem

chmod 644 /home/web/certs/dns.example.com_cert.pem
chmod 600 /home/web/certs/dns.example.com_key.pem

配置 Nginx DoH 反代

创建 Nginx 配置:

server {
    listen 80;
    listen [::]:80;
    server_name dns.example.com;

    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /var/www/letsencrypt;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    listen 443 quic;
    listen [::]:443 quic;

    server_name dns.example.com;

    ssl_certificate /etc/nginx/certs/dns.example.com_cert.pem;
    ssl_certificate_key /etc/nginx/certs/dns.example.com_key.pem;

    location = /dns-query {
        proxy_pass http://127.0.0.1:3000/dns-query;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
        add_header Alt-Svc 'h3=":443"; ma=86400';
    }

    location / {
        return 404;
    }

    client_max_body_size 1m;
}

✅检查并 reload:

nginx -t
nginx -s reload

如果 Nginx 在 Docker 里:

docker exec nginx nginx -t
docker exec nginx nginx -s reload

验证 DoH

✅用 curl 验证:

curl -I \
  --doh-url https://dns.example.com/dns-query \
  https://www.baidu.com

能正常返回 HTTP 200,说明这次请求的 DNS 查询已经走了你的 DoH 服务。

直接访问:

curl -I https://dns.example.com/dns-query

返回 400 或 405 是正常的,因为 DoH endpoint 需要标准 DNS query 请求,不是普通网页。

不建议开放公网 53

🧭这套方案只对外开放 HTTPS DoH,不开放公网 UDP/TCP 53。

原因很简单:

公网 53 容易被滥用成开放递归 DNS
也可能参与 DNS 放大攻击

所以推荐:

公网:只开放 443 DoH
本机:AdGuard / Mosdns 只监听 127.0.0.1

最终效果

最终链路:

客户端
  -> https://dns.example.com/dns-query
  -> Nginx
  -> AdGuard Home 去广告
  -> Mosdns 国内外分流
  -> 国内域名走 doh.pub / dns.alidns.com
  -> 国外域名走 cloudflare-dns.com / dns.google

🧭这套方案的优点:

  • 不暴露公网 53
  • 支持 DoH
  • 支持中文广告过滤
  • 支持国内外 DNS 分流
  • 所有核心服务都跑在 Docker 里,方便维护
  • Nginx 只暴露一个标准 HTTPS 入口,结构比较干净

后续如果想增强广告过滤,可以考虑再加一条 Hagezi Pro 或者 anti-ad 规则,但不建议一口气塞太多规则。DNS 过滤规则不是越多越好,规则越多,误杀排查越麻烦。

我的成品【直接使用】:

使用 AdGuard Home + Mosdns 搭建支持 DoH、去广告和国内外分流的 DNS 服务器-羊的小栈
使用 AdGuard Home + Mosdns 搭建支持 DoH、去广告和国内外分流的 DNS 服务器
此内容为付费阅读,请付费后查看
100积分
付费阅读
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容