注:本文原于2020年2月发布于知乎,后因违规被删除。现重发于个人blog。
我如今使用的方案与当时文中已略有不同,但基本原理类似,故不作修改,仅供参考。
背景
我是个喜欢收集资源的人,也时常跟朋友们分享。以前的115、360还有许多小众网盘都用过,最后它们都死了,剩下的百度云流氓成性,限速、删文件、改视频、客户端偷偷上传。现在我除了偶尔下点别人分享的资源外根本不会用它,就连这点下载用的也是容器化客户端,不让它碰本地系统。
所以应该怎么办呢?国内网盘都不靠谱,国外的要么容量限制太多要么不能直连。我自己口袋里随身装着云梯,总不能指望人人都这样。
最后捣鼓了半天,搞定了一个能通过互联网访问的、有密码保护功能的、没有任何备案登记要求甚至无需注册额外服务、完全可控的私人网盘。把整个思路和配置过程在此记录一下。
这是一篇个人笔记,不是教程,不包含具体问题的解决方法,如果想照做还需要自己研究。
服务器
我家里有一台迷你主机(见此文),还有一台四盘位的QNAP NAS做内部文件分享,CPU是J3455,内存4G,跑稍大的程序就要挂掉。我曾经想试过在它上面搞虚拟化,结果一小时里死机数次,差点全盘重装。而那台迷你主机用VMWare运行一个OSX和三个Linux还游刃有余。
但考虑到这次的需求仅仅是文件分享,而且不会大规模发布,权衡之下还是决定用NAS。
QNAP和群晖的系统经过多年进化,基本变成了同类产品,以前那些谁更好玩谁更稳定的对比早已没了意义。这次用到的无非就是普通文件管理和容器服务,无论哪家都有,功能也一样。
NAS应用中心里有个ContainerStation。起初我以为是它家自己开发的,还担心过兼容性。一用才知道根本就是给docker套了层WebUI,从命令行启动的docker容器在这里也能看到。
用docker有个好处:NAS系统是自制的Linux,你直接在主机运行的脚本、cron任务一重启就会失效;如果魔改到不失效的程度又怕把系统搞坏,就算不怕搞坏,一升级内核照样完。而程序跑在容器里则不会损伤外部系统、易监控维护、随系统启动、崩溃重启都完全没有问题。
至于用了哪些镜像,后面会写到。
网络环境
既然是私人服务,当然就用自家的网络。现在大家都是光纤入户了,从100M到500M,还有少数已经上了千兆。
不过作为服务器时重要的不是下载,而是上传速度。ADSL时代的上行小水管还情有可原,到了光纤时代又来这套未免吃相难看。企业宽带和家庭宽带本质没什么区别,贵出十倍价格换来的无非也就是固定IP、上下行对等、开放常用端口(有时候还未必能开)。
好在我们搞的是个人服务,这些都不算刚需。
唯一的刚需是公网IP,要连这个都没有就只好放弃了。
我自己的300M光纤有40M上传,联通确实很孙子,但起码有公网IP,带宽勉强够用。
至于端口,80、443一定是被封的。你想要个长得像正常的网站(比如http://xxx.com)肯定没戏,而只要网址后面跟了端口号,无论用哪个端口都显得不正常(比如http://xxx.com:8888),也就无所谓了。
唯一有影响的地方就是作为CDN回源时,某些CDN对端口有限制。比如阿里云只支持80和443,基本废了;腾讯云和七牛则没这些限制;cloudflare比较独特,它支持十几个端口号(见博文),包括电信服务商没封的。但端口内外对应,你访问80才能回源80,访问2096就只能回源2096,想随意定制也可以,得加钱。
这次的私人网盘不用CDN,也没了上面这些问题。只需要自己挑一个端口就行,1024~65535均可。
域名
就算端口不标准,但总得有个域名,不然IP随时会变,别人怎么找你。
域名这东西去哪家买都可以,几百个后缀,挑便宜的喜欢的随便买,要是短时间用可以选个第一年便宜的,要是真想长期用,就得看看续费价格了。
如果还嫌贵也有办法。有几个顶级域名后缀可以免费申请,除了很早就有的.tk之外,近些年又加了.ml、.ga、.cf、.gq,这些免费域名归http://www.freenom.com 管。用起来跟普通域名没有任何区别。
需要注意的是大部分特殊域名在国内没法备案,因为不支持,备案机构的官僚们落后时代二十年。反正我们也没打算备案,直接用就行。未备案域名审查是云服务商的事,指向你自己的IP没人管。
我图省事在自己的sulian.link下面建了个子域名,假设就叫demo.sulian.link吧(实际不存在),后面也都用它举例。
不管在哪家注册域名,一般都不会用注册商自己的NameServer,而是比较可靠的第三方。国外的Cloudflare、Azure、Amazon,国内的阿里云或腾讯dnspod都可以。基础服务免费,只做解析不用服务器的话也不必备案。
我用的是Cloudflare,因为平时用它家的CDN服务,功能比较熟。
直接把NS记录指过去,多则一两天,少则几小时就能生效。
DDNS
前面也说了,既然用的是个人家庭网络,没有固定IP地址,就不能像普通网站那样一条A记录搞定,DDNS自然是必需品。
只图省事的话其实这步有点多此一举,因为QNAP自带云服务(群晖也有),可以分配你一个二级域名,连SSL证书和DDNS服务全包括。
哪怕打算用自己的域名,加一条CNAME就搞定了。
如果完全不想用它的云服务,那NAS自己,以及很多新路由器都内置了DDNS支持,国内的有花生壳,国外的dynu、noip等,大大小小十几家。
但这些方案我都没选。一是爱好折腾的乐趣;二是想让这个方案适应性更广些,不用NAS放到别的主机也能用;三是前面说的原则,尽可能不依靠第三方服务,特别是国内第三方(没有理由,就是不喜欢)。域名非用不可没办法,除此之外能自己搞定尽量自己来。
DDNS是个原理很简单的东西。包括我用的Cloudflare在内,所有DNS服务商都支持API修改记录。用大服务商的好处就是网上会有很多通用教程和脚本,甚至还有容器化:cloudflare-ddns,直接pull下来照里面的配置启动就是了。
有的中文教程习惯不好,动不动就拿Global Key走天下,这么高权限的东西万一泄露后果不堪设想。英文教程就很少这么干,基本都会老老实实创建API Token。
这段DDNS脚本的唯一作用就是5秒钟检查一次本机公网IP,如果变了就调用Cloudflare的API修改域名指向本机。十分简洁,只需懂点网络和系统知识,都不用写代码,就比那些又要注册又要安客户端还只能几十分钟检查一次的互联网服务强多了。
网盘
有了上面的准备工作,分享文件就变得超级简单,哪怕是直接用nginx开启本地目录列表,让别人直接访问都行。
但这样一是看起来太粗糙,二是开放所有文件,安全性没保障,也没有分享链接、密码这种便于监控管理的工具。QNAP的应用中心里自带了一个owncloud,版本很旧,而且怎么也安装不成功。后来查了查,它已经停止升级,迁移到一个叫nextcloud的项目。
跑去官网看了看,用户很多,文档详尽,社区也活跃,虽然功能有点过于丰富,我只做简单文件分享可能用不到,但最后权衡了一下还是决定用它。
当然,docker版是必须的。
nextcloud支持多种数据库如MySQL,但自身免安装的SQLite其实已经够用,不必再增加配置的复杂性。
容器配置只有两件事,一是端口,把内部的80端口映射到一个宿主机端口,我选的9900(后面会提到);二是数据卷,需要给它单独建一个目录用来安装配置文件,除此之外还要把需要分享的文件加载到容器里。这样的话,同一个文件夹我可以在PC共享访问、可以通过NAS网页管理,也可以在nextcloud里直接查看并分享出去。考虑到安全性,可以加载为只读卷。
启动容器后进入http://NAS IP:9900这个网址配置数据库、添加管理员,稍等一段初始化之后网盘即可用。因为配置文件和数据库都在单独目录里,重启也不会丢失。
nextcloud很多基本功能都是靠应用插件来实现的,哪怕文件动态、回收站、分享链接、外部存储这些功能都是。
默认有一批应用已经激活,如果没有的话,把External storage support和File sharing激活就可以。然后添加外部存储目录,就能直接读取并分享了。
不用不知道,nextcloud分享功能超级强大,可以添加密码、可以设置过期日期,可以允许访客上传,甚至同一个文件夹可以创建不同的共享链接给不同的人以方便管理。
这还仅仅是一个访客共享,如果配合多用户权限控制,那简直就为所欲为了。
在顶部的“动态”选项卡可以直接看到文件的分享和下载情况。
还有更多功能没来得及一一试用,之后再研究吧。
顺便说说我踩过的一个诡异大坑,网上几乎搜不到同类案例。就是在分享文件时明明一切配置正常,但偏偏那个选项卡莫名失踪。研究一个多小时后发现,是选项卡的id被Adblock当成广告给杀掉了……
反向代理、NAT映射
其实上一步配置完nextcloud就已经基本能实现分享文件的目的。但现在是2020年,纯HTTP几乎等于在脸上写了“我在裸奔”,没有HTTPS就是互联网的二等公民,连浏览器都歧视你。
一般情况下,我们会把HTTPS配置放在服务最外层,也就是直接面对访客的一层,而内部传输还是用HTTP。这样能保证大家各司其职,业务专心管业务,不要管传输和加密的事。
而最常见和简单的方式就是用nginx做反向代理,让它去负责HTTPS连接。
nginx自然也是docker版,映射端口和配置数据卷即可。只做反向代理的话,连本地root目录都省了,只需要加载一个证书目录和一个配置文件目录,再图省事甚至还能把两者合一。
SSL证书申请的事放到下一步讲,假设证书已经申请好了,放到指定位置,写好配置文件加载。
需要注意的是那个proxy_pass,IP地址10.0.5.1从哪来的?其实是从docker容器里访问宿主机用的地址。我们用NAT模式启动docker时有一套虚拟内网,宿主机就是网关,每个容器的IP不固定,所以没法直接访问,只能通过宿主机上映射的端口互访。
在宿主机输入ifconfig,看docker0这个网卡的地址,就是从容器内部互访用的IP了。
(好像新版docker提供一个主机名供内部访问,但我不太熟悉,先用老方法)
nextcloud的安全限制相当严格,你在外面套了层加密反向代理后没法直接访问,有错误提示。需要在config.php里添加这么两行:
用自己的域名覆盖内部URL,重启容器,浏览器地址栏里红色的“不安全”就变成了小锁头。
最后,为了在互联网而非家庭局域网进行访问,我们还要在家里的出口路由配置一个NAT映射,把路由器的8888端口映射到NAS的8888端口。否则这个域名指向路由的公网IP,却访问不到NAS主机。
一切完成之后,浏览器输入https://demo.sulian.link:8888,就能在任何地方加密访问24小时不中断的私人文件分享服务了。
SSL
前面说了有证书后怎么配置,但搞到证书还要做点工作。
SSL证书的90天免费自动签发经过了几年推广后已经深入人心,连很多服务商都被逼得提供了免费版,再花钱买证书纯属人傻钱多。
如果图省事,可以直接去找免费服务手工签发,大部分三个月,也有一年的(国内居多,腾讯阿里都有)。
如果想搞个全自动更新的方案或者单纯不想注册这些服务,那么有请acme.sh大神。
acme.sh的自动更新依赖cron,但前面也说了,NAS不让你随便添加任务,重启就消失,我们还得继续用docker版。官方有简易教程:acme.sh deploy to docker containers,简单来说就是先启动一个容器作为daemon,然后用docker exec执行命令,跟在本机直接运行脚本格式一样,甚至环境变量也能在命令行里配置。
跟原生版的最大不同就是,原先是本机运行,证书部署到本机,重启本机nginx服务;现在需要在容器里运行、证书部署到nginx容器、再重启nginx容器。只要容器别关掉,就能保持两个月一次的自动更新。
申请证书需要验证网址,本地没有80端口,服务器访问不了,只能用域名来验证。acme.sh最好的一点就是内置了几乎所有DNS服务商的API接口和所有服务器的部署方案。
前面配置DDNS时已经有了Cloudflare API Token,继续用它来申请证书即可。
官方教程里还有用docker compose一次性解决的方案。如果配置得当,你就有了一劳永逸的安全访问方案。
总结
经历了这么一大堆折腾之后,终于有了一个完全属于自己的、可以在互联网任意位置访问、随意分享给任何人而无需审核备案也无需国内各种第三方服务的网盘。
前面的记录看似一团乱麻,实际画个结构图就能把思路整理好:
还有改进空间吗?有的是。愿意折腾的话,每一个组件都有很多地方能调整,整体运行和监控也可以继续完善。只不过对小规模存储和分享而言,这个版本已经足够可用。
以这些技术为基础稍作改进,还能做更多的事情。比如https://sulian.link这个网站本体在我家里的电脑上,但你访问到的是它的CDN版,真实IP是完全被隐藏起来的。至于怎么从家庭网络映射到公网的443端口,那就是另一种思路了。
很牛!正好这段时间我也打算搞个nas玩玩,这篇文章正好给了学习思路了~~~
图挂了
没有挂