OpenSSH:实现不存在的用户登录并向pam_exec传递真实密码
OpenSSH:实现不存在的用户登录并向pam_exec传递真实密码
我之前碰到过一模一样的场景,折腾了好一阵子才弄明白问题出在哪——当本地没有这个用户时,OpenSSH会默认跳过真实密码的传递流程,直接给PAM模块发一个无效的认证请求,导致你的pam_exec根本拿不到用户输入的真密码。下面是我亲测有效的解决步骤:
1. 调整OpenSSH配置,允许不存在的用户进入PAM流程
首先得让sshd别提前把不存在的用户挡在门外,而是把认证权交给PAM处理。编辑/etc/ssh/sshd_config,确保以下配置项正确:
UsePAM yes PasswordAuthentication yes # 关闭键盘交互认证,避免干扰密码传递流程 KbdInteractiveAuthentication no # 允许所有用户尝试登录(不要限制特定用户) AllowUsers * # 可选:给新创建的用户指定默认shell,比如bash # DefaultShell /bin/bash
修改完后重启sshd服务生效:
systemctl restart sshd
2. 修改PAM配置,让pam_exec优先获取真实密码
核心问题出在PAM的执行顺序上——默认情况下,PAM会先调用pam_unix.so检查本地用户,一旦用户不存在,这个模块直接返回失败,后面的pam_exec连拿到真实密码的机会都没有。我们要把pam_exec放在最前面,并且用关键参数确保它能拿到真实密码。
编辑PAM的sshd配置文件/etc/pam.d/sshd,调整认证模块的顺序:
# 先运行自定义的认证脚本,expose_authtok是关键!让脚本从标准输入读真实密码 # success=1表示脚本认证成功的话,跳过下1个模块(也就是后面的pam_unix) # default=ignore表示脚本失败时,忽略结果继续执行后面的模块 auth [success=1 default=ignore] pam_exec.so expose_authtok /path/to/your/auth_script.sh # 再调用系统默认的远程登录认证模块(包含pam_unix) auth include system-remote-login
这里的expose_authtok是重中之重,它强制pam_exec从标准输入读取用户输入的真实密码,而不是用OpenSSH生成的假密码。
3. 编写你的认证脚本,处理验证和用户创建
接下来写pam_exec调用的脚本,它要完成三件事:拿真实密码、调用webservice验证、验证通过则创建本地用户。
示例脚本/path/to/your/auth_script.sh:
#!/bin/bash # 从PAM环境变量获取用户名 USER="$PAM_USER" # 读取真实密码(因为用了expose_authtok,密码会从标准输入传入) read -r PASSWORD # 可选:添加日志方便排查,日志文件要确保pam_exec有权限写入 echo "$(date '+%Y-%m-%d %H:%M:%S'): 尝试认证用户 $USER" >> /var/log/pam_auth.log # 调用你的外部webservice验证用户名和密码,这里用curl举例,根据实际情况调整 AUTH_RESPONSE=$(curl -s -X POST \ -d "username=$USER&password=$PASSWORD" \ https://your-webservice-domain/auth-endpoint) # 假设webservice返回"ok"表示验证通过 if [ "$AUTH_RESPONSE" = "ok" ]; then # 检查本地用户是否存在 if ! id -u "$USER" >/dev/null 2>&1; then # 创建用户,-m表示自动创建home目录,-s指定默认shell useradd -m -s /bin/bash "$USER" echo "$(date '+%Y-%m-%d %H:%M:%S'): 为用户 $USER 创建本地账号" >> /var/log/pam_auth.log fi # 返回0告诉PAM认证成功 exit 0 else echo "$(date '+%Y-%m-%d %H:%M:%S'): 用户 $USER 认证失败,webservice返回: $AUTH_RESPONSE" >> /var/log/pam_auth.log # 返回非0表示认证失败 exit 1 fi
给脚本添加执行权限:
chmod +x /path/to/your/auth_script.sh
注意事项
- pam_exec是以root身份运行的,所以
useradd这类命令不需要sudo,直接执行就行 - 要确保脚本里的curl(或其他调用webservice的工具)能正常访问目标服务,注意防火墙、代理、SSL证书等问题
- 日志文件要提前创建并设置好权限,比如
touch /var/log/pam_auth.log && chmod 600 /var/log/pam_auth.log,避免脚本写入失败 - 如果webservice有超时设置,最好给curl加上
--timeout参数,避免PAM流程卡住
这样设置之后,当不存在的用户尝试SSH登录时,sshd会把请求交给PAM,pam_exec先拿到真实密码去webservice验证,验证通过后自动创建本地用户,最后完成登录流程。
备注:内容来源于stack exchange,提问作者Joel Guzmán
相关产品推荐
相关产品推荐

