在前面的一些文章中,我们用到了 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)
这时可以用“–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
本文分析产生这种问题的原因,并给出解决方法,使得通过 http 代理使用 AWS CLi 时不再需要“–no-verify-ssl”参数。
AWS CLi 用的是 Python,所以此方法也适用于单纯的 Python 报此错误
本文主要以 AWS Cli version 2 为例说明,最后会给出 AWS Cli version 1 的解决方法。
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 支持两种系统
AWS Cli 版本
首先,我们在 linux 环境下安装 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
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/
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)
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
我们在公司内网中,使用代理,再一次运行相同的命令,这次加上“–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
输出信息显示 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 的情况一样了
说明我们的猜测是正确的,由于本地环境中缺少 http 代理服务器的证书,导致 AWS Cli 报 SSL: CERTIFICATE_VERIFY_FAILED 错误。
上述分析和解决是基于 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
然后运行如下 python 命令,查看 request 使用的证书路径
python
import requests as r
print(r.certs.where())
最后,像上面一样把公司代理服务器的证书,添加到此路径证书的最后即可。
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 干货”