Python虚拟环境SMTP证书验证失败及Cron认证异常求助
问题分析与解决:Python SMTP邮件发送在Cron中认证失败
问题梳理
- 本地运行Python 3.10的SMTP代码连接Office365正常
- 公司Ubuntu服务器手动运行时,因证书问题报错
unable to get local issuer certificate,改用ssl._create_unverified_context()后手动运行恢复正常 - 但通过Cron执行同脚本时,抛出
Authentication unsuccessful, the user credentials were incorrect.,手动运行凭据可正常使用
可能原因与解决方法
1. Cron环境变量缺失
Cron运行时的环境变量与用户手动登录环境不同,os.environ.get("EMAIL")和os.environ.get("EMAIL_PASSWORD")可能无法正确读取到凭据值,导致认证失败。
解决方法:
- 直接在Cron任务中指定环境变量:
* * * * * EMAIL=your_office365_email@domain.com EMAIL_PASSWORD=your_app_password /path/to/venv/bin/python /path/to/script.py >> /path/to/cron_log.log 2>&1 - 或在脚本开头加载用户的环境配置文件(以
bashrc为例):import os # 加载用户环境变量,路径根据实际用户名调整 with open('/home/your_username/.bashrc') as f: for line in f: if '=' in line and not line.startswith('#'): key, val = line.strip().split('=', 1) os.environ[key] = val
2. 虚拟环境路径未正确指定
Cron可能默认使用系统Python,而非你创建的虚拟环境中的Python版本,导致依赖或配置不匹配。
解决方法:
- 在Cron任务中使用虚拟环境Python的绝对路径,而非仅写
python:* * * * * /home/your_username/venv/bin/python /path/to/script.py >> /path/to/cron_log.log 2>&1 - 或先激活虚拟环境再执行脚本:
* * * * * source /home/your_username/venv/bin/activate && python /path/to/script.py >> /path/to/cron_log.log 2>&1
3. 网络代理/防火墙限制
Cron运行时的网络环境可能未配置公司代理,导致无法正常连接Office365 SMTP服务器,进而触发凭据错误的误导性提示。
解决方法:
- 在脚本中添加代理配置(根据公司实际代理地址调整):
import smtplib import ssl import os smtp_server = "smtp.office365.com" port = 587 sender_email = os.environ.get("EMAIL") password = os.environ.get("EMAIL_PASSWORD") context = ssl._create_unverified_context() server = smtplib.SMTP(smtp_server, port) # 设置公司代理 server.set_proxy("your_proxy_host", port=your_proxy_port) try: server.starttls(context=context) server.login(sender_email, password) # 后续发送邮件逻辑 except Exception as e: print(e) finally: server.quit() - 或在Cron任务中直接设置代理环境变量:
* * * * * HTTP_PROXY=http://proxy_host:proxy_port HTTPS_PROXY=http://proxy_host:proxy_port /path/to/venv/bin/python /path/to/script.py >> /path/to/cron_log.log 2>&1
4. 修复证书验证问题(替代未验证上下文)
使用ssl._create_unverified_context()存在安全风险,建议正确配置证书:
- 下载DigiCert Global Root CA证书(Office365使用的根证书),保存到服务器本地路径
- 在脚本中指定证书文件:
context = ssl.create_default_context(cafile='/path/to/DigiCertGlobalRootCA.crt.pem')
调试建议
- 在脚本中添加日志打印(调试后删除敏感信息),确认Cron运行时是否获取到正确的凭据:
print(f"Sender Email: {sender_email}") print(f"Password exists: {bool(password)}") - 查看Cron日志输出,通过
tail -f /path/to/cron_log.log获取完整错误信息 - 模拟Cron的干净环境运行脚本,排查环境差异:
env -i /bin/bash -c "/path/to/venv/bin/python /path/to/script.py"
内容的提问来源于stack exchange,提问作者Fresh
相关产品推荐
相关产品推荐

