写在前面

前段时间家里的 NAS 换新,换了一台功能强大的群晖 NAS 存储,这玩意鼓捣起来功能还挺多,很多功能发现还很好用,例如,通过 Synology Drive 可以构建自己的专属网盘,可以实时将电脑里的文件同步到 NAS 里面,其他终端设备安装了 Drive 应用之后也可以随时访问这些文件。

image-1669209678734

还有 Synology Photo 可以将照片集中管理起来,随时随地共享和访问这些照片。

image-1669209719802

Synology Note Station 应用类似一个完全私有的云笔记软件,可以将笔记信息实时同步到自己的所有终端设备上。

image-1669209747649

除此之外,还有日历、邮件管理、通讯录等功能,甚至还可以构建自己私密的即时通信系统。总之,群晖 NAS 可以将所有的个人信息和资料安全地存在家庭本地,相当于将一个 icloud 构建在了自己家里。
当然,这么一个功能强大的平台仅仅只有在家里内网环境下才可以使用的话就太浪费了,所以研究了一下如何将群晖平台发布到互联网上,即使不在家里也可以随时随地使用群晖平台。目前通用将群晖平台暴露到互联网上的通用方式主要有以下三种:

  • QuickConnect: QuickConnect 是群晖自身提供一种云服务技术,依托 Synology 可靠的全球基础设施和 hole punching 技术,让用户能够随时、随地从设备和浏览器访问 Synology NAS,无需设置端口转发和防火墙。
  • DDNS: 动态域名服务(Dynamic Domain Name Server),DDNS 是将用户的动态 IP 地址映射到一个固定的域名解析服务上,用户每次连接网络的时候客户端程序就会通过信息传递把该主机的动态 IP 地址传送给位于服务商主机上的服务器程序,服务器程序负责提供 DNS 服务并实现动态域名解析。动态域名服务的对象是指公网 IP 是动态的,经常随机变动,普通 DNS 要求基于静态公网 IP,无论哪种都要求具有公网 IP 地址。
  • 内网穿透: 也称为 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。将服务器部署在具有公网 IP 的机器上,如各大云厂商提供的云主机,客户端部署在内网或防火墙内的机器上,通过访问暴露在服务器上的端口,反向代理到处于内网的服务,实现所谓的“穿透”直连。这种方式一般在家里没有公网 IP 的时候,通过云端主机的公网 IP 实现将内网设备暴露到外部的目的。

这三种方案里,QuickConnect 限制太多,不支持与第三方应用程序的连接,不支持某些需要直接映射到 IP 地址或 DDNS 的服务和套件,并且这种连接要比通过端口转发的连接慢很多,可能是 Synology 服务器的原因,网络延迟较长;动态域名服务要求家里的路由器上有公网 IP,有的小区的运营商默认情况下不提供公网 IP,需要单独找运营商申请;内网穿透方案不需要家里的出口路由设备上有公网IP,只需要可以联网,能访问到拥有公网 IP 的服务器或提供内网穿透服务的三方平台即可。无论哪种方案,在互联网环境访问 NAS,出站带宽都不会超过家里宽带的上行带宽限制。因为种种原因,刚好自己在某公有云有一台长期使用的云主机,所以这里我选择了第三种内网穿透的方案。
内网穿透的平台和工具有很多,选择平台的话,建议选择收费服务的大厂,例如,选择使用花生壳,花生壳在群晖的套件中心也提供了相应的套件。如果有云主机的话可以复用自己的云主机来作为内网穿透的服务器,群晖里常用的开源内网穿透工具主要有 frp,本文也主要介绍如何基于 frp 实现群晖平台的内网穿透。

架构介绍

流程图-2

为了方便可以在内外网都可以灵活方便的访问到家庭 NAS 里的资源,并且要做到内网到外网的无缝切换,不需要更换 NAS 访问地址,所以在访问上,全局都要使用统一的域名地址:nas.domain.com,整个架构主体采用 frp 作为内网穿透的代理工具,frp 服务端部署在公有云的云主机之上,frp 客户端部署在群晖 NAS 的 Docker 容器上,客户端通过访问云主机的公网地址实现和服务端建立稳定的代理连接。
在家庭内网配置路由器上的本地 DNS 解析(需要路由器设备支持),将域名 nas.domain.com 解析为群晖设备的 IP 地址,这样客户端在连接到家庭 wifi 网络时,便可直接将域名解析为内网地址访问群晖设备。frp 客户端采用容器方式部署在群晖设备上,选择使用与 Docker Host 相同的网络,所以在使用 frp 进行内网代理的时候配置 127.0.0.1 地址即可,本次主要代理的端口为 5001/6690/1194/1195,各端口主要作用如下:
image-1669213375049
在互联网 DNS 平台上将域名 nas.domain.com 解析为公有云云主机的公网 IP 地址,这样在互联网环境访问的时候可以自动解析到 frp 服务器。
在互联网环境下通过客户端访问群晖上的 VPN Server 可以实现安全地访问家庭内群晖所在的局域网环境里的其他设备,例如,连接 VPN 后可以在家庭之外的网络直接访问台式机。

frp 介绍

frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:

  • 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。
  • 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。
  • 代理组间的负载均衡。
  • 端口复用,多个服务通过同一个服务端端口暴露。
  • 多个原生支持的客户端插件(静态文件查看,HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。
  • 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。
  • 服务端和客户端 UI 页面。

frp 主要由客户端(frpc)和服务端(frps)组成,服务端通常部署在具有公网 IP 的机器上,客户端通常部署在需要穿透的内网服务所在的机器上。
内网服务由于没有公网 IP,不能被非局域网内的其他用户访问。本文的内网服务主要是群晖平台,所以 frpc 将部署在群晖设备之上。
用户通过访问服务端的 frps,由 frp 负责根据请求的端口或其他信息将请求路由到对应的内网机器,从而实现通讯。本文服务端部署在某公有云平台的云主机之上。

frps 安装及配置

frps 安装

frp 采用 Golang 编写,支持在多种操作系统平台上部署,本次 frps 选择部署在了某云的 ubuntu20.04 系统的云主机上。
通过 ssh 远程登陆云主机,直接使用 wget 命令去 GitHub 下载 frp 的二进制压缩包:

wget https://github.com/fatedier/frp/releases

将下载下来的二进制压缩包直接解压到 /usr/local 目录:

tar -xvf frp_0.45.0_linux_amd64.tar.gz -C /usr/local/

为了方便后台长期运行和开机自动运行,须配置 systemd,使用文本编辑器,如 vim 创建并编辑 frps.service 文件。

vim /etc/systemd/system/frps.service

写入内容如下:

[Unit]
## 服务名称,可自定义
Description = frp server
After = network.target syslog.target
Wants = network.target

[Service]
Type = simple
## 启动 frps 的命令,需修改为您的 frps 的安装路径
ExecStart = /usr/local/frp/frps -c /usr/local/frp/frps.ini

[Install]
WantedBy = multi-user.target

使用 systemd 命令,管理 frps:

# 启动frp
systemctl start frps
# 停止frp
systemctl stop frps
# 重启frp
systemctl restart frps
# 查看frp状态
systemctl status frps

配置 frps 开机自启:

systemctl enable frps

frps 配置

frp 服务端部署了 frps 之后,使用编辑工具修改 frps.ini 文件:

vim /usr/local/frp/frps.ini

文件配置内容如下:

[common]
bind_port = 7000
token = 123456
vhost_https_port = 5001
dashboard_port = 7500
dashboard_user = admin
dashboard_pwd = admin

log_file = /usr/local/frp/log/frps.log
log_level = info
log_max_days = 3 

[common] 是固定名称的段落,用于配置通用参数。
[bind_port] 服务器监听端口,默认值为7000,用来接收 frpc 的连接
[token] 鉴权使用的 token 值,客户端需要设置一样的值才能鉴权通过
[vhost_https_port] 为 HTTPS 类型代理监听的端口,启用后才支持 HTTPS 类型的代理,这里访问群晖 DSM 为了安全我们启用 HTTPS
[dashboard_port] 启用 Dashboard 监听的本地端口,使用浏览器通过该端口可以访问 frps 的控制台
[dashboard_user] 访问 Dashboard 的用户名
[dashboard_pwd] 访问 Dashboard 的密码
[log_file] 日志文件存放地址,这里我们将日志保存在 /usr/local/frp/log/ 目录下
[log_level] 日志等级,分为 trace,debug,info,warn,error
[log_max_days] 日志文件保留天数,这里我们设置为 3 天
更多参数可参考 frp 社区文档:https://gofrp.org/docs/reference/server-configures/

配置完成后,重启 frps 服务使之生效:

systemctl restart frps

群晖配置

部署并配置 frpc

在群晖平台我们将 frpc 以 docker 容器的方式部署并运行,需要在套件中心安装并启动 Docker:

image-1669214252938

打开 docker ,从注册表搜 frpc,这里我们选择 snowdreadmtech/frpc

image-1669214289708

选择最新版本,和服务端版本对应上,当前最新版本是 0.45.0

image-1669214322478

切换到 File Station,在 docker 目录创建 frp 目录,在 frp 目录新建 frpc.ini 文件,该文件的配置如下:

[common]
server_addr = 139.172.x.x
server_port = 7000
token = 123456

[Synology DSM]
type = https
local_ip = 127.0.0.1
local_port = 5001
custom_domains = nas.domain.com
use_encryption = true
use_compression = true

[Synology Drive]
type = tcp
local_ip = 127.0.0.1
local_port = 6690
remote_port = 6690

[OpenVpn1194]
type = udp
local_ip = 127.0.0.1
local_port = 1194
remote_port = 1194

[OpenVpn1195]
type = udp
local_ip = 127.0.0.1
local_port = 1195
remote_port = 1195

[common] 是固定名称的段落,用于配置通用参数。
[server_addr] 连接服务器的地址,这里填写云主机的公网地址
[server_port] 连接服务端的端口,和服务端保持一致
[token] 鉴权使用的 token 值,需要和服务端设置一样的值才能鉴权通过
[Synology DSM] 群晖 DSM 门户的访问配置,除此之外,还有 [Synology Drive] [OpenVpn1194] [OpenVpn1195] 各表示不同的代理参数内容
[type] 代理类型,支持 tcp, udp, http, https, stcp, sudp, xtcp, tcpmux,DSM 暴露我们采用 https,Drive 采用 tpc,OpenVPN 采用 udp
[local_ip] 本地服务 IP,frpc 以 host 网络部署在群晖内,所以这里配置 127.0.0.1
[local_port] 本地服务端口,配合 local_ip。这里我们对外代理的服务端口有 5001/6690/1194/1195
[remote_port] 服务端绑定的端口,用户访问此端口的请求会被转发到 local_ip:local_port,这里的端口我们均配置跟 local_port 一致
[custom_domains] 服务器绑定自定义域名,用户通过 vhost_https_port 访问的 HTTP 请求如果 Host 在 custom_domains 配置的域名中,则会被路由到此代理配置的本地服务,这里我们配置成群晖平台的访问域名
[use_encryption] 是否启用加密功能,启用后该代理和服务端之间的通信内容都会被加密传输
[use_compression] 是否启用压缩功能,启用后该代理和服务端之间的通信内容都会被压缩传输
更多参数可参考 frp 社区文档:
https://gofrp.org/docs/reference/client-configures/
https://gofrp.org/docs/reference/proxy/

配置完成后,我们在 docker 映像里启动之前下载下来的映像,在网络里选择“使用与 Docker Host 相同的网络”

image-1669214560665

勾上“使用高权限执行容器”,之后选择“下一步”

image-1669214611057

“存储空间设置”里“添加文件”,选中前面创建和配置好的 frpc.ini 文件

image-1669214647881

装载路径配置为 /etc/frp/frpc.ini ,选择“下一步”

image-1669214687983

检查摘要无误后,选择“完成”
回到“容器”列表,可以看到 frpc 容器已正常启动,双击进去,可以查看该容器的详细信息

image-1669214712643

这个时候,已经可以通过互联网正常访问群晖 DSM 了。

配置群晖 https 访问

为了保证在互联网上的访问安全,建议给群晖配置 https 证书

image-1669224929101

配置 http 连接自动重定向到 https

image-1669214768459

同时开启 MFA 双因子认证登陆

image-1669214795927

在群晖上配置 OpenVPN

通过 frp 除了可以正常访问和使用群晖平台之外,还可以通过 OpenVPN 使互联网上终端可直接访问家庭内网的设备。前面已经在 frp 的客户端做了 OpenVPN 相应的端口配置,采用的是 UDP 协议。接下来需要在群晖 DSM 门户上进行以下配置。
在套件中心安装并启动 VPN Server,群晖的国行官网已将该套件下架,可以访问群晖美版官网下载该套件的离线安装包,下载地址为:https://global.download.synology.com/download/Package/spk/VPNCenter/1.4.7-2901/VPNCenter-x86_64-1.4.7-2901.spk?model=DS920%2B&bays=4&dsm_version=7.1.1&build_number=42962
打开群晖的套件中心,手动安装下载下来的 spk 安装包

image-1669214841408

安装完成之后启动并打开 VPN Server,选择 OpenVPN,勾选上“启动 OpenVPN” 配置动态 IP 地址和其他参数,为了保证连接安全,勾选“验证 TLS auth 密钥”,配置完成后,点击“应用”

image-1669214861693

在 VPN Server 的 OpenVPN 里 “导出配置文件”,将该配置文件导入到 OpenVPN 客户端工具里进行实验,Windows 上推荐的 OpenVPN 客户端工具有 OpenVPN GUI,mac 上推荐的 OpenVPN 客户端工具有 Tunnelblick 和 OpenVPN Connect。

本地路由器配置 DNS 解析

所有应用访问群晖都采用 nas.domain.com 这个域名,目前这域名已经在互联网上进行了 dns 解析,所以在家庭内网访问的话也会通过云主机的 frp 服务器进行代理,为了避免这种情况,我们需要在本地路由器上做自定义 Hosts,即设备连接到内网的 IP 之后会自动将 nas.domain.com 这个域名解析为群晖的内网IP,避免流量绕路的同时还可以享受内网高速网络。
需要注意的是,并不是所有家用路由器都支持该配置,我购买的是小米路由器,可以使用小米WiFi APP 来进行自定义 Hosts 设置:

image-1669214937566

写在最后

至此,所有的配置操作就全部完成,我们可以更大程度不受位置限制的来使用群晖 NAS 存储,当然通过 frp 代理访问还有一些不是很满意的地方,例如,代理之后无法获取客户端的真实 IP,这样所有在互联网上的访问在日志里显示的都是 127.0.0.1,如果有人恶意登陆,平台设置超过五次登陆失败将会把这个 127.0.0.1 这个 IP 加入到黑名单,这样的话也会影响自己登陆,所以下一步会研究一下如何实现通过 frp 可以获取客户端的真实 IP 地址。

参考链接

https://gofrp.org
https://kb.synology.cn/zh-cn/DSM/tutorial/What_network_ports_are_used_by_Synology_services