AWS LambdaでOpenSSLを使う方法

2020.04.01

AWS Lambdaの最新のランタイムではAmazon Linux 2が使われていて、OpenSSLパッケージを見つける事ができません。このような環境でOpenSSLコマンドを使う方法を説明します。

OpenSSLコマンドの確認

ランタイムがPython3.8(Amazon Linux 2)のLambda関数でOpenSSLコマンドを実行してみました。

lambda_function.py

import subprocess

def lambda_handler(event, context):
  output = subprocess.run(['openssl', 'version'], stdout=subprocess.PIPE)

  print(output.stdout.decode())

コンソールにコマンドが見つからない場合のエラーが出力されます。

{
  "errorMessage": "[Errno 2] No such file or directory: 'openssl'",
  "errorType": "FileNotFoundError",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 4, in lambda_handler\n    output = subprocess.run(['openssl',  'version'], stdout=subprocess.PIPE)\n",
    "  File \"/var/lang/lib/python3.8/subprocess.py\", line 489, in run\n    with Popen(*popenargs, **kwargs) as process:\n",
    "  File \"/var/lang/lib/python3.8/subprocess.py\", line 854, in __init__\n    self._execute_child(args, executable, preexec_fn, close_fds,\n",
    "  File \"/var/lang/lib/python3.8/subprocess.py\", line 1702, in _execute_child\n    raise child_exception_type(errno_num, err_msg, err_filename)\n"
  ]
}

ランタイムがPython3.7(Amazon Linux)のLambda関数では、OpenSSLのバージョンが出力されます。

OpenSSL 1.0.2k-fips 26 Jan 2017

AWS LambdaのランタイムとOS

OSにAmazon Linux 2を使用しているランタイムでOpenSSLを利用するためには、EC2のインスタンスからOpenSSLのバイナリを取得して、Lambdaにデプロイする必要があります。

Node.jsのランタイム

名前 識別子 AWS SDK for JavaScript オペレーティングシステム
Node.js 12 nodejs12.x 2.585.0 Amazon Linux 2
Node.js 10 nodejs10.x 2.585.0 Amazon Linux 2

Pythonのランタイム

名前 識別子 AWS SDK for Python オペレーティングシステム
Python 3.8 python3.8 boto3-1.10.34 botocore-1.13.34 Amazon Linux 2
Python 3.7 python3.7 boto3-1.10.34 botocore-1.13.34 Amazon Linux
Python 3.6 python3.6 boto3-1.10.34 botocore-1.13.34 Amazon Linux
Python 2.7 python2.7 boto3-1.10.34 botocore-1.13.34 Amazon Linux

AWS Lambda ランタイム

OpenSSLのバイナリを取得

EC2でAmazon Linux 2 AMIのインスタンスを立ち上げます。

Amazon Linux 2

インスタンスにSSHで接続して、OpenSSLパッケージのパスを確認します。

$ which openssl
/usr/bin/openssl

SCPでローカルに保存します。

$ mkdir openssl
$ scp -i <鍵ファイルのパス> ec2-user@xxx.xxx.xxx.xxx:/usr/bin/openssl ./openssl

SSH を使用した Linux インスタンスへの接続

Lambdaレイヤーを作成

先ほどローカルに保存したOpenSSLのバイナリをZIPファイルにします。

$ zip -r openssl.zip openssl/

Lambdaレイヤーを作成します。

Lambdaレイヤー

Lambda関数を作成

新しくLambda関数を作成して、Lambdaレイヤーを設定します。

Lambdaレイヤーの設定

LambdaレイヤーのPATHを追加して、OpensSSLコマンドを実行します。

lambda_function.py

import os
import subprocess

os.environ['PATH'] = os.environ['PATH'] + ':/opt/openssl'

def lambda_handler(event, context):
  output = subprocess.run(['openssl', 'version'], stdout=subprocess.PIPE)

  print(output.stdout.decode())

OpenSSLのバージョンが出力されます。

OpenSSL 1.0.2k-fips 26 Jan 2017

まとめ

Lambda関数で秘密鍵や証明書を作成しようとした際に、最新のランタイムでOpenSSLコマンドが使えなくて困りました。他にもOS依存のパッケージをLambdaで使いたい場合は、Lambdaと同じOS(Amazon Linux)をEC2で立ち上げて、バイナリをLambdaレイヤーにデプロイする事で実行できるようになるかと思います。

参考資料