WireGuard 配置笔记

碎碎念 之前一直想着部署一套系统,好让自己不在家的时候也能访问家中部署的服务。然而使用 frp 之类的工具直接将服务暴露在公网上还是过于危险了一些,出于安全考虑,决定使用 VPN 工具组建一个虚拟的局域网,这样要进入家中的私有网络就必须先攻破 VPN 的防护,起到了一定的安全保障作用,让人比较放心。 说起 VPN,马上想到的自然是 OpenVPN。可惜我花了一两周时间也没搞明白我到底要部署哪些东西,写哪些配置文件,实在是太繁琐了。而且官方提供的 OpenVPN Server 貌似还只能提供两个客户端的连接支持,也不太够我几台设备用,所以后面看到 WireGuard 配置起来那么方便果断放弃了折腾 OpenVPN,省下了不少头发。WireGuard 配置起来又省心又方便,虽然功能可能没有 OpenVPN 那么强大,但对于简单的建立隧道的需求来说,WireGuard 已经完全够用了,而稳定性与安全性方面有 Linux 内核的背书,无需顾虑。比较蛋疼的一点是这个在 BSD 系统上支持不算好,在我的 pfSense 防火墙上配置需要一点手动操作,不过无伤大雅。这里就我配置 WireGuard 的过程作一简单记录,以供参考。 Wireguard 是怎么工作的 先简单讲讲 WireGuard 的工作流程。WireGuard 的工作核心是 TUN 网卡。对 Linux 网络稍微有点了解的人可能知道 TUN 设备是什么,简单来说就是虚拟出来的一张网卡,向这张网卡发送的数据会被送到指定的应用进行处理。建立隧道的两端需要分别运行 WireGuard,然后两边的 WireGuard 各创建一个虚拟网卡,网卡通向 WireGuard,两边 WireGuard 再建立一条网络连接,一根虚拟的网线就插上了,我们就有了一个点对点的 VPN 隧道。 除了两个 WireGuard 客户端之间点对点的隧道,WireGuard 还支持同一个接口连接多个客户端,这时候就有点像在接口之间插上了一个路由器,由 WireGuard 在这些接口之间转发数据,就有点像家里路由器和电脑的关系。这两种模式就是 WireGuard 的两种主要工作模式,非常简单。 WireGuard 的配置 WireGuard 套件的安装就不再赘述,官网上提供了各大发行版的详细信息。安装完毕后,有两种模式建立 WireGuard 隧道。一种方式是直接使用命令行工具,很方便,但后续调整起来比较麻烦。个人倾向于先写配置文件,再通过 wg-quick 这一工具根据配置文件启动 WireGuard 隧道。 wg-quick 用到的配置文件以 .conf 结尾,格式和 INI 很像,默认存储在 /etc/wireguard 目录下。配置文件主要由两部分构成,一个 [Interface] 部分用来设置本地端点的信息,而 [Peer] 则描述要连接到的远程接口的信息。一个 .conf 配置文件中只能有一个 [Interface],但能有多个 [Peer]。如果只设置一个 [Peer],那建立的就是点对点隧道。如果设置多个 [Peer],就需要 WireGuard 应用程序进行路由了。 ...

January 3, 2022 · 2 min · H1RA

使用反射在 Go 中动态解析 json

最近在用 Go 写一个对接某个 API 的 Adapter 时碰到了一点关于 json 反序列化的问题。对方通过 websocket 传递 json 来进行推送消息,但是推送的格式不太统一,而且种类繁多,难以使用少量通用的结构体类型来对这些消息进行反序列化。不过好在这些消息中都有一个共同的 type 字段用于标识该 json 对象的类型,不至于手足无措。在经过几天的资料查询参考后,通过一些解析手段和反射完成了对推送消息的解析,做记录如下。 动态 JSON 解析 对于大量格式不同的 json 对象,最为简单粗暴的方法当然就是用万能的 interface {} 去进行解析,再复杂的 json 对象也能解析成简单的基本类型组合。这样子做虽然在解析的时候舒服了不少,但是后续访问对象字段就麻烦了,需要不断使用字典之类的访问方法对其进行访问,而且 interface {} 类型对 IDE 的智能提示也非常不友好,后续的维护也更为繁琐,容易出问题。以上问题一叠加,interface {} 自然是下下策。那么有什么办法来在运行时根据 json 对象自动选择相应类型进行序列化呢? 延迟解析 一个比较好的方法是使用 json.RawMessage 类型来延后解析。json.RawMessage 是 json 标准库中提供的一个类型,预先被定义为这个类型的字段在 json.Unmarshal 时,json 库将不会对这个字段进行递归解析,而是将这个字段对应的 raw json 数据存入该字段,并可用于后续解析。 下面用一个简单的例子来说明这个方法,假设我们的接口会返回如下两种格式的 json: // Fruit json { "type": "fruit", "data": { "name": "orange", "color": "red", "taste": "sweet" } } //vegetable json { "type": "vegetable", "data": { "name": "corn", "size": "big", "juicy": false } } 以上两种格式的 json 的 data 字段下存放的 json 对象格式不同,而 data 字段所存放的 json 格式由 type 字段规定。这就给了我们一个思路,在首次解析中保留 data 的 RawMessage 数据,然后通过解析出来的 type 字段值来确定使用哪个静态类型对 data 字段的 RawMessage 进行解析。解析的代码可能看起来像这个样子: ...

October 5, 2021 · 3 min · H1RA