另一种轻量的 Linux VPS 共享方案
内容提要
本文介绍一种轻量Linux VPS共享方案:利用libpam-net为每个用户创建独立网络命名空间,通过veth虚拟网卡和IPv6链路本地地址实现隔离。配置TinyProxy正向代理供用户访问外网,用Caddy反向代理将域名请求转发至用户服务。同时加强系统安全,限制dmesg和进程查看,并推荐用户级systemd管理服务。
延伸解读
网络隔离与代理结合的设计思路
本文方案的核心在于利用IPv6链路本地地址和代理服务实现用户隔离,而非为每个用户分配公网IP。通过libpam-net创建独立网络命名空间,再以veth虚拟网卡连接用户空间与宿主机,用户仅需通过链路本地地址访问代理即可上网。这种设计简化了网络配置,同时保持了用户间的隔离性,适合轻量级共享场景。
编译与部署注意事项
部署时需注意libpam-net在7.0内核下的编译问题,需链接libpam库,并针对不同CPU架构(如aarch64)指定正确的库路径。此外,veth脚本需在PAM会话开启时执行,且要确保用户属于指定组,否则不会创建网络空间。这些细节对成功部署至关重要。
代理配置的实用细节
正向代理使用TinyProxy,仅允许链路本地地址访问,用户需通过转义后的地址(如http://[fe80::1%25eth0]:8888)使用代理。反向代理选用Caddy而非Nginx,因为Nginx不支持在upstream中使用IPv6链路本地地址。Caddy通过正则提取子域名并转发至对应用户的8080端口,实现外部访问。
系统安全加固措施
多用户共享系统需加强安全:禁用SSH密码登录、限制dmesg查看(kernel.dmesg_restrict=1)、通过hidepid=invisible隐藏其他用户进程。同时,home目录权限设为750,并建议使用磁盘配额和systemd slice限制资源。这些措施可降低用户间干扰和潜在风险。
Q&A
libpam-net 在 7.0 内核上编译时出现 undefined symbol: pam_get_user 错误,如何解决?
该错误是因为编译时未链接 libpam 库。需要确保编译时链接 libpam,或者参考项目 PR 中的修复。
在 Oracle ARM 服务器上编译 libpam-net 时,如何指定安全模块目录?
在 cmake 时使用 -DLIBSECURITYDIR=/lib/aarch64-linux-gnu/security 指定目录。
如何为 zz 组的用户创建独立的网络命名空间?
在 /etc/pam.d/common-account 中添加两行:session optional /usr/local/lib/security/pam_usernet.so group=zz 和 session optional pam_exec.so stdout /usr/local/sbin/veth.sh。第一行创建独立网络空间,第二行执行 veth.sh 脚本配置 veth 设备。
veth.sh 脚本中,为什么将 ve-$USER 设置到 netns 1?
因为根网络空间没有名字,只能通过 ID=1 来指定。将 ve-$USER 放到根空间,eth0 留在用户空间,这样可以通过设备名区分流量来源。
用户如何通过代理访问外部网络?
用户通过正向代理 TinyProxy 访问外网,代理地址为 http://[fe80::1%25eth0]:8888。系统还会为 zz 组成员自动设置 http_proxy 和 https_proxy 环境变量。
为什么选择 Caddy 而不是 Nginx 作为反向代理?
因为 Nginx 不支持在 upstream 中使用 IPv6 链路本地地址,而 Caddy 支持,所以选用 Caddy。
如何让外部网络访问用户服务?
通过 Caddy 反向代理,根据域名将请求转发到用户的 IPv6 链路本地地址的 8080 端口。例如,域名 example.zz.ac 的请求会被转发到 [fe80::2%ve-example]:8080。
如何让用户服务开机自启?
使用 loginctl enable-linger 命令开启用户服务的 linger 功能,然后使用用户级 systemd 管理服务。
如何禁止普通用户查看 dmesg 信息?
执行 sysctl -w kernel.dmesg_restrict = 1 命令。
如何禁止用户查看其他用户的进程?
修改 /proc 挂载参数,添加 hidepid=invisible 选项。例如:proc /proc proc rw,nosuid,nodev,noexec,relatime,hidepid=invisible 0 0。