在 lambda 上使用 M2Crypto 的问题(适用于 EC2)

Issue using M2Crypto on lambda (works on EC2)

我正在尝试在 AWS Lambda 中使用 M2Crypto 安装 python 函数。

我使用 Lambda AMI image 启动了一个 EC2 实例,将 M2Crypto 安装到 virtualenv 中,并且能够让我的函数在 EC2 上运行。

然后我压缩了站点包并上传到 Lambda。我收到这个错误

Unable to import module 'epd_M2Crypto': /var/task/M2Crypto/_m2crypto.cpython-36m-x86_64-linux-gnu.so: symbol sk_deep_copy, version libcrypto.so.10 not defined in file libcrypto.so.10 with link time reference

还有类似的问题和提示 and 。我尝试在 zip 文件中上传有问题的库 (libcrypto.so.10),但仍然出现相同的错误。我假设错误意味着 libcrypto.so.10 的 EC2 版本(用于安装 M2Crypto)与 Lambda 上的版本(我试图 运行 不同),所以 M2Crypto 抱怨。

如果我查看 openssl 的版本,它们是不同的:

我不认为答案是在 ec2 上降级 openssl,因为 1.0.0 版本已经过时(AWS 应用了安全补丁,但版本仍然显示为 1.0.0)。 (而且 yum 没有这么旧的版本)

以下是我在 EC2 实例上使用的使其在 EC2 上运行的步骤:

$ sudo yum -y update
$ sudo yum -y install python36
$ sudo yum -y install python-virtualenv
$ sudo yum -y groupinstall "Development Tools"
$ sudo yum -y install python36-devel.x86_64
$ sudo yum -y install openssl-devel.x86_64

$ mkdir ~/forlambda
$ cd ~/forlambda
$ virtualenv -p python3 venv
$ source venv/bin/activate

$ cd ~
$ pip install M2Crypto -t ~/forlambda/venv/lib/python3.6/site-packages/

$ cd ~/forlambda/venv/lib/python3.6/site-packages/
$ (create python function that uses M2Crypto)
$ zip -r9 ~/forlambda/archive.zip .

然后添加到zip文件

并上传到 Lambda,这是我现在卡住的地方。

我是否需要执行某些操作才能让 Lambda 使用我在上传的 zip 中包含的 libcrypto.so.10 版本?

我的函数:

"""
Wrapper for M2Crypto
https://github.com/mcepl/M2Crypto
https://pypi.org/project/M2Crypto/
"""

from __future__ import print_function
from M2Crypto import RSA
import base64
import json

def decrypt_string(string_b64):
    rsa = RSA.load_key('private_key.pem')
    string_encrypted = base64.b64decode(string_b64)
    bytes = rsa.private_decrypt(string_encrypted, 1)
    string_plaintext = bytes.decode("utf-8")

    response = {
        's': string_plaintext,
        'status': "OK",
        'statuscode': 200
    };
    return response


def lambda_handler(event, context):

    response = ""
    action = event['action']

    if action == "decrypt":
        string_b64 = event['s']
        response = decrypt_string(string_b64)

    return response

首先,我 运行 在 EC2 实例上执行此命令以确保我在我的 .zip 中包含了正确的 .so 文件:

$ ldd -v _m2crypto.cpython-36m-x86_64-linux-gnu.so 

ldd 命令的输出(为简洁起见进行了编辑):

    libssl.so.10 => /lib64/libssl.so.10 (0x00007fd5f1892000)
    libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007fd5f1433000)

根据上面的输出,我在我的 .zip 中包含了 /lib64/libcrypto.so.10。

另外(根据 AWS Support 的建议),在 Lambda 控制台的 'Environment variables' 下,我添加了一个键 'LD_LIBRARY_PATH',值为“/var/task”。

我不确定我是否需要这两项更改来解决我的问题,但它现在可以正常工作,经过三天的故障排除后,我不敢触摸它,看看是不是其中一个导致了它工作。

这可能太残酷了,但是可以使用 LD_PRELOAD 强制使用您喜欢的 OpenSSL 库版本吗?

AWS 支持提供了解决方案,升级到使用 Python 3.7,问题已解决:

Our internal team has confirmed that the issue is with Lambda's Python runtime. In a few rare cases, when the Lambda function is being initialised, Lambda is unable to link against the correct OpenSSL libraries - instead linking against Lambda's own in-built OpenSSL binaries.

The team suggests trying this out in the Python3.7 environment where this behaviour has been fixed. Also, python3.7 is compiled with the newer openssl 1.0.2 and you should not have to include the binaries in the Lambda package. ... still had to include the OpenSSL binaries in the package and could not get it working with the default libraries.

AWS lambda 运行s 旧版本亚马逊 linux (amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2) 的代码,如官方文档中所述 https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html

所以运行一段依赖共享库的代码,需要在相同的环境下编译才能link正确

在这种情况下我通常做的是使用 docker 容器创建 virtualenv。 virtualenv 可以用 lambda 代码打包。

请注意,如果您需要使用 yum 安装任何东西(在 docker 容器中),您必须使用与亚马逊 linux 版本相同的发布服务器:

yum --releasever=2017.03 install ...

virtualenv 也可以使用 EC2 实例而不是 docker 容器来构建(不过,我发现 docker 方法更容易)。只需确保用于 EC2 的 AMI 与 lambda 使用的相同。