FortiVPN-7-Podman 开发手记
4 min readMay 21, 2023
strace
是好东西,FortiClient 7 for VPN only 在运行过程中可以看到add_key()
call 添加了user
类型的密钥到 process-scope kernel-managed keyring,参数为KEY_SPEC_PROCESS_KEYRING
,包含了当前登录用户的 VPN 密码,可以通过xxd /proc/keys
检视。strace
显示返回为 EPERM,由于我测试环境没有 SELinux,故而问题只可能在安全增强,使用--security-opt seccomp=unconfined
解决。(Podman 默认的 Seccomp 里并没有显式 Blockadd_key()
,目测是 block 了 underlying function call,Profile 可参考:https://github.com/containers/common/blob/main/pkg/seccomp/seccomp.json)- SSL 类型的VPN在 Linux 上通常都会依赖 tun,因此需要确保内核自动加载 tun 模块并挂载入容器。(现实也确实如此)
expect
(from tcl/tk) 在容器环境下自动输入完成时遇到 stdin 关闭收到 EOF 信号后会自动退出,且后续不会继续打印来自 spawn 的输出,即使你使用interact
或close_on_eof 0
都没用,如果使用expect eof
那就直接退出了。如果你发现expect
匹配的字符串没有正确匹配,可以通过-d
参数 debug,最终添加expect -exact "pattern"
解决。无论保存为 expect script 文件执行还是直接从 stdin 输入脚本都无法完美解决 EOF 退出这个问题。查了资料没找到方案。因此最终换用了 Netflix 的go-expect
自己写了 Daemon。- Systemd 文档中提到了关于允许 systemd 在容器中运行的
$container
环境变量判断,Podman 实现了这一标准,Docker 没有,参考:https://systemd.io/CONTAINER_INTERFACE/ ,因此可以通过这个环境变量拒绝非 Podman 的容器运行镜像。 - FortiClient请求密码时会主动
open("/dev/tty")
,因此需要使用支持 TTY 操作的expect
需求,使用 Golang 的 PTY 库时依然没有捕获到输出,不知道问题在哪里,使用Netflix/go-expect
正常工作,但内部机制依然是 allocate PTY at very beginning. - FortiClient 会在登录成功后尝试使用
mv /etc/resolv.conf /etc/resolv.conf.fortibackup
类似语句备份当前 DNS 配置并在 VPN 断开后还原。但由于 Docker Runtime 在运行时 Hardcode 了 bind-mount 宿主机 /etc/resolv.conf 到容器内部的行为,会导致出现:mv: Device or Resource is busy
的报错失败后导致 VPN 直接退出。经过多方查找无法 override,实现了可 Override 这一特性的目前只发现了 Podman,使用参数--dns=none
后在 Dockerfile 中手动指定一个 /etc/resolv.conf 的有效配置即可。 systemd-networkd
和dhcpcd
类的第三方网络管理工具可能会对虚拟网卡尝试进行自动配置,请务必限制到物理网卡,不需管理虚拟网卡和新网卡。- Podman 和 Docker 通用操作方式,Containerfile 和 Dockerfile 内部的 syntax 共用,所以可以无缝迁移。