飞牛 NAS 和群晖一样,自带系统级内网穿透功能。多年来,老T偶尔使用群晖 QuickConnect 用来访问家中的 群晖 NAS,虽然速度慢,但作为 NAS 界的扛把子,群晖服务的安全性还是让人比较安心。但飞牛这边就有点心结,于是只好自己配置外网访问,同时通过防火墙和一定的规则设置,尽量在易用性和安全性中间寻求平衡,本文主要介绍我这种选择的原因和配置方案。
为何不用飞牛 FN Connect
1. 默认传输速度一般
飞牛的 FN Connect 默认为用户提供免费穿透方案,实测大致是 2-4Mbps 带宽,也就是传输速度在 200-500KB/s 水平,与群晖 QuickConnect 相当。
另外,飞牛也提供了收费版 FN Connect,价格相对实惠,可以提速到 12Mbps 甚至 40Mbps ,其中 40Mbps 几乎达到绝大多数家庭宽带用户上传速率的天花板。
老T觉得 2-4Mbps 速度虽然也能够忍受,但 12Mbps 显然更符合日常使用需求。不过 12Mbps 的价格比自己购买 VPS 稍贵,事实上老T手上有很多可以用来中转的廉价 VPS,像首年38元的阿里云 200Mbps 轻量服务器,日常完全能够把家中上传带宽跑满。
2. 出于隐私安全考虑
我在上一篇 《飞牛 OS 到底是选物理机安装还是虚拟机安装》 提到,毕竟飞牛还只是一家社保登记只有 20 多人的初创公司,从理性的角度来看,我可能没有必要把自己的隐私全交给它保管。
相比而言,群晖那边,好歹是一家行业排名第一的全球化企业,在隐私安全方面有着更为雄厚的技术实力和更长时间的保持记录。
虽然有点厚此薄彼,但这也是无奈的选择。
此外,即便是群晖的联网服务,在更高的数据安全需求面前,可能也不是那么靠谱。例如,老T 在2018 年曾建议我司购买了一台群晖 DS1618+,基于数据安全考虑,这台 NAS 从购买至今都没有开启过外网连接,更谈不上使用 QuickConnect 进行远程访问,所有数据都只保存在内网。
3. 还是个人信息安全
从技术角度来看,以前想要从 NAS 中“偷数据”,难度还是不小。
NAS 本质还是个存储设备,用户将各式各样数据一股脑存在 NAS 中后,别有用心者想要盗取数据时,很难去鉴别哪些数据重要。特别是在家用 NAS 领域,用户存储的绝大多数都是照片、视频类文件。动则几十万文件数,逐一筛选难度过大。
但近年来兴起的 AI 以文搜图、视频增强识别等工具,极大简化了这种操作。能够精准定位到用户的重要隐私数据。比如,老T在飞牛相册中搜索信用卡 CVV,立马就能找到多张信用卡的交易验证码。这些照片,都是老T此前图方便,存储在手机中的,在将手机照片备份到 NAS 过程中并没有意识到这种个人信息安全风险。
{"card_front": {"card_number": "4111 1111 1111 1111","cardholder_name": "ZHANG SAN","expiry_date": "12/25"},"card_back": {"cvv": "123"},"extraction_timestamp": "2023-10-27T14:30:15Z","source_image_hash": "a1b2c3d4e5f6..."}
申明:老T这里绝对没有质疑飞牛、群晖或其他任何 NAS 厂商之意,只是基于普通用户的个人信息安全防护角度,提出合理建议。
总的来说,将 NAS 暴露在公网是一个风险很高的操作,需要用户尽可能在数据安全和使用便利性中寻求平衡。基于此,老T建议是,如果自己不是很喜欢折腾,对数据安全也没那么高要求,那么用 FN Connect 的免费服务或者 99 元的价格购买 12Mbps 高级版就行了,事实上飞牛默认服务也都足够日常使用。如果有更多个人信息安全和隐私方面考虑,也可以参照老T下边的设置思路,为 NAS 筑起更为坚固的防护墙。
选择何种内网穿透方案
在决定不用飞牛 FN Connect 之后,老T便开始思索自己该用那种穿透方案好。
通常有三种选择:
-
有公网固定 IP 的可以选择使用腾讯、阿里、Cloudflare 等大厂 DDNS 穿透方案。 -
使用自己的 VPS 搭建 FRP 之类的通道进行穿透。 -
使用 Cloudflare Tunnel,Ngrok 或者 Tailscale 等第三方穿透方案。
由于老T家没有公网固定 IP,就连 IPv6 都因为小区上层节点原因没能开通,所以,要么在自己的 VPS 上搭建 FRP ,要么使用第三方现成的穿透方案。
最终,老T选择使用 Cloudflare Tunnel,核心原因还是基于网络安全考量,毕竟 VPS 也得进行安全防护设置,比之 Cloudflare Tunnel 繁琐不少。而且 Cloudflare Tunnel 连接速度相较 VPS 并没有太大差距,在老T设置 Cloudflare 优选 IP 加速后,也是能达到 30Mbps 的。
温馨提示
以下操作需要有一定的网络基础知识,且需要两个前提条件:1.有自己的域名;2.有Cloudflare账号。如果没有的话,可以在网上搜索相关教程,这里就不再赘述。
设置方法如下:
1. 在飞牛中安装 Cloudflare Tunnel
Cloudflare Tunnel 提供多种安装方式,可以根据自己实际情况选择 Debian 或 Docker 安装。建议在飞牛内使用 Docker 安装,后续管理比较方便。只需要将 Cloudflare 提供的安装命令转写成 Docker Compose 格式即可。
例如:
version: '3.8'services:cloudflared:image: cloudflare/cloudflared:latestcontainer_name: cloudflared-tunnelcommand: tunnel --no-autoupdate runenvironment:- TUNNEL_TOKEN=XXXxx # 替换为您的真实Tokenrestart: unless-stoppednetwork_mode: host
2. 添加管道
安装 Cloudflare Tunnel 后,继续添加公共主机名,输入内网地址即可连接成功。
需要注意的是,像飞牛 Web 这类服务,既可以添加 http 也可以添加 https,添加完就能正常在公网打开了。而 PVE 管理面板只能添加 https,并且需要在下方应用程序设置中关闭 TLS 认证才能正常打开。
另外,也可以把家中其他服务添加到 Cloudflare Tunnel。比如:路由器管理面板,其他 NAS 设备等。Cloudflare Tunnel的强大之处在于,内网只需要安装一个程序,就能把家中所有需要外网访问的服务都一并拉入加密管道内暴露在公网。不过,这也显然加大了家庭网络暴露风险。
3. 添加 ACCESS 访问控制
Cloudflare 作为全球互联网最大善人之一,其全家桶防护套餐都可以用到 Cloudflare Tunnel 上,显著增强家庭设备暴露到公网后的安全性。
结合日常需要,我觉得最实用的是添加 ACCESS 应用程序控制。
目前我采取的是身份认证接入(也可以选择 PIN 码、一次性邮箱验证码等方法)方法如下:
首先,在 Cloudflare Zero Trust 主页下方设置中,添加身份认证信息。基于国内目前网络环境,可以添加 Azure/Github/Yandex 账号。特别是 Azure,毕竟微软头比较硬,几乎不会出现打不开的情况,其他的偶尔可能会断连。
在添加页面,Cloudflare 有详细的使用说明,按照说明一步一步往下操作即可。
其次,在 Zero Trust 的 Access 中添加策略。选择 Login Methods 为 Azure,Emails 为你自己的 Azure 账号邮箱。
然后,在 Access 中添加自托管应用程序,选择前边在 Cloudflare Tunnel 中设置过的主机名,以及刚创建的 Azure 策略。
这样,当访问飞牛 Web 面板时,首先跳出来的是 Cloudflare Zero Trust 的应用界面,如果不使用你自己的 Azure/Github 等账号登录,别人是不可能访问到飞牛 OS 登录界面的。
4. 为飞牛手机 APP 添加访问策略
日常使用飞牛时,有 WEB 版和手机 APP 两种方式,其中 WEB 版由于不支持常用手机浏览器,使用很是不便。虽然看到有人说 Alook 浏览器能显示得稍微正常点,但实测使用起来还是比较坑。查看飞牛论坛,发现官方也没有适配手机浏览器的想法,无奈之下,老T只能继续研究飞牛手机 APP 登录。
主要原因在于,添加前边这种 ACCESS 访问控制方式后,飞牛 APP 中无法再使用相同的地址登录。毕竟手机 APP 登录时可不会弹出个 WEB 页面出来,只要手机 APP 发出登录命令没有得到相应地址响应,连接就自动终止了。
解决办法倒也不难。只需要在 Cloudflare 中为手机 APP 添加另一条专门访问通道,然后通过 Cloudflare 的访问策略对这条通道进行限制。
以 Cloudflare 安全规则中最简单的 IP 规则为例。如果自己比较固定的使用某些 IP 连接 Cloudflare,可以将不是该 IP 的连接全部阻止。
考虑到老T有时候用固定 IP,有时候不用,在安全规则中设置起来较为不便。老T选择使用 Cloudflare Workers 来控制飞牛 APP 登录连接。
老T设置了两个访问控制逻辑:
-
IP 白名单的访问自动放行。 -
Hosts 头多重认证。包括验证 APP 使用的 IP 段,User-Agent, TLS 版本,运营商,线路等。
需要注意的是,在验证后要放行 Websocket,不然 APP 内的相册打不开。下边是老T的 Cloudflare Workers 示例,通过这种访问控制,估计能屏蔽日常网络上绝大多数网络攻击了。
addEventListener('fetch', event => {event.respondWith(handleRequest(event.request))})const ORIGIN_HOST = 'app.domain.com'const IP_WHITELIST = new Set(["114.114.114.114",])// 允许的User-Agent列表(主应用 + 相册模块)const ALLOWED_USER_AGENTS = ['Dart/3.5 (dart:io)', // 主应用'okhttp/4.12.0' // 相册模块]// 支持的ISP关键词(已扩展)const ALLOWED_ISP_KEYWORDS = ["Mobile", "Cable", "Broadcast","Television", "CMCC", "Cable Television","Broadcasting" // 新增关键词覆盖日志中的"China Broadcasting"]async function handleRequest(request) {const clientIP = request.headers.get('CF-Connecting-IP')const ua = request.headers.get('User-Agent') || ''const cf = request.cf || {}const rawISP = cf.asOrganization || ''// 1. 固定IP白名单直接放行if (IP_WHITELIST.has(clientIP)) {return fetch(request)}// 2. User-Agent验证(支持多UA)const isValidUA = ALLOWED_USER_AGENTS.some(allowedUA => ua.includes(allowedUA))if (!isValidUA) {return blockResponse(403, `UA not allowed: ${ua.substring(0, 20)}...`, clientIP, cf)}// 3. ISP验证(针对IPv4和Cloudflare识别的IPv6)const isValidISP = ALLOWED_ISP_KEYWORDS.some(keyword =>rawISP.includes(keyword))if (!isValidISP) {return blockResponse(403, `ISP not allowed: ${rawISP.substring(0, 30)}`, clientIP, cf)}// 4. WebSocket特殊处理if (isWebSocketRequest(request)) {return handleWebSocket(request)}// 所有验证通过return fetch(request)}// 判断WebSocket请求function isWebSocketRequest(request) {return request.headers.get('Upgrade') === 'websocket'}// 处理WebSocket连接function handleWebSocket(request) {// 创建新的请求头const headers = new Headers(request.headers)// 关键:修复WebSocket协议问题headers.set('CF-WebSocket-Version', '13')// 透明转发WebSocket请求return fetch(new Request(request, {headers: headers}))}// 拦截响应function blockResponse(status, reason, ip, cf) {return new Response('Access Denied', {status,headers: {'Content-Type': 'text/plain','X-Blocked-Reason': reason,'X-Client-IP': ip,'X-Client-ISP': cf.asOrganization || 'Unknown','X-Client-Location': `${cf.city || 'Unknown'}, ${cf.country || 'Unknown'}`}})}
不过这个方案还有个小瑕疵:APP 内无法打开 Docker 应用。如果喜欢在 APP 内打开 Docker 应用的话,建议将每个应用都添加到 Cloudflare Tunnel 管道,并重新在 Docker 中逐个设置一遍外网地址。
但总而言之,上边这种访问策略控制,也只是大幅降低公网暴露风险。如果想继续增强防护,建议设置更为个性化的访问策略,确保只有在满足自己设定的访问条件才能在公网访问到 NAS。

