简介
在前面的一些文章中,我们用到了 AWS Cli。比如为 ECS Fargate 建 Task 和 Service。
处于安全原因,很多公司都是通过 http 代理访问 internet,这时使用 AWS Cli 操作 AWS 时,可能会碰到“SSL validation failed”错误。
比如运行如下命令
aws sts get-caller-identity
会产生如下报错
SSL validation failed for https://sts.cn-north-1.amazonaws.com.cn/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
图 1
这时可以用“--no-verify-ssl”参数解决
aws sts get-caller-identity --no-verify-ssl
但这种方式不但降低了安全性,而且结果总会带有一长串恼人的告警
urllib3/connectionpool.py:1013: InsecureRequestWarning: Unverified HTTPS request is being made to host 'pitc-zscaler-americas-cincinnati3pr.proxy.corporate.ge.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
图 2
本文分析产生这种问题的原因,并给出解决方法,使得通过 http 代理使用 AWS CLi 时不再需要“--no-verify-ssl”参数。
AWS CLi 用的是 Python,所以此方法也适用于单纯的 Python 报此错误
本文主要以 AWS Cli version 2 为例说明,最后会给出 AWS Cli version 1 的解决方法。
目录
- 环境(配置)
- AWS Cli 简介
- 实战步骤
- 错误分析
- 解决方法
- AWS Cli version 1
- 安装 AWS Cli
- 测试报错
- 报错重现
- 错误分析与解决方法
- 总结
- 引申
- 后记
环境(配置)
- AWS 中国或 Global 帐号,可在官网申请,一年内使用指定资源免费
- AWS cli version 2,Win10 + terminal
AWS Cli 简介
AWS Command Line Interface(AWS CLi)是 AWS 开源工具,允许用户以命令行的形式对 AWS 资源进行操作。
AWS CLi 可以实现 AWS 网页控制台一样的功能,而且有些 AWS 特性只能通过 AWS Cli 实现。
AWS Cli 虽然使用起来没在网页上操作那么直观,但更简洁高效,生产中用 AWS Cli 其实也更多一些。
另外在与 Jenkins 做 CICD 集成时,除了用 aws 插件,使用 AWS CLi 也是经常使用的方式。
AWS Cli 支持两种系统
- Linux shells Linux 或者 macOS
- Windows command line cmd 或者 PowerShell
AWS Cli 版本
- 2.x 当前主流版本,与 python 集成,不需要单独安装 python
- 1.x 老版本,需独立安装 python
实战步骤
1. 报错重现
首先,我们在 linux 环境下安装 AWS Cli 2
安装 AWS Cli 2
运行以下命令安装
#下载安装包
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.0.30.zip" -o "awscliv2.zip"
#解压到本地aws目录
unzip awscliv2.zip
#用root用户安装
sudo ./aws/install
安装完成后,可以用如下命令测试
aws --version
图 3
测试报错
在公司的内网中,没加公司 http 代理,运行命令
export AWS_PROFILE=YOUR_CONFIGURED_IAM_USER
aws sts get-caller-identity
说明:此命令用来返回当前用户的信息,使用 aws configure 配置后即可使用。
报错
Could not connect to the endpoint URL: "https://sts.cn-north-1.amazonaws.com.cn/"
无法连到https://sts.cn-north-1.amazonaws.com.cn/
图 4
在公司的内网中,加上公司代理,运行命令失败,报错
export AWS_PROFILE=YOUR_CONFIGURED_IAM_USER
export http_proxy=YOUR_HTTP_PROXY_NAME:80
export https_proxy=YOUR_HTTP_PROXY_NAME:80
aws sts get-caller-identity
SSL validation failed for https://sts.cn-north-1.amazonaws.com.cn/ [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
图 5
在公司的内网中,加上公司代理,加上“--no-verify-ssl”参数,运行命令成功,但是有告警
export AWS_PROFILE=YOUR_CONFIGURED_IAM_USER
export http_proxy=YOUR_HTTP_PROXY_NAME:80
export https_proxy=YOUR_HTTP_PROXY_NAME:80
aws sts get-caller-identity --no-verify-ssl
图 2
如果我们在相同的环境下,直接连接 internet(比如,我用的手提断开公司连接,直接连 wifi)运行命令,成功
图 6
2. 错误分析与解决方法
错误分析
我们在公司内网中,使用代理,再一次运行相同的命令,这次加上“--debug”参数
aws sts get-caller-identity --debug
加上 debug 参数后,输出信息非常长,这里只截取了其中一段
MainThread - botocore.httpsession - DEBUG - Certificate path: /usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem
图 7
输出信息显示 AWS Cli 从“/usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem”文件中查找网站证书。
结合测试中的报错信息“unable to get local issuer certificate”
我们可以推断出,“/usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem”中缺少必要的证书。
这个证书是什么?
从上述测试可以看到,如果连接 internet 时使用了公司代理,则或者报错,或者告警;而不使用代理直接连接 internet 则完全没有问题。
所以客户端(AWS Cli)和服务器端(sts.cn-north-1.amazonaws.com.cn)都没有问题,问题出在使用的 http 代理服务器上。
也就是说 cacert.pem 缺少的应该是代理服务器的证书。
解决方法
代理服务器的证书,一般需要向公司网络管理员获取。(我所在的环境中,公司提供了下载此证书的网站,所以可以自行下载)
用本文编辑打开公司证书,把内容复制到“/usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem”的最后
公司证书内容如下
-----BEGIN CERTIFICATE-----
MIIDozCCAougAwIBAgIQeO8XlqAMLhxvtCap35yktzANBgkqhkiG9w0BAQsFADBS
....
5ad/qyN+Zgbjx8vEWlywmhXb78Gaf/AwSGAwQPtmQ0310a4DulGxo/kcuS78vFH1
mwJmHm9AIFoqBi8XpuhGmQ0nvymurEk=
-----END CERTIFICATE-----
可用 vi 命令,复制到 cacert.pem 的最后
sudo vi /usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem
保存后,我们重新在公司内网加代理的环境下测试
export AWS_PROFILE=YOUR_CONFIGURED_IAM_USER
export http_proxy=YOUR_HTTP_PROXY_NAME:80
export https_proxy=YOUR_HTTP_PROXY_NAME:80
aws sts get-caller-identity
这时运行成功,和直接连接 internet 的情况一样了
图 6
说明我们的猜测是正确的,由于本地环境中缺少 http 代理服务器的证书,导致 AWS Cli 报 SSL: CERTIFICATE_VERIFY_FAILED 错误。
AWS Cli version 1
上述分析和解决是基于 AWS Cli 2,AWS Cli 1 的处理方法略有不同。
在 AWS CLi 1 的环境下运行 debug 时,输出的的信息中并没有提供类似“Certificate path: /usr/local/aws-cli/v2/2.1.30/dist/botocore/cacert.pem”这样的信息。
AWS CLi 1 与 AWS CLi 2 不同,需要单独安装 python,所以我们可以通过 python 找到证书路径。
首先,确定 AWS Cli 使用的 python 版本
aws --version
图 8
然后运行如下 python 命令,查看 request 使用的证书路径
python
import requests as r
print(r.certs.where())
图 9
最后,像上面一样把公司代理服务器的证书,添加到此路径证书的最后即可。
总结
AWS Cli 报 SSL: CERTIFICATE_VERIFY_FAILED 错误,是调用 Python 代码报的错 python 包 urllib3 传递 https 请求时,会要求网站证书或其根证书。
当通过公司代理服务器时访问外网时,urllib3 也会要求代理服务器的证书。
但 python 安装时(如果安装了 request 包或者 certifi 包),只会包含大型证书服务商提供的根证书,并不会包含公司内部代理服务器的证书。所以会报找不到证书的错误。
这时我们要么选择用“--no-verify-ssl”,要么手工导入代理服务器证书。
在 Python 官网上 request 一段可以查到如下内容
Finally, note that using a proxy for https connections typically requires your local machine to trust the proxy’s root certificate.
引申
如果是自己部署的 http 代理服务器,也可以用下列 openssl 命令产生代理服务器的自签名证书,然后复制 example.crt 中的内容。
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
-keyout example.key -out example.crt -subj "/CN=YOUR_PROXY_NAME.com"
AWS CLi 这个错是 python 的错,搞清楚了这个问题,在 python 代码中遇到同样的错也可以用相同的方法解决。
网上也有说用如下命令,解决类似问题
pip install certifi
但 Certifi 里面是 Mozilla 提供的一组根证书,只可以解决连接大部分网站的证书问题。
如果是公司内网的代理服务器,基本上是不可能通过这种方法解决的,只能通过手工导入解决。
资源下载
这篇文章讲的很详细,很有启发https://lukasa.co.uk/2013/07/Python_Requests_And_Proxies/
这个讨论挺有帮助https://stackoverflow.com/questions/22027418/openssl-python-requests-error-certificate-verify-failed
Python 官网上那段可在下列链接中找到https://docs.python-requests.org/en/master/user/advanced/
后记
之前也尝试解决这个问题,可惜一直没成功。主要还是不明白其中的原理,只是把网上 X 度搜到的方法机械的试一遍。
这次查了不少资料,连蒙带试,终于把 SSL: CERTIFICATE_VERIFY_FAILED 这个老问题解决了。
把详细分析和解决过程记下来,省得以后忘了,另外也分享给有需要的人。
喜欢请点赞,欢迎转发
微信公众号“全是 AWS 干货”