FortiVPN-7-Podman 开发手记

Patrick Young
4 min readMay 21, 2023

--

  1. 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 里并没有显式 Block add_key(),目测是 block 了 underlying function call,Profile 可参考:https://github.com/containers/common/blob/main/pkg/seccomp/seccomp.json
  2. SSL 类型的VPN在 Linux 上通常都会依赖 tun,因此需要确保内核自动加载 tun 模块并挂载入容器。(现实也确实如此)
  3. expect (from tcl/tk) 在容器环境下自动输入完成时遇到 stdin 关闭收到 EOF 信号后会自动退出,且后续不会继续打印来自 spawn 的输出,即使你使用 interactclose_on_eof 0都没用,如果使用expect eof那就直接退出了。如果你发现 expect 匹配的字符串没有正确匹配,可以通过 -d 参数 debug,最终添加 expect -exact "pattern"解决。无论保存为 expect script 文件执行还是直接从 stdin 输入脚本都无法完美解决 EOF 退出这个问题。查了资料没找到方案。因此最终换用了 Netflix 的 go-expect 自己写了 Daemon。
  4. Systemd 文档中提到了关于允许 systemd 在容器中运行的 $container 环境变量判断,Podman 实现了这一标准,Docker 没有,参考:https://systemd.io/CONTAINER_INTERFACE/ ,因此可以通过这个环境变量拒绝非 Podman 的容器运行镜像。
  5. FortiClient请求密码时会主动 open("/dev/tty"),因此需要使用支持 TTY 操作的 expect 需求,使用 Golang 的 PTY 库时依然没有捕获到输出,不知道问题在哪里,使用 Netflix/go-expect 正常工作,但内部机制依然是 allocate PTY at very beginning.
  6. 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 的有效配置即可。
  7. systemd-networkddhcpcd 类的第三方网络管理工具可能会对虚拟网卡尝试进行自动配置,请务必限制到物理网卡,不需管理虚拟网卡和新网卡。
  8. Podman 和 Docker 通用操作方式,Containerfile 和 Dockerfile 内部的 syntax 共用,所以可以无缝迁移。

--

--

No responses yet