创建无登录shell的系统用户应设shell为/usr/sbin/nologin或/bin/false,配合PAM拦截、SSH限制及进程启动时UID绑定,三者缺一不可。
Linux 系统下创建无登录 shell 的系统用户
隐含用户本质是不被允许交互式登录的账号,核心是禁用其 shell 而非隐藏用户名。直接设为 /usr/sbin/nologin 或 /bin/false 是最稳妥的做法,别用空 shell 或注释掉 /etc/passwd 字段——后者会导致部分 pam 模块拒绝认证,反而让 proxy 连接失败。
-
/usr/sbin/nologin会返回友好提示,适合调试;/bin/false更轻量,适合生产环境 - 创建命令示例:
sudo useradd -r -s /usr/sbin/nologin -d /var/empty -M appproxy_user(-r表示系统用户,-M确保不创建 home 目录) - 切勿用
usermod -s "" username清空 shell 字段——glibc 会将其视为无效登录 shell,SSH/PAM 直接拒绝,Proxy 服务可能拿不到有效 UID 上下文
应用层 Proxy 如何正确使用该用户身份
Proxy 进程本身需以该用户运行,但不能靠 su/sudo 在启动脚本里临时切换——权限降级不彻底,残留父进程能力。必须从启动源头就指定 UID/GID。
- Systemd 服务中用
User=appproxy_user和Group=appproxy_user,并确保NoNewPrivileges=yes - Nginx 的
user指令不适用此场景:它只控制 worker 进程,无法约束 upstream 或 auth_request 模块发起的后端调用身份 - 若 Proxy 是 Python/Node.js 编写的,启动前务必调用
os.setuid()和os.setgid()(且在 drop privileges 前关闭所有不需要的文件描述符)
验证账号是否真被“隐含”且仅限 Proxy 使用
关键不是看 getent passwd 是否显示,而是检查它能否被绕过 Proxy 直接利用。很多团队漏掉 PAM 和 SSH 层的兜底限制。
- 确认
/etc/ssh/sshd_config中有DenyUsers appproxy_user(或用AllowGroups白名单机制) - 检查 PAM 配置:在
/etc/pam.d/common-auth或对应 service 文件中加入auth [success=ok default=bad] pam_succeed_if.so user != appproxy_user,防止其他服务(如 cron、at)意外触发认证流程 - 运行
sudo -u appproxy_user bash -c 'echo test'应失败;但sudo -u appproxy_user curl http://localhost:8080(假设 Proxy 正在监听)应成功——说明权限隔离生效
为什么不能依赖 /etc/shadow 锁定或密码设为空
设空密码或加锁(passwd -l)只防密码登录,对 key-based SSH、PAM 认证、本地进程调用完全无效。更危险的是,某些 Proxy 实现(如 Squid 的 external_acl_type)会主动调用 getpwnam() 并执行用户 shell 初始化逻辑,若 shell 可执行,就可能被注入。
-
passwd -l只是在密码字段前加!,不影响su -s /bin/bash -c 'whoami' appproxy_user这类无密码切换 - 真正起作用的是 shell 路径不可执行 + PAM 拦截 + 进程启动时 UID 绑定三者叠加
- 额外提醒:Docker 容器内若复用该用户,需确保镜像基础层的
/usr/sbin/nologin存在,Alpine 默认不含此路径,得改用/bin/false
nologin 路径缺失导致降权失效。










