文中提及或使用的 .NET 开源项目: SangServerTool(DDNS,SSL证书申请工具),FastTunnel(内网穿透工具),Certes(ACME证书申请库),CommandLineParser(命令行解析库)
背景
前几天用.NET玩IoT设备,拿出了角落吃灰的Jetson Nano。近期也买了一堆传感器,还在路上,准备到手之后,好好捣鼓一番。Nano设备呢,虽然没有一直开机,但是连上了智能插座,随时待命。
这里不禁要吐槽一下小爱同学,我把插座命令为了“Jetson Nano”,然后怎么叫它,小爱就是不应。行吧,我只能叫他“二蛋”了,啊,不,“小电脑”(然后还被时不时听成“手电筒”)。
为了让后期的使用可以随时随地,更顺手些,那网络的处理是必不可少的。
如何在外网访问内网服务
如果你也有一台树莓派或者Jetson设备,想让其在外网提供服务,那么一般有这么几条路:
- 在路由器中将设备设置为DMZ区
- 在路由器中配置虚拟主机
- 借助其他第三方内网穿透工具
因为现在一般都是光宽带了,前两种呢需要拿到光猫的管理员账户,账户需要费些功夫。除非你是桥接的网络,用的自家路由器拨号。
第三种,如果你有一台外网的服务器,可以借助开源项目 FastTunnel 隧道开实现。这是一个.NET开发的开源的内网穿透工具。当然你也可以使用其他的第三方内网穿透工具,这里不再举例。
工具虽好,然则不能全速走宽带的公网带宽着实非完美的方案。那么有没有更好的方案呢?当然有了,现在IPv6如此普及,待我ipconfig
一观:
果然比沙子还多,诚不欺我。但当我拿出之前的 nodejs 项目修改了IP监听后,兴致勃勃的用手机浏览器访问了这个 IPv6 站点,我不禁开始怀疑人生了。服务没法访问就算了,还 ping 不通。在折腾了几番电脑防火墙和管理员身份启动之后,已是半夜。无奈只好躺在床上简单搜索了几番答案,便去找周公探讨究竟是哪里出了问题。
翌日,再次打开电脑我才发现,果然不能老熬夜啊,我监听的IP竟然是 0.0.0.0
,这是IPv4的!IPv6要用 ::
。这次等我再次用手机流量访问网站,果然就顺利了许多。完全不需要什么电脑防火墙给开入站规则用管理员去权限嘛,直接就进来了。
知识点:
- 包含端口号的 IPv6 地址
http://[0:0:0:0:0:ffff:4137:270a]:9080/
- IPv6 监听的
::
和 IPv4 的0.0.0.0
等效- IPv6 监听的
::1
和 IPv4 的127.0.0.1
等效,都是环回接口
什么是 DDNS
我想大家应该都清楚域名解析是什么,就是将不好记的IPv4地址变为好记的域名嘛。DDNS 多的D 呢,是 Dynamic ,顾名思义就是将我们老变的宽带IP变成固定的域名访问。
之前我们拿到了一串那么长的 IPv6 地址,不仅是分配的IP老变的问题,再说他也不好记啊。
这时,我们除了使用常见的 DDNS 服务商的服务外,我们也可以拿出我们程序猿之前给女朋友买的,那些便宜的,女朋友并不喜欢的,域名来,优势当然就是,自己的域名,自己选的,想怎么解析前缀就怎么解析。
好的,那么假设你有一个阿里云域名(要已备案),通过 解析管理接口 我们很容易就能自己做一个 DDNS 服务出来。
SSL证书自动申请
既然 DDNS 要自己整活,那之前写的自动域名证书续签服务也可以直接整合进来,做个新的工具。毕竟,现在是个网站都用 https 了。
证书自动申请这里使用的是 Certes 库来实现 Let’s Encrypt 证书的自动续签。
Let’s Encrypt 是一个证书颁发机构(CA)。要从 Let’s Encrypt 获取网站域名的证书,只需要证明对域名的实际控制权即可。有两种验证方式,通过域名解析添加 TXT 记录,或是在网站添加指定的验证文件(实现访问指定地址返回要求的字符串即可,不过不支持有通配符的申请)。
那么假设你有一个域名,通过接口解析管理这也很好实现。
SangServerTool
将两个功能整合成一个小工具,我起名叫 SangServerTool
,开源地址: https://github.com/marin1993/SangServerTool 。
依赖 .NET 跨平台的特性,可以方便的在各种服务器上使用。
作为一个控制台应用程序,在参数解析上使用 CommandLineParser 这个命令行解析库。这个库提供了简洁明了的 API,用于操作命令行参数和相关任务,并提供帮助界面。
SangServerTool 包含两款工具:
- 服务器 DDNS 工具,用于内网服务动态域名解析,支持 IPv6
- 服务器 SSL 证书申请工具
目前仅支持阿里云,其他云服务的实现可以自行添加。
提供独立的 linux-x64、linux-arm、linux-arm64、win-x64 下载。其他平台可自行通过源码编译发布。
这个服务的启动一般来说不需要一直运行。DDNS可以在设备开启时检测一次,以后每间隔一段时间检测一次,如一小时。SSL证书申请,可以每天0点固定检查一次即可,将要过期时,程序会自动进行续期,更新证书。注意 nginx 等服务需要重新加载一下证书,可配置 Certificate:okshell
来实现申请成功调用你指定的脚本文件。
工具的使用可以通过传入不同的参数和配置文件来实现不同的功能。DDNS和SSL证书申请作为工具,只需要根据自己的需求设置好计划任务即可,下面介绍这两个功能的基本使用。
DDNS
参数说明:
参数 | 说明 |
---|---|
-c, --config | Required. Set config json file. 设置配置文件路径 |
--delay | (Default: 0) How many seconds delay? 启动后延迟多少秒进行检查处理,默认为 0,防止开机启动过早导致出现一些问题 |
--del | (Default: false) Is delete DDNS? 删除配置文件中设置的DDNS域名解析,默认为 false ,如果为 true,则尝试删除后退出 |
--v6 | (Default: false) Is ipv6? 使用 IPv6 来解析,默认获取 IPv4 |
--ip | (Default: ) If set will be used. Otherwise automatically obtained. You can set 'ifconfig', It will check from 'https://ifconfig.me/ip' to get you Internet IP. 默认为空字符,如果传入了指定 IP ,则使用这个 IP 来解析。 可以传入 'ifconfig' 值,该值则表示通过网络获取网络出口 IP 来解析 |
如:使用本地的 IPv6 进行 DDNS 设置
SangServerTool ddns -c "test.json" --v6=1
如:删除 DDNS 的域名解析
SangServerTool ddns -c "test.json" --del=1
该功能的配置文件使用 Access
和 DDNS
这两段。
{
"Access": {
"AK": "阿里云 AccessKeyId", //AccessKeyId
"SK": "阿里云 AccessKeySecret" //AccessKeySecret
},
"DDNS": {
"ddns": "xxx.domain.com", // DDNS要解析的域名
"basedomain": "domain.com" // 主域名
}
}
这一功能的核心其实是电脑网卡IP的获取,需要判断网卡的类型,排除回环和临时和本地的 IPv6 地址(临时IPv6不会第一个FirstOrDefault()
就可以了)。
/// <summary>
/// 获取电脑网卡IP
/// </summary>
/// <param name="isV6">是获取IPv6</param>
/// <returns></returns>
public static string? CurrentIPAddress(bool isV6 = false)
{
var family = isV6? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork;
List<string> exps = new List<string> { "docker0", "lo", "l4tbr0" };
var ips = NetworkInterface.GetAllNetworkInterfaces()
.Where(p => !exps.Contains(p.Name)) // 排除docker、lo等
.Select(p => p.GetIPProperties())
.SelectMany(p => p.UnicastAddresses)
.Where(p => p.Address.AddressFamily == family && !IPAddress.IsLoopback(p.Address));
//IPv6 时去除本地的
if (family == AddressFamily.InterNetworkV6)
{
ips = ips.Where(p => !p.Address.IsIPv6LinkLocal);
}
return ips.FirstOrDefault()?.Address.ToString();
}
SSL
参数说明:
参数 | 说明 |
---|---|
-c, --config | Required. Set config json file. 设置配置文件路径 |
--retry | (Default: 2) How many retries? 验证域名时重试几次,默认2次 |
--delay | (Default: 10) How many seconds to retry? 验证域名时重试间隔多少秒,默认10秒 |
如:申请域名重试 3 次
SangServerTool ssl -c "test.json" --retry=3
该功能的配置文件使用 Access
、 Certificate
、 ACME
、CSR
,文章不详细介绍配置文件,详细配置文件说明可前往仓库查阅。
在配置 Certificate
信息时:
- 如果是新申请的只需要配置好证书
cerpath
和证书私钥privatekey
的存放路径,程序会自行生成。若已经有证书会私钥配置好其位置会自行更新证书或使用当前已有的私钥。 domains
支持多个域名,使用空格隔开okshell
证书更新后执行的脚本文件,如果服务器不能热加载证书,记得配置好,通过脚本文件进行重启服务
在配置 ACME
信息时:
- 如果第一次使用仅需要写上你的邮箱
email
和存放 ACME 账户的私钥文件位置account
,证书过期会收到邮件提醒 - 如果之前已有账户,可以使用已有的账户私钥,配置给
account
关于 CSR
,这段配不配都无所谓,毕竟是免费的证书,也不会生效,只是验证了域名的归属权。
DDNS 配置使用示例
下面以 Jetson Nano 为例,演示其 DDNS 功能的配置使用。Windows 系统可通过“任务计划程序”进行类似操作。
- 首先,前往仓库的 releases 下载程序上传到 Jetson Nano,然后添加执行权限。
- 按照说明编写自己的配置文件
- 编写开机启动服务
sudo vi /etc/systemd/system/ddns.service
文件内容如下:
[Unit]
Description=SangServerTool DDNS
After=network.target
ConditionPathExists=/home/sangsq/.tools/SangServerTool
[Service]
Type=forking
ExecStart=/home/sangsq/.tools/SangServerTool ddns -c /home/sangsq/.tools/config.json --v6=1 --delay=30
TimeoutSec=0
StandardOutput=journal+console
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
ConditionPathExists
为刚上传的程序文件地址,当其存在这个服务才会启动
ExecStart
这里要写程序和配置文件的全路径,在这里我用的是 IPv6 地址进行解析。保险起见,服务启动后延迟 30 秒后开始执行,主要是接口查询需要访问阿里云服务器,刚启动的时候,直接运行可能会报 DNS 解析的错误,也许使用 After=network-online.target
会解决,不过没有测试这个。
- 设置开机启动服务
sudo systemctl enable ddns.service
- 添加计划任务
除了开机启动外,我们也可以通过计划任务,半个小时执行以下程序,检查 IP 是否有变化。
sudo crontab -e
添加计划任务
*/30 * * * * /home/sangsq/.tools/SangServerTool ddns -c /home/sangsq/.tools/config.json --v6=1
这里去除了延迟的检测,因为不是刚开机了。
- 其他
SSL 证书申请也可以通过计划任务处理,每天 0 点检查一次,如果服务器不能热加载证书,记得在配置文件配置好 okshell
,来实现 web 服务器的重启。
后记
这篇文章随着软件的开发迭代,修修改改,终于算是告一段落。后续好好整活这个台小电脑,再补充写一些其他的硬件设备。
目前手机的网络应该是都有 IPv6 地址的,但是如果你使用的网络只接入了 IPv4,那么你就不能访问纯IPv6的服务器。如果运营商支持,但是你的路由器不支持,你也是无法使用 IPv6 网络的。如果你想检测自己的 IPv6 可用性,可以访问这个 IPv6 检测网站 。