使用 systemd 可以做的更有趣的事情之一是使用“套接字激活”功能:systemd 本身打开某种套接字进行监听,然后将其交给您的程序,就像 inetd 风格一样。是的,我知道,通过说“inetd 风格”,它甚至算不上是一个新事物。明显地。这是关于你还可以用它做什么。
就像我之前关于 systemd 的故事一样,您可以添加“拒绝”和“允许”规则,这会给您正在做的任何事情带来另一个维度的过滤。这适用于 .socket 文件,它们是套接字激活的一部分。它甚至可以将其强制绑定到特定的接口,即:
[插座] 监听流=443 IP地址拒绝=任意 IP地址允许=192.0.2.0/24 绑定到设备=wg0
这为您提供了一个侦听 TCP 端口 443 的套接字,并且它将执行一些 bpf 恶作剧来丢弃流量,除非另一端位于该特定的 /24 中。然后它还会锁定它,这样它就不会监听整个世界,而是绑定到此 wg0 接口(在本例中意味着 WireGuard)。
这加上通常的 ip[6]tables 规则将使事情的定义非常狭窄,这正是我喜欢的方式。
在过去的一年里,我做了很大的努力,然后在安装了这样的魔法后再也没有重新启动过有问题的盒子。然后本周早些时候,我将该系统的“个性”迁移到新硬件,这意味着到处启动和重新启动,每次都花费近两分钟重新启动,这不是很奇怪吗?到底是什么,对吧?
深入研究 systemd 日志发现一些“wg”的东西没有出现,而且它看起来确实像一个依赖循环。 A依赖于B,B又依赖于C,B依赖于D,B又依赖于A?如果不是最终超时,它就不会启动。
我很感谢那个暂停,因为盒子的其余部分出现了,我能够进入那个无头小怪物来解决这个问题。
问题基本上是这样的:如果您在 systemd 世界中安装了 .socket,则默认情况下您会在启动时在排序/排序方面获取几个依赖项,其中之一是“sockets.target”。您的 foo.socket 本质上有一个“Before=sockets.target”,这意味着在您启动并运行之前,sockets.target 不会成功。
但是,如果您的 foo.socket 有一个指向 WireGuard 的 BindToDevice 怎么办?现在你对即将出现的 wg0 事物有了依赖,而且,至少在 Debian 上,这变得很有趣,因为它(“wg-quick@wg0”或类似的)想要 basic.target 完成,并且 basic.target 完成反过来希望 sockets.target 首先发生。
foo.socket 等待 wg 等待基本等待套接字 等待 foo.socket。这就是循环。
摆脱这种混乱意味着打破循环,而这样做的方法是从 .socket 文件中删除默认依赖项,如下所示:
[单元] 默认依赖项=否
之后,您需要在 .socket 上设置适当的 WantedBy、Wants、Before 或 After 声明,以确保它附加到调用图的某个位置。
我应该提到的是,在我走到这一步之前,我花了很多时间重新启动、分析日记、咒骂和抱怨事情。如果你陷入这样的混乱,“systemd-analyze dump <whatever>”通常是你的朋友,因为它会指出“隐式”依赖关系,这些依赖关系同样重要,但不会出现在你的 .socket 中或 .service 文件。然后你可以在纸上画出草图,再咒骂一些,并调整内容以不再循环。
在启动过程中出现此类问题之前,似乎没有一个好的方法可以发现此类问题。在你用大炮直接瞄准你的脚之前,这当然不会阻止你。显然,“systemd-analyze verify <whatever>”至少会警告您有一个循环,但弄清楚如何到达那里以及如何处理它完全取决于您。另外,如果您不记得运行该验证步骤,那么显然它不会对您有帮助。我刚刚在写这篇文章时才了解到这一点 – 对于我遇到的问题来说太晚了。
我确实喜欢这些功能,但复杂性可能是一个真正的挑战。