基于Unraid的高成本自建网盘及私有云服务:(三)Wireguard+DDNS 实现安全远程访问

unraid在局域网欢乐地用起来了,但是公网访问怎么办呢?

说明:本文归入unraid折腾系列,因此nas系统就以unraid为例,实际上提及的技术适用于任何nas系统,对于群晖、qnap、freenas、omv等系统,在配置上基本上大同小异。

这里特别强调安全,因为我们不希望数据是通过非加密的方式传输的,比如:把samba共享暴露到公网通过直连访问数据。另外,直接把unraid服务器暴露到公网本身也很危险。

本文介绍通过ipv6公网安全访问数据以及服务的具体部署方法,包含两大核心内容:部署wireguard组建虚拟局域网,以及部署ddns解析域名。

1. 背景

ipv4公网地址一号难求。ipv6已经全面铺开。国内云主机性价比太低,国外云主机延迟太高、速度太慢。wireguard简洁、高效、相对安全,部署简单,全平台。

2. 总体思路

有ipv4公网地址的,用10+年前的ddns大法,这个网上教程很多。拿不到ipv4公网地址的用ipv6,起码不少地区的可以获得/60的前缀的,就是只分配1个/64子网也远远够用了。无论是ipv4还是ipv6,都需要通过ddns解析域名到公网地址(有固定ip的就不必了)。公网地址只是解决了直连这个关键问题,接下来就要解决加密通信的问题。

2.1. 关于ddns

目前,ipv6上提供ddns的机构数量还比不过ipv4。在此推荐一下dynv6。dynv6提供自定义三级域名以及对应的dns解析服务,支持txt记录,因此想要获取泛域名https证书也是没有问题的。dynv6同时支持http、https、REST API等多种方式来更新dns记录,非常方便。

如果只是简单应用,比如nas远程访问、监控、远程串流游戏等等,用dynv6完全够用了,免费且相对可靠(dynv6官方不建议用于企业等核心业务上)。

2.2. 关于加密通信

思路:

部署反向代理服务器,通过https的方式访问各种服务,比如nextcloud、jellyfin等。或者部署网关,相关设备加入一个虚拟私有网络(virtual private network,也就是vp*),用类似局域网的方式来访问各种服务。

个人两种方式都试过,目前更偏向于第2种,因为非常灵活,也可以异地组网(实现异地同步备份数据等等)。

2.3. 关于unraid主机安全

unraid直接支持安装wireguard插件来提供配置vp*的前端页面。我并不推荐将unraid直接部署为wireguard中继服务器节点,因为这样势必将unraid暴露在ipv6公网上。即便目前几乎不可能通过扫描的方式发现unraid主机,但将其隐藏在nat后面更安全一点。

本文采取的方法是,创建一个alpine虚拟机并部署成为wireguard中继服务器节点,unraid主机、手机、pc等设备作为客户端节点连入,从而组成虚拟局域网,以达到通过类似局域网的方式安全访问数据或服务的目的。

实测,手机在移动4G网络下,使用moonlight远程串流3A大作游戏,基本可以稳定720p@60fps,4G信号较好的地方可达1080p@60fps。

3.开始部署

3.1. 创建虚拟主机

实际上,本文提到的unraid主机有4个物理网卡,其中物理网卡a接入互联网(网卡a本身不予分配任何ip地址),物理网卡b接入局域网用于后台管理(网卡b分配地址192.168.1.2/24),ab网卡之间通过软路由联通。这样一来,虽然unraid主机接入了ipv6公网,但因为物理机本身并没有被分配任何公网ip地址,因此unraid主机无法通过公网直接访问,这样增加了一点安全性。 下载alpine的iso镜像文件并上传到unraid服务器里(或者直接在unraid终端里面通过wget下载)。我使用的时候virt版本的镜像(alpine-virt-3.13.0-x86_64.iso)。在unraid的vm标签下,“add vm”创建linux虚拟机,实测内存256M够用了。根据实际网卡情况,如果unraid主机有多张网卡,那么桥接或者直通一张网卡皆可,如果只有一张网卡,只能桥接了。挂载alpine iso镜像。启动虚拟机,安装系统。

3.2. 部署wireguard中继服务器

为了方便识别,后文统一称为alpine中继服务器,即包含了系统信息和功能信息。

开启内核级别的ip转发功能

必须开启,否则各个设备只能各自与中继服务器通信,但无法以中继服务器为桥梁来联通。

编辑/etc/sysctl.conf并增加以下内容:

net.ipv4.ip_forward=1 net.ipv4.conf.all.proxy_arp=1

运行以下命令加载配置文件后重启系统:

sysctl -p

2. 安装wireguard

apk add wireguard-tools

3. 生成密钥对

wg genkey | tee privatekey | wg pubkey > publickey

4. 创建并编辑:/etc/wireguard/wg0.conf

# 注意,这是位于alpine服务器上的wg0.conf [Interface] Address = 10.0.0.1/32# 设置一个与现有网段都不一样的局域网ip地址 ListenPort = 45340 # 请自定义监听端口 PrivateKey = ......# 填入第2步里面生成的私钥 [Peer] PublicKey= P556VVMhhBb+kc2Gm7JV46jh1oRv75YscK8q131iSnM=# 填入对等节点的公钥 PresharedKey = oZ33KPYBwo7aFyxewm5Eutlelztz9lXQ2Z8tyouc7TM=# 可选,将增加安全性,每个peer均不相同,通过“wg genpsk”命令生成 AllowedIPs = 10.0.0.2/32

以后每添加1个对等节点,就在alpine服务器上的wg0.conf增加[peer]及相关信息,具体信息参考后文第3.4节以帮助理解。

AllowedIPs一项起到设置路由表的作用,每个peer之间不允许有重合。由于本次仅仅是为了让多个设备加入虚拟局域网,不需要转发,所以设置为/32就好。

5. 手动启动wireguard

wg-quick up wg0

实际上,为了方便起见,这里推荐暂时先不启动wg0,等添加完所有[peer]条目后,最后再使用该命令启动,这样可以避免每次添加[peer]都要重启wg0。

6. 设置开机自启动(方法之一)

如果因为各种原因,导致服务器重启,那么wg0不会自动启动,需要手动重复上一步。如果希望开机自启,编辑:/etc/network/interfaces:

# 注意,这是位于alpine服务器上的:/etc/network/interfaces auto wg0 iface wg0 inet static address 10.0.0.1# 即alpine服务器的虚拟局域网ip地址,必须与现有ip地址处于不同网段 netmask 255.255.255.0 pre-up ip link add dev wg0 type wireguard pre-up wg setconf wg0 /etc/wireguard/wg0.conf post-up ip route add 10.0.0.1/24 dev wg0# 根据address设置 post-down ip link delete wg0

如此一来,pre-up wg setconf wg0 /etc/wireguard/wg0.conf,这一栏将会报错导致不能顺利组网,提示不能识别wg0.conf里面的Address字段:编辑wg0.conf,删掉或备注掉[interface]下的Address即可。局域网ip地址将通过/etc/network/interfaces直接指定,其实与wg-quick命令本质上是一致的。

注意,这里介绍的只是其中一种设置开机自启的办法。该方法与wg-quick会有冲突,比如临时添加[peer]后通过wg-quick up wg0会导致问题。

至此,wireguard中继服务器部署完毕,接下来配置ddns解析。

3.3. 部署ddns解析

这里仅介绍通过http协议、查询字符串里拼接token和ipv6 gua来更新dynv6的AAAA记录的办法。

上dynv6官网注册账号,申请一个三级域名,生成对应的http token备用,过程很简单,不再说明。在alpine服务器上,创建一个脚本文件,命名为dynv6_ipv6.sh(或者任意你喜欢的名字),填入脚本内容,该脚本的功能为检测本机ipv6 gua然后通过http协议在dynv6上更新dns记录:#!/bin/sh -e hostname=$1 device=$2 file=$HOME/.dynv6.addr6 [ -e $file ] && old=`cat $file` if [ -z "$hostname" -o -z "$token" ]; then echo "Usage: token=<your-authentication-token> [netmask=64] $0 your-name.dynv6.net [device]" exit 1 fi if [ -z "$netmask" ]; then netmask=128 fi if [ -n "$device" ]; then device="dev $device" fi address=$(ip -6 addr list scope global $device | grep -v " fd" | sed -n s/.*inet6 \([0-9a-f:]\+\).*/\1/p | head -n 1) if [ -e /usr/bin/curl ]; then bin="curl -fsS" elif [ -e /usr/bin/wget ]; then bin="wget -O-" else echo "neither curl nor wget found" exit 1 fi if [ -z "$address" ]; then echo "no IPv6 address found" exit 1 fi # address with netmask current=$address/$netmask if [ "$old" = "$current" ]; then exit fi # send address to dynv6 $bin "?hostname=$hostname&ipv6=$current&token=$token" # save current address echo $current > $file

记得赋予该脚本可执行的权限:

chmod +x dynv6_ipv6.sh

3. 在alpine服务器上,编辑crontab以自动定期执行上述dns记录更新脚本:

crontab -e

增加以下内容后保存退出:

*/1 * * * * token=在dynv6上设置的key ./dynv6_ipv6.sh 你的域名 >> ./ddns_log.txt 2>&1 # 比如: */1 * * * * token=xxxxxxxxxx ./dynv6_ipv6 example.dynv6.net >> /home/xxx/ddns_log.txt 2>&1 简要说明:*/1 * * * * # 即crontab里指定每分钟运行此脚本。token=xxxxxxxxxx # http token可以在dynv6里面为每一个域名单独生成,增加一定的安全性。>> /home/xxx/ddns_log.txt 2>&1 # 即将脚本运行结果重定向输出至一个文件方便追踪。

至此就基本上可以通过域名来直连alpine服务器了。

3.4. 将unraid主机作为客户端连入

在wireguard协议里,每台主机无论起到什么实际作用,中继服务器也好,客户端也好,其地位均平等。每台主机关于本机的接口信息均在各自wg0.conf里的[interface]下,关于其他节点的信息都在[peer]下。

如此思路就很明确了,先在每一台设备上先配置好本机的[interface],把alpine中继服务器作为[peer]添加,然后启动各自的wg0。最后统一在alpine中继服务器上的wg0.conf把各个连入的设备作为[peer]添加,并启动alpine中继服务器的wg0即可。所有主机地位平等,wireguard会自动尝试重连,因此不用担心启动顺序。

unraid主机这边,首先需要在unraid里安装一款叫做Dynamix WireGuard的插件以提供前端配置界面。通过settings -> network services -> vp* manager进入wireguard配置界面。

首先点击右上角add tunnel。

填写相关信息,这里相当于wg0.conf配置文件里的[Interface]相关条目,也就是设置本机的相关接口信息。点击generate keypair生成密钥对后将自动填好。切换basic到advanced打开全部项目。

以下简要说明必填项目:

Network protocol: ipv4 only # 为了简化配置,选择ipv4 only即可,地址方便记忆。 Local tunnel network pool: 10.0.0.0/24 # 填写虚拟局域网网段信息,沿用本文前述alpine服务器虚拟局域网网段,在此填入10.0.0.0/24。 Local tunnel address: 10.0.0.2 # 指定unraid主机在虚拟局域网的ip,日后将通过该ip访问unraid主机上的数据以及服务。 Local endpoint: 192.168.1.2:5000 # 指定unraid主机的监听地址和端口,端口是必须填写的,ip可以填入unraid主机本地局域网地址(也就是unraid管理后台的ip)。

2. 然后点击add peer。

在wireguard里,所有节点都是平等的,不会严格区分服务端及客户端。这里的peer实际上应该填写alpine中继服务器的公钥、监听地址端口等信息。

以下简要说明必填项目:

Peer type of access: remote access to server # 点击即可展开说明,因为本文仅仅介绍远程访问服务器,暂不设置lan到lan转发,所以在此设置为“remote access to server”即可满足要求。 Peer public key: # 必填,否则无法与alpine中继服务器实现双向通信,填入alpine中继服务器的公钥。 Peer endpoint: 192.168.1.5:45340 # 填入alpine服务器局域网ip,注意不是虚拟局域网ip。以本人为例,alpine中继服务器的eth0网卡的ip为192.168.1.5,wireguard监听端口为45340。 Peer allowed IPs: 10.0.0.1/32 # 即设置路由规则,wireguard将根据该条目来判断数据该发往何处。 Persistent keepalive: 25 # 是否发送udp心跳包,以及时间间隔,非必填。

3. 最后就在wireguard配置界面里切换inactive到active即可,推荐也把autostart开启。

至此,unraid主机便完成配置。为了能够实现与alpine中继服务器双向通信,务必记得在alpine中继服务器上的wg0.conf文件添加与unraid主机对应的[Peer]条目。以下为示例:

# 注意,这是位于alpine服务器上的wg0.conf [Interface] Address = 10.0.0.1/32 ListenPort = 45340 PrivateKey = ...... [Peer] # unraid PublicKey= ......# 填入unraid里“tunnel wg0”下的“local public key" PresharedKey = ......# 可选 AllowedIPs = 10.0.0.2/32# 填入unraid主机的虚拟局域网ip

3.5. 将其他设备连入虚拟局域网

参考第3.3节,采用类似的办法配置各个设备即可。最后alpine中继服务器上启动wg0,虚拟局域网就互联互通了。

4. 优化配置

这里提供一些思路:

alpine中继服务器上的ssh服务,配置为公钥免密登录。

网上搜一下教程即可。

2. 开启ipv6隐私扩展

也就是启用临时地址,避免mac地址泄露。

# /etc/sysctl.conf net.ipv6.conf.eth0.use_tempaddr=2# 2表示开启隐私扩展 net.ipv6.conf.eth0.temp_valid_lft=14400# 单位为秒,14400为示范用时间,即4小时 net.ipv6.conf.eth0.temp_prefered_lft=86400# 单位为秒,86400为示范用时间,即1天

运行以下命令加载配置文件后重启系统:

sysctl -p

3. lan到lan转发

稍高级的玩法,可以实现连接多个内网。

参考文章:https://anyisalin.github.io/2018/11/21/fast-flexible-nat-to-nat-vpn-wireguard/

# /etc/wireguard/wg0.conf # 启动动作, 要把 eth0 替换成自己的网口名称,这里设置了ipv6转发,因此需要相应的配置。 PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # 关闭动作 PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

4. 设置防火墙等。