因为在家里部署了不少应用服务,所以,也想将一些服务暴露到公网上,但是呢,直接暴露的话,会有非常大的风险。全部走vpn也略微有点麻烦,虽然最近看到一些油管的UP主在推荐cloudflare的tunnel服务,但是我都自己托管了,还走cloudflare是不是有点……愚蠢呢。
因为工作的原因有接触过pfSense,然后也尝试过其分支OPNsense,一番测试之后,发现大有可为,不得不说真好使。
安装OPNsense
就是很普通的用iso引导,然后根据屏幕上的说明一路下一步就行。安装完毕之后,顺手更新一下系统。调整一下时区、默认语言等。
安装必要的插件
最主要的插件只有2个,一个是os-acme-client,另外一个是os-nginx。acme负责签署证书,而nginx,则负责做反向代理。
自动注册证书
从侧边栏访问“服务-ACME客户端”,默认的设置页面会报错,这时候,需要切换成英文,就能够正常打开页面了。跑去settings页面,打开启用插件和自动更新,应用。剩下的就可以切换回中文了。

下一步,就是创建一个账号,只要填一下邮箱地址,其他默认就行。

下一步是添加一个“挑战类型”,这里其实翻译成“验证类型”会比较好……

下一步是创建一个自动操作,这里选择重启Nginx,这样证书在续签之后会自动重启nginx,并应用新的证书。

然后就是创建一个证书(申请)

注意这里的域名应该这样设计:一个通用域名(example.com),还有其下的通配符域名(*.example.com)。到时候,example.com应该返回一个404;具体对应的系统,则应该放在子域名来访问,例如nextcloud.example.com。另外,所有的应用都应该使用同一个监听端口,除非是由于某些原因,只能使用”流“(stream)代理。如此,可以最大限度地隐藏服务入口。
最后,自动操作里面选择一下刚创建的重启nginx的操作。保存。
保存之后出来可以手动触发一下证书签署,顺利的话就直接拿到一个可以用的证书了。
配置NGINX
OPNsense使得nginx的配置简单的很多,只要点点点就行,而且附加了很多安全方面的设置,有很多值得学习的内容。
创建错误提示页面
为了误导可能的攻击者,我们需要整点花活,因为默认情况下,被策略拦截之后,会向客户端返回一个页面,提醒被OPNsense上的策略拦截了。这可不行,怎么能让对方知道我们用了什么东西的呢对吧。我们来创建一个错误页面,整一个Nginx默认的500错误页面。并且匹配所有的状态码。

这样,无论是什么错误,用户都只能看到一个500的错误。(邪恶)
创建一个默认的服务器
设计的思路在证书那里已经说过了,首先,创建一个默认的服务器,在访问域名不正确的情况下,所有的访问都会跑到这里来:
位置
首先创建一个位置(HTTP(s)-位置),打开高级模式,修改以下的内容,确认保存:
- 描述:随便填写
- 网址格式:/
- 强制HTTPS:勾选
- honeypot:勾选
这里解释一下这个honeypot,在打开这个参数之后,如果访问了这个位置,就只会返回一个403,并且源地址将会被记录,然后屏蔽。具体屏蔽的时间,可以在“常规设置”的页面,打开高级选项,就可以自定义屏蔽的时间了。



HTTP服务器
然后创建一个服务器(HTTP(s)-HTTP服务器),修改这些内容:
- HTTP监听地址:空
- HTTPS监听地址:0.0.0.0:8443(你应该换个别的端口号)
- TLS证书:(刚签署的那个)
- 仅HTTPS:勾选
- 错误页面:刚创建的糊弄人的默认错误页面


配置好DNS之后就可以测试一下是否生效了。尝试访问一下这个服务器,会得到一个空白页。然后在禁止的页面上就能看到这个ip进入了拦截清单了。

调整防火墙策略
最后一步就是调整防火墙策略,新增一个策略,拦截掉这些坏东西的访问:
- 操作:阻止
- 源:nginx_autoblock

这样,那些来源不明的访问就会被拦截。

应用保存之后,一个基本的拦截就做好了,攻击者在不知道你有什么域名的情况下,如果访问了OPNsense,就会被关小黑屋,被防火墙拦截,而被防火墙拦截的情况下,其他的端口也会无法访问。
另外,由于我们签署的证书包含2个域名,一个是example.com,另外一个是*.example.com。所以,即便是通过这次的访问,拿到了SSL证书,也不会知道我们用的是什么域名。这就能最大限度地隐藏访问入口。
添加需要暴露的服务器
基础搭建好了之后,就是增加需要通过nginx反向代理的服务了。一共有4个步骤,简单的说法:
- 添加一个“上游服务器”
- 添加一个“上游”
- 添加一个“位置”
- 添加一个“HTTP服务器”
上游服务器


需要填写的一共4项,描述、服务器、端口还有优先级。这里特别说明以下,因为设计上,是有多个后端的,所以必须要填上一个优先级。因为一般只有1个后端,所以……随便填一个数字就行。
上游


这里的话,只要填写描述,然后,服务器条目选择刚创建的上游服务器即可。值得注意的是,如果被反代的服务器是使用https的,则需要勾选上“启用TLS(HTTPS)”
位置
位置的话,刚才在创建一个默认的服务器的时候已经讲过了,这次稍有不同的地方,就是有一个额外的选项需要选择,就是“上游服务器”,这里选择刚才创建的“上游”即可。

HTTP服务器
可以参考上面的创建http服务器,和之前的主要差别,只有1个,那就是需要指定一个你自己才知道的属于*.example.com的子域名。由于证书是泛域名证书,所以所有子域名都能够使用。

应用配置

一切搞定之后,点击应用,保存并生效。
最后,在调整完dns之后,你就能用https://private.example.com:8443/来访问你需要暴露的服务了。有多个应用的情况下,重复创建上游服务器、上游、位置、http服务器,即可。