Cron环境下Paramiko通过公私钥执行SCP任务失败的排查求助
我之前也踩过Cron和SSH密钥交互的坑,结合你描述的情况(终端运行正常、Cron报错"Not a valid RSA private key file"),给你几个针对性的排查方向:
1. 确认Cron与终端的Python/Paramiko环境一致
很多时候终端用的是虚拟环境的Python,但Cron默认调用系统Python,导致Paramiko版本差异——不同版本对私钥格式的支持可能有区别。你可以:
- 在终端执行
which python3和python3 -c "import paramiko; print(paramiko.__version__)",记下路径和版本号 - 在Cron任务里用绝对路径调用Python,比如把任务写成:
0 * * * * /path/to/your/python3 /path/to/script.py - 同时在脚本开头加上shebang行:
#!/path/to/your/python3,确保执行环境统一
2. 检查私钥文件的权限链(从Home目录到私钥)
SSH类工具(包括Paramiko)对密钥文件的权限要求非常严格,哪怕你指定了绝对路径,只要权限不符合安全标准就会报错。你可以:
- 检查Home目录权限:
ls -ld /home/myuser,必须是drwx------(700)或drwxr-xr-x(755),不能是更宽松的权限(比如777) - 检查
.ssh目录权限:ls -ld /home/myuser/.ssh,必须是drwx------(700) - 检查私钥文件权限:
ls -l /home/myuser/.ssh/id_rsa,必须是-rw-------(600)
Cron运行时的umask可能比终端更严格,即便终端能正常运行,权限不符也会触发Cron环境下的安全校验失败。
3. 模拟Cron的干净环境复现问题
Cron的执行环境比终端精简很多,很多你默认存在的环境变量其实没设置。你可以用env -i模拟Cron环境运行脚本:
env -i HOME=/home/myuser SHELL=/bin/bash PATH=/usr/bin:/bin /path/to/your/python3 /path/to/script.py
如果这样能复现错误,就逐步添加环境变量(比如export PYTHONPATH=/your/virtualenv/lib/python3.x/site-packages),直到找到缺失的关键变量。
4. 在脚本里加调试日志定位差异
在Paramiko调用connect之前,添加几行代码打印关键信息,帮助你排查问题:
import os import stat # 打印当前环境变量 print(f"HOME环境变量: {os.environ.get('HOME')}") print(f"当前工作目录: {os.getcwd()}") # 检查私钥文件的权限和内容开头 key_path = "/home/myuser/.ssh/id_rsa" if os.path.exists(key_path): print(f"私钥文件存在,权限: {oct(stat.S_IMODE(os.stat(key_path).st_mode))}") with open(key_path, "r") as f: print(f"私钥文件开头: {f.readline().strip()}") else: print(f"私钥文件不存在: {key_path}")
然后把Cron任务的输出重定向到日志文件:0 * * * * /path/to/python3 /path/to/script.py >> /tmp/cron_script.log 2>&1,对比终端运行的输出,就能找到Cron环境下的异常点(比如私钥内容读取异常、权限不符)。
5. 尝试禁用SSH配置文件的干扰
有时候~/.ssh/config里的配置(比如IdentityFile、StrictHostKeyChecking)会干扰Paramiko的行为,而Cron环境下读取配置文件的逻辑可能不同。你可以在Paramiko初始化时显式禁用读取配置文件:
ssh = paramiko.SSHClient() ssh.load_system_host_keys() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 禁用读取~/.ssh/config ssh._config = paramiko.SSHConfig()
再用指定的key_filename连接,看是否解决问题。
内容的提问来源于stack exchange,提问作者f.gallardo

