Skip to content

DNS方案

zfl9 edited this page Jun 6, 2023 · 17 revisions

分享一些 DNS 玩法,比如给内置 DNS 方案套上 DoH/DoT,以及自定义 DNS 方案的例子。

欢迎大家在 Discussions 页面分享自己的 DNS 方案,我会不定时的将它们整理到 wiki 页面。

给内置 DNS 套上 DoH/DoT

https://github.com/AdguardTeam/dnsproxy 为例,给 direct 和 remote DNS 套上 DoH:

dns_direct='127.0.0.1#65001' # 指向 dnsproxy
dns_direct_white='223.5.5.5 223.6.6.6' # 加入白名单

dns_remote='127.0.0.1#65002' # 指向 dnsproxy
dns_remote_black='8.8.8.8 8.8.4.4' # 加入黑名单

# 如果给 remote DNS 套了 DoH/DoT,则不必启用 dns2tcp
dns2tcp_enable='false'

pre_start() {
    # 设置所属 group、setgid 权限位 (只需执行一次)
    set_dns_group dnsproxy

    # 启动 dnsproxy 进程,用于 direct DNS
    (dnsproxy -l 127.0.0.1 -p 65001 \
        -b 223.5.5.5:53 \
        -u https://dns.alidns.com/dns-query \
        </dev/null &>>/var/log/dnsproxy.log &)

    # 启动 dnsproxy 进程,用于 remote DNS
    (dnsproxy -l 127.0.0.1 -p 65002 \
        -b 223.5.5.5:53 \
        -u https://dns.google/dns-query \
        </dev/null &>>/var/log/dnsproxy.log &)
}

post_stop() {
    # 关闭 dnsproxy
    kill -9 $(pidof dnsproxy) &>/dev/null
}

extra_status() {
    _status "doh/direct" udp_port_is_exists 65001
    _status "doh/remote" udp_port_is_exists 65002
}

给内置 DNS 套上 AdGuardHome

来自:https://github.com/zfl9/ss-tproxy/discussions/218

使用 AdGuardHome 可以方便的查看 dns 日志、block 不想要的域名、以及广告过滤。

AdGuardHome 的配置和用法就不介绍了,这里只说下如何接入 ss-tproxy,挂到 dnsmasq 前面:

# 让 AdGuardHome 监听 53 端口
dns_mainport='53'

# dnsmasq 作为 AdGuardHome 的上游,监听非 53 端口
dnsmasq_bind_port='54'

pre_start() {
    # 设置所属 group、setgid 权限位 (只需执行一次)
    set_dns_group /path/to/AdGuardHome

    # 启动 AdGuardHome,以 systemd 为例
    systemctl start AdGuardHome
}

post_stop() {
    # 停止 AdGuardHome,以 systemd 为例
    systemctl stop AdGuardHome
}

自定义 DNS:CoreDNS + chinadns-ng

来自:https://github.com/zfl9/ss-tproxy/discussions/218#discussioncomment-5977470

  • coredns 监听 53,接收所有 DNS 请求,转发给 chinadns-ng
  • chinadns-ng 实现 global/gfwlist/chnroute 域名分流,与 ipset 联动
  • coredns 监听 127.0.0.1#65001,作为 chinadns-ng 的国内上游,配置 DoT
  • coredns 监听 127.0.0.1#65002,作为 chinadns-ng 的可信上游,配置 DoT

coredns 配置文件 /etc/ss-tproxy/Corefile:

# 有效时间单位: "ns", "us" (or "µs"), "ms", "s", "m", "h"
# (xyz) { ... } 是定义一个配置片段, 通过 import xyz 来引用
# import 也可以导入外部配置文件,具体见 import 插件的 README

# DNS 缓存相关
(enable_cache) {
    # 缓存在到达数量上限之后,才会被随机删除。
    # 默认缓存数量 256 * 39 = 9984
    cache {
        # 30 分钟之内查询过2次,当 ttl 剩余 10% 就会自动预查询
        prefetch 2 30m 10%
        # 立刻发送过期缓存, 同时后台刷新缓存, 提高效率
        serve_stale 24h immediate
        # 不要缓存 SERVFAIL
        servfail 0
        # 不要缓存 NXDOMAIN
        disable denial
        # 不要缓存这些域名 比如你的 ddns 域名
        # disable success ddns.example.com
    }
}

# /etc/hosts 相关
(enable_hosts) {
    hosts {
        # 默认会加载 /etc/hosts 以及下面自定义的 hosts
        #
        # 自定义 hosts 格式为
        # ip 域名 别名 别名 别名 ....
        # 1.2.3.4 example.com us1
        #
        # 如果 hosts 没有匹配, 就继续查询
        fallthrough
    }
}

(server_common) {
    # EDNS0 请求的缓冲区大小
    bufsize 1232
    # 记录错误日志 (stdout)
    errors
}

(forward_common) {
    # 禁用健康检查
    max_fails 0
}

# 监听 53,接收所有 DNS 请求
.:53 {
    import server_common
    import enable_cache
    import enable_hosts

    # 转发给 chinadns-ng,进行分流
    forward . 127.0.0.1:65353 {
        import forward_common
        # 因为 chinadns-ng 目前只支持 UDP
        prefer_udp
    }
}

# 作为 chinadns-ng 的国内上游
.:65001 {
    import server_common
    bind 127.0.0.1

    # 转发给 223.5.5.5 (DoT)
    forward . tls://223.5.5.5 {
        import forward_common
        tls_servername dns.alidns.com
    }
}

# 作为 chinadns-ng 的可信上游
.:65002 {
    import server_common
    bind 127.0.0.1

    # 转发给 8.8.8.8 (DoT)
    forward . tls://8.8.8.8 {
        import forward_common
        tls_servername dns.google
    }
}

配置 /etc/ss-tproxy/ss-tproxy.conf:

dns_custom='true'

# 初始化,脚本加载时调用
custom_dns_init() {
    set_dns_group coredns
    set_dns_group chinadns-ng
}

# 要加入白名单的ip,启动dns之前调用
custom_dns_whiteip() {
    # 格式同 ignlist.ext,一行一个
    echo "-223.5.5.5"
}

# 要加入黑名单的ip,启动dns之前调用
custom_dns_blackip() {
    # 格式同 gfwlist.ext,一行一个
    echo "-8.8.8.8"
}

# 启动dns进程,请务必以dns_procgroup身份运行
custom_dns_start() {
    pid_coredns=$(coredns </dev/null &>/var/log/coredns.log & echo $!)

    # 请使用最新版 chinadns-ng (version >= 2023.06.01)
    local chinadns_arg="-c 127.0.0.1#65001 -t 127.0.0.1#65002"

    if is_global_mode; then # 白名单 (ignlist)
        pid_chinadns=$(
            trap "" CHLD # 避免僵尸进程
            chinadns-ng $chinadns_arg \
            -m <(list_ext_domain ignlist.ext) \
            -d gfw \
            -a sstp_white,sstp_white6 \
            </dev/null &>/var/log/chinadns.log &
            echo $!
        )
    elif is_gfwlist_mode; then # 黑名单 (gfwlist)
        pid_chinadns=$(
            trap "" CHLD # 避免僵尸进程
            chinadns-ng $chinadns_arg \
            -g gfwlist.txt,<(list_ext_domain gfwlist.ext) \
            -d chn \
            -A sstp_black,sstp_black6 \
            </dev/null &>/var/log/chinadns.log &
            echo $!
        )
    elif is_chnroute_mode; then # 白名单 (ignlist,chnlist,chnroute) + 黑名单 (gfwlist)
        pid_chinadns=$(
            trap "" CHLD # 避免僵尸进程
            chinadns-ng $chinadns_arg \
            -m chnlist.txt,<(list_ext_domain ignlist.ext) \
            -g gfwlist.txt,<(list_ext_domain gfwlist.ext) \
            -a sstp_white,sstp_white6 \
            -A sstp_black,sstp_black6 \
            -4 sstp_white -6 sstp_white6 \
            </dev/null &>/var/log/chinadns.log &
            echo $!
        )
    fi
}

# 关闭dns进程,stop时调用
custom_dns_stop() {
    kill -9 $pid_coredns &>/dev/null
    kill -9 $pid_chinadns &>/dev/null
}

# 打印运行状态,status时调用
custom_dns_status() {
    _status "coredns" process_is_running $pid_coredns
    _status "chinadns" process_is_running $pid_chinadns
}

# 清空dns缓存,flush-dnscache时调用
custom_dns_flush() {
    ! ss_tproxy_is_started && return

    # 重启 coredns 进程
    kill -9 $pid_coredns
    pid_coredns=$(coredns </dev/null &>/var/log/coredns.log & echo $!)

    # 更新 pid 文件
    save_pidfile
}

# 此函数在start的最后一步执行,获取运行时状态,同extra_pid
custom_dns_pid() {
    # 格式同shell变量赋值,注意变量命名,防止冲突/覆盖
    # pid文件是一个shell脚本,下次执行时会source加载它
    echo "pid_coredns=$pid_coredns"
    echo "pid_chinadns=$pid_chinadns"
}