OpenVPN auth-user-pass-verify执行外部程序失败求助
解决OpenVPN auth-user-pass-verify无法执行外部脚本的问题
一、先手动验证脚本的可执行性
- 切换到nobody用户执行脚本,模拟OpenVPN的运行环境,直接定位执行报错:
提前创建su -s /bin/bash nobody -c "/etc/openvpn/auth2.sh /tmp/test_auth_file"/tmp/test_auth_file,第一行写测试用户名,第二行写测试密码。执行后观察是否有找不到命令、权限不足等明确报错。 - 检查脚本shebang路径是否匹配系统环境:
- Bash脚本:执行
which bash,若输出为/bin/bash,将脚本开头的#!/usr/bin/bash改为#!/bin/bash; - Python脚本:执行
which python3,确认路径与脚本中#!/usr/bin/python3一致,不一致则修改shebang。
- Bash脚本:执行
二、修复文件与目录权限
- 确保脚本所在目录对nobody有访问权限:
chmod o+rx /etc/openvpn - Python脚本涉及的数据库文件与目录需开放读权限:
# 开放db目录的访问权限 chmod o+rx /etc/openvpn/db # 开放数据库文件的读权限 chmod o+r /etc/openvpn/db/openvpn-ui.db
三、排查安全模块限制(SELinux/AppArmor)
- SELinux排查:
临时关闭SELinux测试:
重启OpenVPN服务后若错误消失,说明是SELinux阻止了脚本执行,需添加允许规则:setenforce 0
规则加载完成后恢复SELinux:# 基于审计日志生成规则模块 audit2allow -a -M openvpn_script # 加载规则 semodule -i openvpn_script.ppsetenforce 1 - AppArmor排查:
打开OpenVPN的AppArmor配置文件(通常为/etc/apparmor.d/usr.sbin.openvpn),添加允许执行脚本的规则:
重新加载AppArmor生效:/etc/openvpn/*.sh rPx, /usr/bin/python3 mrPx,systemctl reload apparmor
四、检查OpenVPN配置细节
- 确认配置中未启用
chroot选项:若启用chroot,脚本及所有依赖(bash、python3、curl等)必须放在chroot目录内,否则OpenVPN无法找到。 - 检查临时文件目录权限:
via-file模式下OpenVPN会在临时目录创建文件,默认/tmp需对nobody开放读写权限;也可在openvpn.conf中指定自定义临时目录:
然后创建目录并设置权限:tmp-dir /etc/openvpn/tmpmkdir /etc/openvpn/tmp chown nobody:nobody /etc/openvpn/tmp chmod 700 /etc/openvpn/tmp
五、修正脚本本身的逻辑错误
Bash脚本修正:
- 修复变量赋值方式、HTTP请求规范及JSON格式错误:
#!/bin/bash PATH=$PATH:/usr/local/bin auth_usr=$(head -1 "$1") auth_passwd=$(tail -1 "$1") if [ "$common_name" = "$auth_usr" ]; then # 改用POST请求发送合法JSON格式参数 result=$(/usr/bin/curl -s -X POST -H "Content-type: application/json" -d '{"username":"'"${auth_usr}"'","password":"'"${auth_passwd}"'"}' http://openvpn-ui:8080/auth) echo "$result" if [ "$result" = "Authorized" ]; then echo "Authorization succeeded" exit 0 else echo "Authorization failed" exit 1 fi else echo "Authorization failed" exit 1 fi
Python脚本优化:
- 添加异常捕获,处理数据库连接失败等异常情况:
#!/usr/bin/python3 import sys import sqlite3 DB_FILE = '/etc/openvpn/db/openvpn-ui.db' def main(): try: with open(sys.argv[1], 'r') as tmpfile: username = tmpfile.readline().rstrip('\n') password = tmpfile.readline().rstrip('\n') creds = get_password(username) if not creds: print(f'>> user {username} not defined.') sys.exit(1) if len(creds[0]) < 2 or password != creds[0][1]: print(f'>> Incorrect password provided by user {username}.') sys.exit(1) sys.exit(0) except Exception as e: print(f'>> Authentication error: {str(e)}') sys.exit(1) def get_password(username): try: db = sqlite3.connect(DB_FILE) cursor = db.cursor() cursor.execute('''select username, password from user where username=?''', (username, )) creds = cursor.fetchall() db.close() return creds except sqlite3.Error as e: print(f'>> Database error: {str(e)}') return [] if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者DISCO
相关产品推荐
相关产品推荐

