<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>伪教程 on Ace-Radom's Blog</title><link>https://ace-radom.io/tags/%E4%BC%AA%E6%95%99%E7%A8%8B/</link><description>Recent content in 伪教程 on Ace-Radom's Blog</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Wed, 06 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://ace-radom.io/tags/%E4%BC%AA%E6%95%99%E7%A8%8B/index.xml" rel="self" type="application/rss+xml"/><item><title>基于 OpenVPN 的校园网穿透</title><link>https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/</guid><description>&lt;style&gt;
 .article-page .main-article .article-content h2::before,
 .article-page .main-article .article-content h3::before,
 .article-page .main-article .article-content h4::before {
 content: attr(data-heading-num) " ";
 margin-right: 4px;
 color: inherit;
 }
&lt;/style&gt;

&lt;script&gt;
 document.addEventListener("DOMContentLoaded", () =&gt; {
 const articleContent = document.querySelector('.article-page .main-article .article-content');
 if (!articleContent) return;

 const headings = articleContent.querySelectorAll('h2, h3, h4');
 let counts = { h2: 0, h3: 0, h4: 0 };

 headings.forEach(heading =&gt; {
 let numStr = "";

 if (heading.tagName === 'H2') {
 counts.h2++;
 counts.h3 = 0;
 counts.h4 = 0;
 numStr = `${counts.h2}.`;
 }
 else if (heading.tagName === 'H3') {
 counts.h3++;
 counts.h4 = 0;
 numStr = `${counts.h2}.${counts.h3}`;
 }
 else if (heading.tagName === 'H4') {
 counts.h4++;
 numStr = `${counts.h2}.${counts.h3}.${counts.h4}`;
 }

 heading.setAttribute('data-heading-num', numStr);
 });

 const refLinks = document.querySelectorAll('.dynamic-ref-link');

 
 refLinks.forEach(link =&gt; {
 const targetId = link.getAttribute('data-target');
 const targetEl = document.getElementById(targetId);

 if (targetEl &amp;&amp; targetEl.hasAttribute('data-heading-num')) {
 link.innerText = `第 ${targetEl.getAttribute('data-heading-num').replace(/\.$/, "")} 节`;
 } else {
 link.innerText = "[section not found!]";
 link.style.color = "red";
 }
 });
 });
&lt;/script&gt;
&lt;p&gt;在许多校园网环境中，或多或少都会遇到类似于“游戏端口被封”、“校园网内部 DNS 污染”等问题。&lt;/p&gt;
&lt;p&gt;笔者所在的学校算是一个较为极端的例子：根据扫描结果，无论 TCP 还是 UDP 协议，超过 1024 的端口如非必要几乎都被屏蔽。同时在校园网关进行的 DNS 污染严重到甚至让 GitHub 都无法正常打开。在这种情况下，“翻墙”就是几乎唯一的选择。&lt;/p&gt;
&lt;p&gt;本文将详细记述笔者使用的校园网穿透配置，并且适当探讨部分设置的利弊。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;请注意：笔者的网络环境位于国外，故本文所有内容不考虑 GFW 封锁问题。&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="vpn-套件选择"&gt;VPN 套件选择
&lt;/h2&gt;&lt;p&gt;目前主流的VPN套件和协议有很多种：从穿透 GFW 常用的 V2Ray 和 Shadowsocks，到 WireGuard，以及笔者最后选择的 OpenVPN。&lt;/p&gt;
&lt;p&gt;应该说，不同的选择有不同的好处。笔者自己也有搭建基于 V2Ray+VMess/VLess 的自用 VPN，但这种技术栈在不同种类的终端上使用的客户端并不统一，
并且对“小白”用户来说界面并不友好（因为笔者的校园网穿透同时也提供给别的同学使用，因此也需要考量易用性）；
而 WireGuard 的劣势相信对该协议有所了解的读者都不难想到，即其完全依赖 UDP 传输，在部分网络受限环境下可能无法使用。&lt;/p&gt;
&lt;p&gt;那么笔者最后选择 OpenVPN 的原因也就显而易见了：并不算复杂的配置，完全基于虚拟网卡（不用像 V2Ray 一样额外配置 TUN），
可以自行选择传输层协议和端口，有现成的、成熟的管理脚本和多端统一、由官方维护的简易客户端，几乎完美契合了笔者的需求。即便带来了相对大的服务端 CPU 开销，但这些负载对现代 VPS 来说一般不成问题。&lt;/p&gt;
&lt;p&gt;当然，选择 OpenVPN 的前提是校园网内没有部署深度包检测（DPI），因为其流量特征过于明显，在 DPI 技术面前几乎等于自杀。在这种情况下，
建议换用如 V2Ray 等基于流量伪装设计的技术栈，本文对此不作赘述。&lt;/p&gt;
&lt;h2 id="安装-openvpn"&gt;安装 OpenVPN
&lt;/h2&gt;&lt;p&gt;笔者选用了 GitHub 上的 &lt;a class="link" href="https://github.com/Nyr/openvpn-install" target="_blank" rel="noopener"
 &gt;Nyr/openvpn-install&lt;/a&gt; 项目进行安装和后期的管理。&lt;/p&gt;
&lt;p&gt;在终端中运行以下命令安装：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://git.io/vpn -O openvpn-install.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; bash openvpn-install.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;随后依次选择使用的 IP，协议，端口（此处按照笔者需求选择 TCP 协议和 443 端口）：&lt;/p&gt;
&lt;p&gt;&lt;img alt="选择使用的 IP，协议，端口" class="gallery-image" data-flex-basis="366px" data-flex-grow="152" height="506" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/OpenVPN_Install.png" width="772"&gt;&lt;/p&gt;
&lt;p&gt;再然后就是相对重要的一点：选择 DNS 服务。&lt;/p&gt;
&lt;p&gt;为了彻底避免校园网内 DNS 污染，我们需要在服务器配置中加上 &lt;code&gt;push &amp;quot;block-outside-dns&amp;quot;&lt;/code&gt; 强制客户端使用指定的 DNS（安装脚本已经自动加上了该项配置）。但这也意味着我们必须手动选择一个合适的 DNS 服务。&lt;/p&gt;
&lt;p&gt;笔者一般建议选择谷歌或是 Cloudflare 的 DNS，但也可以结合 Dnsmasq 进行高阶配置（见&lt;a href="#dnsmasq" class="dynamic-ref-link" data-target="dnsmasq" style="color: var(--accent-color); font-weight: bold; text-decoration: none;"&gt;
 [reading...]
&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="选择 DNS 服务" class="gallery-image" data-flex-basis="456px" data-flex-grow="190" height="318" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/OpenVPN_Install_DNS.png" width="605"&gt;&lt;/p&gt;
&lt;p&gt;再随后只需要给第一个 Client 取一个名字，便会自动进行安装。&lt;/p&gt;
&lt;p&gt;完成安装后，再次运行 &lt;code&gt;openvpn-install.sh&lt;/code&gt; 脚本便可进行 Client 管理和卸载 OpenVPN，对此本文不再赘述。&lt;/p&gt;
&lt;h2 id="在客户端中导入-profile"&gt;在客户端中导入 Profile
&lt;/h2&gt;&lt;p&gt;不像别的技术栈，由 OpenVPN 官方维护的客户端 OpenVPN Connect 可以说在使用上傻瓜到了极致。用户只需要将服务器上的 Profile（&lt;code&gt;.ovpn&lt;/code&gt; 文件）下载下来，并且在 OpenVPN Connect 中导入便可连接服务器，
几乎不会有犯错的地方。&lt;/p&gt;
&lt;p&gt;在完成导入后，只要能够正常看到绿色的 Connected 字样，便代表已经连接成功。此时电脑的所有流量都会经由 OpenVPN 代理。&lt;/p&gt;
&lt;p&gt;这也是笔者选择 OpenVPN 的一大原因：极简的客户端大大降低了“小白”的学习成本，让笔者几乎不用在别的用户（同学）的设备上操作。在导入 Profile 后，所有的问题基本上都不用再在客户端更改设置，若真的需要也一般都是直接删除服务器重新导入配置。&lt;/p&gt;
&lt;h2 id="笔者自建的管理界面"&gt;笔者自建的管理界面
&lt;/h2&gt;&lt;p&gt;为了更方便地管理 OpenVPN 服务，笔者自己编写了一套运行在 CLI 下的管理界面。&lt;/p&gt;
&lt;p&gt;但由于代码写得实在太糙了，为了快速上线没有严谨设计导致满是屎山，又在“能跑就别动”的指导方针下放弃了重构计划，所以在此处便不作深入探讨。&lt;/p&gt;
&lt;p&gt;读者若感兴趣可以查看在 GitHub 上的 &lt;a class="link" href="https://github.com/Ace-Radom/openvpn-mgmt" target="_blank" rel="noopener"
 &gt;Ace-Radom/openvpn-mgmt&lt;/a&gt; 和&lt;a class="link" href="https://github.com/Ace-Radom/openvpn-mgmt-web" target="_blank" rel="noopener"
 &gt;Ace-Radom/openvpn-mgmt-web&lt;/a&gt; 两个仓库，
分别是服务器管理界面和 Profile 申请/管理的网站。&lt;del&gt;真的很糙，建议别看代码。&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;本节仅略谈部分在管理界面中用到的 OpenVPN 功能。&lt;/p&gt;
&lt;h3 id="openvpn-management-interface"&gt;OpenVPN Management Interface
&lt;/h3&gt;&lt;p&gt;注意这个管理界面和笔者编写的管理界面不同，是 OpenVPN 的内置功能。为了开启这个功能，需要在服务器配置文件中加上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;management 127.0.0.1 5555 mgmt_pswd.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;5555&lt;/code&gt; 是管理界面监听的端口号，&lt;code&gt;mgmt_pswd.txt&lt;/code&gt; 则是和服务器配置文件放于同一目录下的用于存放管理界面密码的文本文件。&lt;/p&gt;
&lt;p&gt;这个管理界面功能相当强大，笔者自己的管理界面中许多功能也基于该 Interface 实现，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实时状态监控（&lt;code&gt;status&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;强制断线（&lt;code&gt;kill&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然这些远远不是其所有的功能，读者可以自行查阅&lt;a class="link" href="https://openvpn.net/community-docs/management-interface.html" target="_blank" rel="noopener"
 &gt;社区文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id="openvpn-server-side-script"&gt;OpenVPN Server-Side Script
&lt;/h3&gt;&lt;p&gt;虽然在 Management Interface 内也可以做到身份验证的功能，但比起直接挂载服务端脚本来说复杂度过高。为此笔者编写了一套脚本，
用于在服务启动/关闭时轮转日志，并在用户连接时进行身份验证、在断开时记录网络使用量以方便审计。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;script-security 2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;up script/up.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;down script/down.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;client-connect script/client_connect.sh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;client-disconnect script/client_disconnect.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;client-connect&lt;/code&gt; 脚本通过返回值验证授权：若返回值为 0 则认定通过，否则则不通过。这个功能与前文提到的强制断线功能结合使用，便可以做到类似于屏蔽恶意用户的功能，或是在服务器维护中阻止所有用户连接。&lt;/p&gt;
&lt;p&gt;其余脚本在笔者的用例中均只有简单的日志记录功能，在此不做过多讨论。&lt;/p&gt;
&lt;h2 id="dnsmasq"&gt;可选：基于 Dnsmasq 的域名屏蔽
&lt;/h2&gt;&lt;p&gt;有些时候，我们不希望让电脑上的程序去访问某些 URL。通常我们通过更改系统 hosts 文件将域名解析到本地来实现这个功能，
但 hosts 文件在功能上有诸多限制（例如不支持通配符），且作为系统文件修改需要管理员权限，也进一步降低了易用性。&lt;/p&gt;
&lt;p&gt;前文提到，我们可以在安装 OpenVPN 服务时选择使用自建 DNS 服务。在这里我们在同一台服务器上安装 Dnsmasq，因为它极其轻量且易于配置。&lt;/p&gt;
&lt;p&gt;我们先配置 Dnsmasq 并启动 DNS 服务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Dnsmasq Configure
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;listen-address=127.0.0.1,10.8.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bind-interfaces
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;no-resolv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 DNS 服务启动后，我们便可以在 OpenVPN 安装中选择使用自建 DNS 服务器，并将 DNS 服务器的 IP 设为 &lt;code&gt;10.8.0.1&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="使用自建 DNS 服务器" class="gallery-image" data-flex-basis="694px" data-flex-grow="289" height="390" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/OpenVPN_Install_DNS_Custom.png" srcset="https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/OpenVPN_Install_DNS_Custom_hu_f3496b024de419bd.png 800w, https://ace-radom.io/p/%E5%9F%BA%E4%BA%8E-openvpn-%E7%9A%84%E6%A0%A1%E5%9B%AD%E7%BD%91%E7%A9%BF%E9%80%8F/OpenVPN_Install_DNS_Custom.png 1129w" width="1129"&gt;&lt;/p&gt;
&lt;p&gt;随后在 Dnsmasq 配置中定义上游 DNS：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server=1.1.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server=1.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server=8.8.8.8
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;server=8.8.4.4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;完成这些步骤并重启 Dnsmasq 服务后，若在客户端上可以正常解析域名，则代表配置正确。&lt;/p&gt;
&lt;p&gt;我们可以按照以下方式添加配置以屏蔽部分域名：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;address=/block.site/127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;需要注意的是：即便我们在这里没有使用通配符，所有挂载在 &lt;code&gt;block.site&lt;/code&gt; 下的次级域名一样会被解析到本地。这是由 Dnsmasq 的设计决定的：由于其仅支持泛解析而没有实现精确匹配，想要简单地做到类似于“仅屏蔽某个二级域名，却不屏蔽其下的三级域名”的功能是不可能的。&lt;/p&gt;
&lt;p&gt;在某些场合这可能是一种限制，但更多的时候这就是我们想要的功能。而在什么情况下会需要这种大范围屏蔽某一域名及其下所有次级域名呢？
此处不便细说，请读者自行联想。&lt;/p&gt;
&lt;h2 id="可选通过多服务器架构绕过网站屏蔽"&gt;可选：通过多服务器架构绕过网站屏蔽
&lt;/h2&gt;&lt;p&gt;如今许多网站会依赖由如 Cloudflare 等公司提供的安全保护服务，其中一种惯用策略便是按 IP 段或地理位置等信息屏蔽某些访问请求。不巧的是，由于我们的 OpenVPN 服务器大多部署在 VPS 上，且这些一般配置的 VPS 也常被网络爬虫/扫描服务租用。因此，我们的 VPS 所在的 IP 段被网站错误屏蔽并非很罕见的情况。而由于我们的所有流量都会被 OpenVPN 代理，
所以一旦在我们的设备上连接了 OpenVPN 服务器，我们就无法打开这些本来可以打开的网页了。&lt;/p&gt;
&lt;p&gt;解决这个问题的简单方法也十分纯粹：将这些被屏蔽的流量分离出来，由另外的一台没有被网站屏蔽的服务器转发。由于我们已经安装了 Dnsmasq 并与 OpenVPN 配套使用，所以实现这个功能并不困难。&lt;/p&gt;
&lt;p&gt;假定目前有两台服务器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;代理服务器 A：安装了 OpenVPN 和 Dnsmasq，但被网站错误屏蔽。&lt;/li&gt;
&lt;li&gt;转发服务器 B：没有被网站屏蔽。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们首先在服务器 A 上创建一个 IP Set，用于存储所有需要被转发到服务器 B 上的 IP。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ipset --create proxy_domains hash:ip --timeout &lt;span class="m"&gt;3600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其中强烈建议加上 &lt;code&gt;--timeout&lt;/code&gt; 开关以自动淘汰长时间未被访问的 IP。原因见下文。&lt;/p&gt;
&lt;p&gt;之后我们需要利用 Iptables 设定转发逻辑（可按实际情况调整转发端口，此处使用 8443）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -t nat -A PREROUTING -s 10.8.0.0/24 -p tcp --dport &lt;span class="m"&gt;443&lt;/span&gt; -m &lt;span class="nb"&gt;set&lt;/span&gt; --match-set proxy_domains dst -j DNAT --to-destination &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVER_B_IP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:8443
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -t nat -A POSTROUTING -d &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SERVER_B_IP&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; -p tcp --dport &lt;span class="m"&gt;8443&lt;/span&gt; -j MASQUERADE
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然而实验发现，Cloudflare 获取到的仍然是服务器 A 的真实 IP，因而再次触发屏蔽机制。其中主要原因是 Cloudflare 的保护服务已经默认启用了 HTTP/3 协议，而其使用基于 UDP 传输的 QUIC 协议实现。由于我们只拦截了 TCP 流量，
所以服务器 A 的真实 IP 依然能被 Cloudflare 获取到。&lt;/p&gt;
&lt;p&gt;虽然我们也能转发 UDP 流量，但这对服务器 B 上运行的转发服务有要求且配置上更加复杂，最简单的解法是迫使连接退回到 TCP 协议：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;iptables -I FORWARD -s 10.8.0.0/24 -p udp --dport &lt;span class="m"&gt;443&lt;/span&gt; -m &lt;span class="nb"&gt;set&lt;/span&gt; --match-set proxy_domains dst -j REJECT
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;完成以上配置后切记执行持久化保存（Debian 系一般使用 ipset-persistent 和 iptables-persistent，并调用 &lt;code&gt;netfilter-persistent save&lt;/code&gt; 保存所有配置）。&lt;/p&gt;
&lt;p&gt;在 Dnsmasq 的配置中我们只需要加上：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ipset=/cf_blocked.site/proxy_domains
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Dnsmasq 会自动将解析出的 IP 加入对应的 IP Set 中，随后所有连接到对应 IP 的流量都会被内核转发到服务器 B。&lt;/p&gt;
&lt;p&gt;而在服务器 B 上，由于其只作流量转发用途，理论上可以使用任何 TCP 流量中转工具（如 sniproxy 和 GOST）。在笔者的环境中，由于服务器 B 上已经运行了 Nginx 并配置了 stream 模块，因此采用了这种看上去有些臃肿的组合。&lt;/p&gt;
&lt;p&gt;首先，我们需要先确定服务器 B 上的 IPv4 转发功能是否启用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检查 IPv4 转发功能是否启用&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sysctl net.ipv4.ip_forward
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 若没有启用 则运行以下命令开启&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;net.ipv4.ip_forward = 1&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; /etc/sysctl.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sysctl -p
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后便是按照个人选择配置转发服务。这里附上笔者的 Nginx stream 模块配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;resolver&lt;/span&gt; &lt;span class="n"&gt;1.1.1.1&lt;/span&gt; &lt;span class="n"&gt;8.8.8.8&lt;/span&gt; &lt;span class="s"&gt;valid=300s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;ssl_preread&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="nv"&gt;$ssl_preread_server_name:443&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;allow&lt;/span&gt; &lt;span class="nv"&gt;${SERVER_A_IP}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 Nginx 配置中切记对转发来源 IP 加以限制。为了方便审计，我们一般还需要加上 Access Log。由于这不是本文的重点，故不做阐述。&lt;/p&gt;
&lt;p&gt;这种转发仅有一个问题：由于我们的底层转发逻辑是按照 IP 匹配的，而那些真的会屏蔽服务器 A 的网站大概率都会使用 Cloudflare 等公司的代理服务。也就是说，我们解析到并加入转发 IP Set 的 IP 实际上大概率是 CDN 的 IP，而非网站真实服务器的 IP。&lt;/p&gt;
&lt;p&gt;这会造成一个限制：由于所有依赖于这种代理服务的网站都可能被解析到这些 CDN 上，可能会出现在访问完一个被屏蔽的网站后，
别的正常网站由于被解析到了相同 CDN IP 因此也被转发到了服务器 B 上。&lt;/p&gt;
&lt;p&gt;这类误转发在当前架构下，在我的认知范围内是无解的。因为一旦按照域名转发就会极大增加复杂度和性能开销，而这是笔者不期望的。因此笔者建议在创建 IP Set 时引入 Timeout 机制，在规定时间后自动淘汰被解析的 CDN IP，以避免越来越多的网站被错误转发。&lt;/p&gt;
&lt;p&gt;而 Timeout 的具体值则按服务器 A 与服务器 B 之间的物理距离决定：
笔者一般将与代理服务器（服务器 A）处在同一地区的转发服务器（服务器 B）的 Timeout 设为 12 小时（即 43200 秒）。若两者相距很远（如代理服务器在欧洲而转发服务器在香港），则建议设定为一小时甚至更短，以避免误解析造成的延迟激增。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2026-05-13 Update&lt;/strong&gt;：在五月十二日，笔者配置的转发突然部分失效。在浏览器上记录到了一个很诡异的现象：当网页冷加载时，
浏览器可以正确获取到网页的根 HTML，但在随后加载资源的过程中连接被 Cloudflare 识别到代理服务器真实 IP 并被屏蔽，导致网页上大量内容缺失。在点击刷新后则彻底无法访问网站内容。&lt;/p&gt;
&lt;p&gt;根据排查得知这是 Cloudflare Insights 服务造成的。在冷加载中，有些网站会注入这项性能监测服务脚本，而该脚本是直接从 Cloudflare 的域名下拉取的。由于我们没有对该域名做任何限制，相关流量自然也没有被路由到转发服务器上。笔者怀疑 Cloudflare 在后台更新了他们的安全策略，
会将请求该脚本的 IP 和请求网页内容的 IP 进行比较，并优先采信被网站安全规则屏蔽的 IP。由于时间原因笔者没有办法进一步详细调查，
也无法确定受影响的网站在之前是否有被注入 Cloudflare Insights 相关脚本，但鉴于在阻断了该脚本的拉取后相关问题便得以解决，
并且那些没有被注入的网站也没有受到影响，基本可以确定该脚本和本次事件有关。&lt;/p&gt;
&lt;p&gt;为规避相关风险，笔者在此不详细说明解决方法。鉴于所有需要的技术细节在本文中都有讨论且本文读者应该都具备对相关技术的一定了解，
笔者相信根据本文内容找出合适的解决方案对读者来说绝非难事。&lt;/p&gt;</description></item></channel></rss>