在访问某些有地域限制的网络服务(如流媒体、AI 工具等)时,我们常常会因为 IP 地址不在服务区而受限。传统的代理或 VPN 方案虽然有效,但可能会影响整个网络的连接速度,或配置较为复杂。
本文档旨在提供一种高性能、低资源消耗且配置相对简单的解决方案:通过在有原生 IP 的 VPS 上搭建一个 DNS 解锁服务,实现对特定域名的“解锁”,同时不影响对其他网站的正常访问。
本方案的核心是 DNS 劫持 与 SNI 代理 的结合,整个流程对客户端透明。
DNS 劫持 (Dnsmasq)
aistudio.google.com)时,Dnsmasq 不返回其真实 IP,而是“欺骗”性地返回我们 VPS 自己的公网 IP。www.debian.org),Dnsmasq 则正常返回其真实 IP。SNI 代理 (Nginx)
优点:
假设您的 VPS 公网 IPv4 地址为 a.a.a.a。
bash自动换行:关放大阅读展开代码# 更新软件包列表 sudo apt update # 安装 Dnsmasq 和功能齐全的 Nginx Extras sudo apt install -y dnsmasq nginx-extras
编辑 Dnsmasq 主配置文件:
bash自动换行:关放大阅读展开代码sudo nano /etc/dnsmasq.conf
将以下内容覆盖到文件中,并将所有 a.a.a.a 替换为您的 VPS 公网 IP。
plaintext自动换行:关放大阅读展开代码# --- 基础配置 --- # 阻止不规范的 DNS 请求 domain-needed bogus-priv # 不读取系统的 resolv.conf,完全手动指定上游 no-resolv # 同时向上游查询,并使用最快返回的结果 all-servers # 手动指定上游 DNS 服务器 server=8.8.8.8 server=1.1.1.1 # 增加 DNS 缓存 cache-size=2048 # 为手动指定的 address 记录设置 60 秒 TTL local-ttl=60 # 明确指定监听的 IP 地址(本机和公网) listen-address=127.0.0.1,a.a.a.a # --- 自定义域名劫持列表 --- # GPT/OpenAI address=/.openai.com/a.a.a.a address=/openai.com/:: address=/.chatgpt.com/a.a.a.a address=/.chatgpt.com/:: # Google AI Studio address=/aistudio.google.com/a.a.a.a address=/aistudio.google.com/:: address=/.ai.google.com/a.a.a.a address=/.ai.google.com/:: # ... 在此添加更多您需要的域名 ...
配置解读:
address=/.openai.com/a.a.a.a表示匹配 openai.com域名规则的返回 a.a.a.a的ip;address=/openai.com/::匹配该规则的响应空回复(此处如此设置是因为dns服务器没有ipv6地址);当如此设置之后,一般服务请求时,如果同时存在ipv4,和ipv6,会优先选择ipv6进行响应,此时由于ipv6响应设置为空回复,会自动回退到使用ipv4请求服务;如果dns服务器具有ipv6地址,可以改为如下形式
bash自动换行:关放大阅读展开代码# a.a.a.a为ipv4,b.b.b.b为ipv6 address=/.openai.com/a.a.a.a address=/openai.com/b.b.b.b
编辑 Nginx 主配置文件:
bash自动换行:关放大阅读展开代码sudo nano /etc/nginx/nginx.conf
将以下内容覆盖到文件中,并将所有 a.a.a.a 替换为您的 VPS 公网 IP。
nginx自动换行:关放大阅读展开代码# 加载动态 stream 模块,必须放在文件最顶部 load_module modules/ngx_stream_module.so; user www-data; worker_processes auto; pid /run/nginx.pid; error_log /var/log/nginx/error.log info; events { worker_connections 1024; } stream { # 定义日志格式,方便调试 log_format main '[$time_local] $remote_addr to $server_addr' ' proto: $protocol status: $status' ' upstream: $upstream_addr'; access_log /var/log/nginx/stream-access.log main; # 核心的 SNI -> 后端映射规则 map $ssl_preread_server_name $filtered_sni_name { # GPT/OpenAI ~^(.*|)openai\.com$ $ssl_preread_server_name; ~^(.*|)chatgpt\.com$ $ssl_preread_server_name; # Google AI Studio ~^aistudio\.google\.com$ $ssl_preread_server_name; ~^(.*|)ai\.google\.com$ $ssl_preread_server_name; # ... 在此添加与 Dnsmasq 列表对应的域名 ... # 默认值:防止服务器被用作开放代理 default "127.255.255.255"; } # 监听和转发服务器 server { # 指定 DNS 解析器,仅做 IPv4 解析 resolver 8.8.8.8 1.1.1.1 ipv6=off; # 监听所有 IPv4 地址的 443 端口 listen 443; # 开启 SNI 预读 ssl_preread on; # 将流量转发到 map 计算出的后端地址 proxy_pass $filtered_sni_name:443; # 强制对外连接时使用此源 IP 地址 proxy_bind a.a.a.a; } }
Debian 12 默认限制 Dnsmasq 只为局域网服务,我们需要移除此限制。
bash自动换行:关放大阅读展开代码sudo systemctl edit dnsmasq.service
ini自动换行:关放大阅读展开代码[Service] ExecStart= ExecStart=/usr/sbin/dnsmasq -x /run/dnsmasq/dnsmasq.pid -u dnsmasq -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new --no-resolv --conf-file=/etc/dnsmasq.conf
假设您允许访问的客户端 IP 为 b.b.b.b。
bash自动换行:关放大阅读展开代码# 设置默认策略 sudo ufw default deny incoming sudo ufw default allow outgoing # 允许您自己的 IP 访问 SSH, DNS, Nginx sudo ufw allow from b.b.b.b to any port 22 proto tcp sudo ufw allow from b.b.b.b to any port 53 sudo ufw allow from b.b.b.b to any port 443 proto tcp # 允许本机测试 sudo ufw allow from 127.0.0.1 to any port 53 sudo ufw allow from 127.0.0.1 to any port 443 proto tcp # 启用防火墙 sudo ufw enable
bash自动换行:关放大阅读展开代码sudo systemctl daemon-reload sudo systemctl restart dnsmasq sudo nginx -t && sudo systemctl restart nginx
bash自动换行:关放大阅读展开代码# 应返回 VPS 的 IP a.a.a.a dig @127.0.0.1 aistudio.google.com # 应返回 debian.org 的真实 IP dig @127.0.0.1 www.debian.org
bash自动换行:关放大阅读展开代码# 应成功完成 TLS 握手并返回网页内容 curl --resolve 'aistudio.google.com:443:127.0.0.1' https://aistudio.google.com -v
systemd-resolved)我们将配置 systemd-resolved 来使用我们的 VPS DNS,并实现自动故障回退和与 NetBird 等工具的兼容。
在bash自动换行:关放大阅读展开代码sudo nano /etc/systemd/resolved.conf
[Resolve] 部分添加或修改,将 a.a.a.a 替换为您的 VPS 公网 IP:
ini自动换行:关放大阅读展开代码[Resolve] DNS=a.a.a.a 8.8.8.8 1.1.1.1
systemd-resolved 接管系统 DNS:
bash自动换行:关放大阅读展开代码sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
bash自动换行:关放大阅读展开代码sudo systemctl restart systemd-resolved
在客户端上执行以下测试:
dig aistudio.google.com (应返回 a.a.a.a)dig www.debian.org (应返回其真实 IP)dig 一个内部域名,应能正常解析。sudo systemctl stop dnsmasq。sudo resolvectl flush-caches。dig aistudio.google.com 应该能够正常解析,但是解析地址变为真正的公网地址,而不是a.a.a.a(dns解锁服务器的ip)。dig www.debian.org 应该依然能够成功解析。sudo systemctl start dnsmasq 恢复服务。sudo systemctl restart systemd-resolved 来强制重置状态。Q1: 在 VPS 上测试 dig @127.0.0.1 ... 返回 REFUSED?
--local-service 限制。请参考 3.4 节 的步骤,通过创建 systemd 覆盖文件来移除此限制。Q2: 在 VPS 上测试 curl --resolve ... 返回 Connection refused?
127.0.0.1:443 上监听。请参考 3.3 节,将 listen 指令修改为 listen 443; 或 listen 0.0.0.0:443;,使其监听所有 IPv4 地址。Q3: 客户端 DNS 配置重启后被覆盖?
/etc/resolv.conf。请严格按照 4.1 节 的步骤,通过配置 systemd-resolved 并创建符号链接来实现持久化。Q4: VPS 上的 Dnsmasq 恢复后,客户端没有立即切回使用它?
systemd-resolved 的正常行为,它会暂时“记住”失效的服务器。要立即恢复,请在客户端上执行 sudo systemctl restart systemd-resolved 来重置其内部状态。部署完毕后应该对特定的ip范围限制使用,放置服务的滥用
bash自动换行:关放大阅读展开代码# 此处使用防火墙限制,b.b.b.b为你允许访问服务的ip sudo ufw allow from b.b.b.b to any port 53 sudo ufw allow from b.b.b.b to any port 443 proto tcp
目前未解决,原因不详
在客户端上配置完 systemd-resolved 后,有时会发现 DNS 解析结果不稳定,在我们的 VPS DNS 地址和另一个公共 DNS 地址(如 1.1.1.1)之间随机跳变。这是因为客户端的网络接口(如 eth0)从更高优先级的配置源(如云服务商的 DHCP、netplan 或 NetworkManager)获取了 DNS 设置,与我们设置的全局 DNS 发生了冲突。
以下是如何诊断并从根源上解决此问题的步骤。
在客户端上运行以下命令,查看详细的 DNS 状态:
bash自动换行:关放大阅读展开代码resolvectl status
如果看到类似下面的输出,即在 Global 配置之外,Link 2 (eth0) (或您的主网卡) 下也出现了 DNS Servers,那么就证明存在配置冲突:
plaintext自动换行:关放大阅读展开代码Global DNS Servers a.a.a.a ... <-- 这是我们期望的全局配置 Link 2 (eth0) Current Scopes: DNS ... Protocols: +DefaultRoute ... DNS Servers: 1.1.1.1 ... <-- 这是不期望的、冲突的配置源 DNS Domain: ...
解决方案的核心是阻止网络管理工具为网卡设置 DNS,让 systemd-resolved 的全局配置成为唯一来源。您需要根据客户端使用的网络管理工具选择对应的方案。
1. 首先,判断客户端使用的网络管理工具
sudo systemctl status NetworkManager。如果服务 active (running),NetworkManager 不存在,运行 sudo systemctl status systemd-networkd。如果服务 active (running),请检查 systemd-networkd 的日志 sudo journalctl -u systemd-networkd,如果日志中提到 netplan,即为使用netplan进行网络管理。2.客户端使用 Netplan (常见于云服务器或 Ubuntu 移植环境)
Netplan 是 systemd-networkd 的一个上层管理工具。我们需要修改 Netplan 的源配置文件。
找到 .yaml 配置文件:
配置文件位于 /etc/netplan/,例如 50-cloud-init.yaml。
检查文件内容:
dhcp4-overrides: {use-dns: false}。cloud-init 的网络管理功能,并创建我们自己的永久性 Netplan 配置。示例:处理由 cloud-init 生成的静态配置
a. 禁用 cloud-init 的网络管理:
bash自动换行:关放大阅读展开代码# 创建一个新文件 sudo nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
粘贴 network: {config: disabled} 并保存。
b. 创建自己的 Netplan 配置文件:
bash自动换行:关放大阅读展开代码# (可选) sudo rm /etc/netplan/50-cloud-init.yaml sudo nano /etc/netplan/01-custom-network.yaml
c. 粘贴修改后的配置:复制原始 .yaml 文件的内容,但彻底删除 nameservers 整个部分。
yaml自动换行:关放大阅读展开代码network: version: 2 ethernets: eth0: addresses: - ipv4地址 - ipv6地址 match: macaddress: mac地址 # nameservers: 部分已被完全删除 routes: - to: default via: ipv4网关 - on-link: true to: default via: ipv6网关 set-name: eth0
d. 应用 Netplan 配置:
bash自动换行:关放大阅读展开代码sudo netplan apply
在执行完适合您系统的方案后,重启 systemd-resolved (sudo systemctl restart systemd-resolved),然后再次运行 resolvectl status。此时,您应该会看到主网卡下的 DNS Servers 部分已经消失,冲突彻底解决。
bash自动换行:关放大阅读展开代码hedeoer@FzZ0HW0YJikn:~$ resolvectl status Global Protocols: +LLMNR +mDNS -DNSOverTLS DNSSEC=no/unsupported resolv.conf mode: stub Current DNS Server: a.a.a.a DNS Servers a.a.a.a 1.1.1.1 2606:4700:4700::1111 Link 2 (eth0) Current Scopes: LLMNR/IPv4 LLMNR/IPv6 Protocols: -DefaultRoute +LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolvectl status 结果解释:
本文作者:hedeoer
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!