@@ -118,6 +118,30 @@ WantedBy=multi-user.target
118118
119119 对于定时器,`[Timer]` 部分的字段可以在 [`systemd.timer(5)`][systemd.timer.5] 中找到。
120120
121+ !!! note "Generator"
122+
123+ 或许你会注意到,有一些 unit 文件存储在 `/run/systemd/generator/` 下:
124+
125+ ```console
126+ $ systemctl status home.mount
127+ ● home.mount - /home
128+ Loaded: loaded (/etc/fstab; generated)
129+ Active: active (mounted) since Mon 2026-04-06 09:27:46 UTC; 1 day 8h ago
130+ Invocation: 513aed29e7f049babe06455dbbc90087
131+ Where: /home
132+ What: /dev/vda1
133+ Docs: man:fstab(5)
134+ man:systemd-fstab-generator(8)
135+ Tasks: 0 (limit: 2318)
136+ Memory: 84K (peak: 1.7M)
137+ CPU: 4ms
138+ CGroup: /system.slice/home.mount
139+ $ ls /run/systemd/generator/home.mount
140+ /run/systemd/generator/home.mount
141+ ```
142+
143+ 这些临时生成出来的 unit 是由 [systemd.generator.5][systemd.generator.5] 生成的。Generator 程序(大部分都在 `/usr/lib/systemd/system-generators/`)会在系统启动最开始,以及重新加载配置的时候执行,生成对应的 unit。
144+
121145#### 顺序与依赖 {#unit-dependency}
122146
123147相比于 SysVinit(完全顺序启动)和 upstart(基于 event 触发的方式有限的并行),systemd 的每个 unit 都明确指定了依赖关系,分析依赖关系后 systemd 就可以最大化并行启动服务,这样可以大大缩短启动时间。
@@ -339,6 +363,8 @@ notify 和 dbus
339363
340364Socket activation 帮助实现了服务的惰性加载,可以在不必要的情况下减小资源占用,并且提升系统启动速度。例如对 Docker,` docker.service ` 如果 enable,它可能会在系统启动的关键路径上面占用几秒的时间,而如果不 enable ` docker.service ` ,改为 enable ` docker.socket ` ,那么就能够减小启动时间,让对应服务延迟到首次使用时开启。同时,socket activation 机制允许 systemd 预先绑定低权限用户无法绑定的低端口(小于 1024),然后在有用户访问时把 socket 交给低权限的服务进程。
341365
366+ 当然,对 SSH,如果你想继续使用 ` ssh.socket ` 并且修改端口的话,就需要 ` systemctl edit ssh.socket ` ,而不是编辑 sshd 配置了。
367+
342368!!! note "应用是如何获取到自己的 socket 的?"
343369
344370 Systemd 提供了两种方法:应用可以用 libsystemd 的 [sd_listen_fds(3)][sd_listen_fds.3] 函数获取到 socket 对应的文件描述符,然后直接 `accept` 或者 `recv`(根据 `Accept` 选项的不同)。
@@ -353,6 +379,40 @@ Socket activation 帮助实现了服务的惰性加载,可以在不必要的
353379
354380 如果无法切换到 `Accept=no`,那么可能需要增大最大连接数,或者利用 [fail2ban](./security.md#public-service-and-login) 与[防火墙](./network/firewall.md)等方式来阻止恶意的扫描影响正常服务。
355381
382+ !!! note "systemd-ssh-generator"
383+
384+ 可能出乎人意料的是,systemd 默认除了对外的 TCP 22 端口以外,还会额外 bind:
385+
386+ - `/run/ssh-unix-local/socket`
387+ - 如果是容器里面的 systemd,且 `/run/host/unix-export/` 可以写入,那么会 bind `/run/host/unix-export/ssh`,用来让 host 能 ssh 进入容器。
388+ - 如果是虚拟机,会在 `AF_VSOCK` 的 22 端口也 bind 一份。`AF_VSOCK` 是一种 VM 与 host 交互的特殊 socket,相比传统的 `IP:port`,vsock 使用 `CID:port`,其中 CID 是虚拟机管理器设置的上下文 ID,对虚拟机一般从 3 开始。
389+
390+ 这些操作是 [systemd-ssh-generator.8][systemd-ssh-generator.8] 完成的。相关的引入原因可以参考 [Lennart Poettering 的 mastodon](https://mastodon.social/@pid_eins/112411218075942131)。
391+
392+ 需要特别注意的是,**[命名空间机制](./virtualization/container.md#namespace)在 Linux 的 7.0 内核之前,无法隔离 `AF_VSOCK`**。这也就意味着,如果没有设置其他的保护措施,所有虚拟机里开启的容器都可以访问到虚拟机的 SSH,同时因为有虚拟机的主机上也有 vsock 的另一端,因此主机里的容器也可以访问虚拟机的 SSH。如果虚拟机上的用户采用了弱密码,并且 SSH 允许密码登录(或者其他脆弱的场景下),恶意程序就可能会从虚拟机或者主机里的容器逃逸到虚拟机中。不过好消息是,不少容器运行时都使用 seccomp 阻止了容器内部对 vsock 的访问(例如 [moby](https://github.com/moby/moby/pull/44562))。
393+
394+ 可以使用以下命令检查机器上有没有在 vsock 上监听的服务:
395+
396+ ```console
397+ $ ss --vsock --all
398+ Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
399+ v_str LISTEN 0 0 *:22 *:*
400+ ```
401+
402+ 不过从另一方面来说,这也给管理虚拟机带来了方便。Systemd 提供了 [systemd-ssh-proxy.1][systemd-ssh-proxy.1] 工具,用来作为 [`ProxyCommand`](../dev/ssh.md#proxy) 方便连接由 Unix socket 和 `AF_VSOCK` 暴露的 SSH 服务。Debian 的 `systemd` 包会写入 `/etc/ssh/ssh_config.d/20-systemd-ssh-proxy.conf`,允许直接使用 `.host` 或 `machine/.host` 连接到本机的 SSH,使用 `unix/*`、`vsock/*`、`machine/*` 连接到 Unix socket、`AF_VSOCK` 和使用 systemd-machined(systemd 的虚拟机和容器管理器)管理的虚拟机。于是可以这样连接到启用了 vsock 功能的虚拟机上:
403+
404+ ```shell
405+ # 可以 open /dev/vsock 后使用 ioctl IOCTL_VM_SOCKETS_GET_LOCAL_CID 获取虚拟机自己的 CID
406+ # 详情可阅读手册 vsock.7
407+ ssh user@vsock/3
408+ ```
409+
410+ 当然也可以用 `socat` 来做:
411+
412+ ```shell
413+ ssh -o ProxyCommand="socat - VSOCK-CONNECT:3:22" taoky@anythingyoulike
414+ ```
415+
356416### 定时任务 {#timers}
357417
358418Systemd 提供了 timer 类型的 unit,用于定时执行任务。一个 timer unit 通常会对应一个 service unit,即在指定的时间点或者时间间隔触发 service 的启动。
0 commit comments