Amazon Web Services ブログ

Ethereumアカウントを AWS Key Management Service を活用して安全に管理する – Part 1

このブログは、Use Key Management Service (AWS KMS) to securely manage Ethereum accounts: Part 1を翻訳したものです。

Ethereumは、止まらないアプリケーションを作ることが可能な人気のパーミッションレス型のパブリックブロックチェーンです。Ethereumアカウントを持っているすべてのユーザーが利用できます。これらのEthereumアカウントは、秘密鍵と関連する公開鍵で構成されています。

Ethereumなどのパブリックブロックチェーンに参加するユーザーの主な課題は、ブロックチェーンの認証情報を安全に管理することです。

外部所有の Ethereum アカウントは、資金移動やその他の機密操作を含むトランザクションを承認する必要があるため、そのキー マテリアルは慎重に保護する必要があります。

一部の完全に分散化されたアプリケーションでは、ユーザーが独自のキー マテリアルを管理する必要があります。ただし、キー マテリアルの管理を外部のプロセスまたはサービスに委ねるのが望ましいアプリケーションは他にもあります。例えば、ユーザーが利用できないときでもキー マテリアルが頻繁に必要になる場合などです。これは、トークンのステーキングやその他の最新のブロックチェーン アプリケーションの一般的な要件です。

この一連の投稿では、AWS Key Management Service (AWS KMS) を使用してソリューションを作成する方法について説明します。この最初の投稿では、以下の項目について説明します。

  • AWS Cloud Development Kit (AWS CDK) と Python を使用してクラウドインフラストラクチャテンプレートを開発および構築する

  • Docker を使用した AWS CDK ベースの AWS Lambda 関数をバンドルしてデプロイする

  • カスタマーマスターキー (CMK) を使用して、新しい AWS KMS ベースのEthereumアカウントを設定およびプロビジョニングする

2つ目の投稿では、Ethereumの内部の仕組みと、作成した CMK インスタンスを安全なEthereumキー管理サービスとして使用する方法に焦点を当てています。

この一連の3つ目の投稿では、Ethereum改善提案 1559 (EIP-1559) の詳細と、AWS KMS を使用して EIP-1559 トランザクションを署名する方法について説明しています。

ソリューション概要

次の図は、ソリューションアーキテクチャを示しています。

AWS CDK リポジトリで提供されるソリューションの範囲は、赤い点線内の領域に限定されます。

前提条件

このチュートリアルでは、次の前提条件を満たしている必要があります。

AWS CDKを使用してソリューションをデプロイする

AWS CDK は、クラウドアプリケーションリソースを定義およびプロビジョニングするためのオープンソースフレームワークです。JavaScript、C#、Python などの一般的なプログラミング言語を使用します。

AWS CDK command line interface (CLI)を使用すると、AWS CDK アプリケーションを操作できます。AWS CloudFormation テンプレートの合成、セキュリティ変更の確認、アプリケーションのデプロイなどの機能を提供します。

このセクションでは、AWS CDK とサンプルコードを実行するための環境を準備する方法を示します。

Python を使用するときは、venv を使ってプロジェクト固有の仮想環境を作成するのが良いでしょう。AWS CDKもvenvを使用することに対応しています。詳細については、ワークショップ Activating the virtualenv をご覧下さい。

サンプルアプリケーションをインストールするには、以下の手順を実行します。

  1. AWS CDK をインストールして AWS CDK CLI をテストします。
npm install -g aws-cdk@1.90.0 && cdk --version
  1. GitHub リポジトリからコードをダウンロードし、新しいディレクトリに移動します。
git clone https://github.com/aws-samples/aws-kms-ethereum-accounts.git && cd aws-kms-ethereum-accounts
  1. lambci/lambda: build-python3.8 Docker コンテナをダウンロードする。
docker pull lambci/lambda:build-python3.8
  1. Python パッケージマネージャーを使用して依存関係をインストールします。
pip install -r requirements.txt
  1. AWS CDK CLI を使用してサンプルコードをデプロイします。
cdk deploy

AWS CDK は、次のスクリーンショットに示すように、ソリューションをデプロイするための追加の確認を求めます。

  1. y を入力して確定します。



これにより、派生した CloudFormation テンプレートが指定した AWS アカウントにデプロイされます。AWS CloudFormation コンソールに移動して aws-kms-lambda-ethereum スタックを選択すると、デプロイプロセスとスタック (設定とリソース) に関する追加の詳細を確認できます。


デプロイが完了すると、ターミナルには CloudFormation スタック ARN とCMK KeyIDが表示されます。

CMK でEthereumトランザクションに署名する

Ethereumネットワークに公開できるEthereumトランザクションを作成して署名するには、十分な資金があるアカウントが必要です。RinkebyのようなEthereumテストネットのアカウントに資金を供給するには、Rinkeby faucet を 使用できます。 

CMK ベースのEthereumアドレスを特定するには、まず CMK ベースのアカウントのパブリック Ethereum アドレスを返す Lambda 関数を実行する必要があります。

  1. Lambda コンソールで、新しく作成した aws-kms-lambda-ethereum-ethkmsclientFunction Lambda 関数を選択します。

Lambda 関数に付けられるランダムなサフィックスは、AWS CDK がどのようにリソースに名前を付けて識別するかに関係しています。

  1. 関数を選択したら、[Test] タブを選択します。

  1. 次の JSON スニペットを新しいテストイベントのリクエストとして使用します。

{
"operation": "status"
}
  1. [Test] を選択します。



テストイベントを正常に実行すると、CMK パブリックキーの一致する Ethereum アドレスが計算され、次のスクリーンショットに示すように、チェックサムが有効なアドレス(eth_checksum_address)として返されます。

  1. 指定した CMK ベースのアドレスでEthereumトランザクションを作成して署名するには、次の JSON スニペットを使用して Lambda 関数を実行します。

{ 
"operation": "sign", 
"amount": 0.01, 
"dst_address": "0xa5D3241A1591061F2a4bB69CA0215F66520E67cf", 
"nonce": 0
 }

上記の JSON スニペットでは、Amount は送信するイーサの量を指定し、dst_address はEthereumの宛先アドレスを指定し、nonce は送信アドレスの現在のトランザクション数を指定します。

AWS KMS ベースのアドレスは一度も使用されていないため、最初のトランザクションではnonce値は 0 でなければなりません。

  1. [Test] を選択します。

正しいパラメータが指定されている場合、sign オペレーションは署名されたトランザクションを JSON オブジェクトにラップされた 16 進文字列として返します。


おめでとうございます!AWS KMS CMK が支援するEthereumトランザクションを初めて作成しました。

Amazon Managed Blockchain Ethereum ノードを介してトランザクションを送信するには、「Amazon Managed Blockchain でのEthereumノードのデプロイ」の手順に従います。参照されているブログから新しく作成された Lambda 関数は、署名バージョン 4 認証を使用して、専用の Amazon Managed Blockchain Ethereum ノードで認証されます。次の Node.JS の例に示すように、Ethereum クライアント Lambda 関数への入力パラメータとして 16 進エンコードされた Ethereum トランザクションを指定する必要があります。

web3.eth.sendSignedTransaction(serializedTx);

内部では何が起きているのか?

ローカルワークステーションにクローンされた CDKソースコードリポジトリには、CloudFormation テンプレートを定義する 2 つのファイルが含まれています。このテンプレートは、ソリューションアーキテクチャに示されている AWS クラウドインフラストラクチャを定義します。

aws-kms-ethereum-accounts フォルダの中にあるapp.pyはスタック名をaws-kms-lambda-ethereumと定義し、aws_kms_lambda_ethereum.aws_kms_lambda_ethereum_stackをスタック定義としてインポートします。

cdk deploy または cdk synthesize ステップ中に、このハイレベル ConstructはCloudFormation ステートメントに変換されます。

Python で利用できるハイレベル API では、期待されるタイプ、デフォルト値、または一致する必要がある正規表現を指定できます。

モジュールを追加するには、リポジトリのルートフォルダの setup.py ファイルにリストします。

install_requires=[
        "aws-cdk.core=={}".format(CDK_VERSION),
        "aws-cdk.aws-lambda=={}".format(CDK_VERSION),
        "aws-cdk.aws-kms=={}".format(CDK_VERSION)
     ],

これらのモジュールは、前述の pip インストール手順でインストールされます。

スタック定義ファイルでは、これらのモジュールを標準的な Python の方法でインポートできます。

from aws_cdk import (core, 
                     aws_lambda, 
                     aws_kms
                     )

aws_kms モジュールが利用可能になったので、それを使用してEthereum CMK インスタンスを定義できます。

cmk = aws_kms.Key(self, "eth-cmk-identity",
                        removal_policy=core.RemovalPolicy.DESTROY) 
cfn_cmk = cmk.node.default_child
cfn_cmk.key_spec = 'ECC_SECG_P256K1' 
cfn_cmk.key_usage = 'SIGN_VERIFY'

表示されている CMK インスタンス定義について詳しく説明しなければならないことが 2 つあります。

まず、デフォルトの CMK インスタンスは RETAIN ポリシーで作成されます。つまり、CloudFormation スタックが削除されても、これらのインスタンスは削除されません。特にこのアドレスに資金が送られた後は、キー(またはこの場合はEthereumアカウント)を失いたくないので、これは重要です。

この例では、removal_policy が明示的に DESTROY に設定されているため、CMK リソースはこのソリューションの CloudFormation スタックによって自動的に削除され、今後の請求を防ぐことができます。作成された CMK リソースと関連付けられている Ethereum アカウントを CloudFormation スタックと一緒に削除しない場合は、removal_policy を RETAIN に戻す必要があります。

CMK リソースを削除すると、デフォルトで 30 日間の待機期間 が設定され、その後 CMK は消去されます。待機時間の間、CMK はどの暗号化操作にも使用できませんが、回復は可能です。

さらに、CDK キーの定義では、この例ではいわゆるエスケープハッチを使用しています。これらは、低レベルの CloudFormation リソース構造にアクセスするために必要です。

この記事の執筆時点では、標準の AWS CDK では対称キーしか定義できないため、この低レベルの AWS CDK API を使用して、非対称キーの定義とキーの使用法を指定する必要があります。

低レベル API を使用して、key_speckey_usage を指定できます。key_usage パラメータを ECC_SECG_P256K1 に設定する必要があります。これは、Ethereumで使用されているのと同じ楕円曲線 secp256k1 を表します。

非対称 CMK の場合は、key_usageSIGN_VERIFY に設定する必要があります。

aws_kms モジュールと同様に、Lambda リソースを指定するには aws_lambda モジュールをインポートする必要があります。

lf = aws_lambda.Function(
            self,
            "Function",
            handler="lambda_function.lambda_handler",
            runtime=aws_lambda.Runtime.PYTHON_3_8,
            environment=env,
            timeout=core.Duration.minutes(2),
            code=code,
            memory_size=256
     )

このコード例は aws-kms-ethereum-accounts/aws_kms_lambda_ethereum/aws_kms_lambda_ethereum_stack.py ファイルにあります。

CloudFormation リソースの作成と設定の他に、AWS CDK では高レベルの Python オブジェクトを使用してアクセス権限を管理することもできます。

この投稿では、Lambda 関数のみが AWS CMK リソースを使用する必要があります。これを確実にするために、CMK オブジェクトで使用できる付与メソッドを使用できます。Lambda リソースの参照と、許可するアクション (‘kms:GetPublicKey’‘kms:Sign’) を渡す必要があります。

cmk.grant(eth_client.lf, 'kms:GetPublicKey') 
cmk.grant(eth_client.lf, 'kms:Sign')

AWS KMS のアクションとアクセス権限の詳細については、「AWS KMS アクセス許可」を参照してください。

Dockerベースのビルド

Lambda 関数で利用可能にする必要がある外部依存関係によっては、Lambda コンストラクターをローカルファイルシステムフォルダーに指定するだけで十分な場合があります。詳細については、「AWS CDK を使用してサーバーレスアプリケーションを作成する」を参照してください。

このフォルダはsynthesizeステップで圧縮され、デプロイステップ中に Amazon S3 にアップロードされます。このメカニズムは、Lambda 関数が Python Lambda ランタイム環境 で利用可能な Boto3 ライブラリのみを必要とする場合にうまく機能します。

追加の依存関係を提供する必要がある場合は、これらの依存関係が Lambda ソースコードファイルとともにルートディレクトリにある必要があります。詳細については、「チュートリアル:Python 3.8 での Lambda 関数の作成」を参照してください。

無効な ELF ヘッダーなど、Lambda 環境でのインポートの問題を防ぐために、特定の Python 依存関係をコンパイルして Linux 環境にインストールする必要があります。

同じ要件がカスタム node.js パッケージとモジュールにも当てはまります。詳細については、「AWS Lambda でのパッケージとネイティブ nodejs モジュールの使用」を参照してください。

Amazon Linux on EC2 ベースのアプローチに加えて、Docker ベースのバンドルを使用して依存関係をインストールし、適切なファイル形式を維持することができます。

これを行うには、Linux Docker コンテナー内でpip install ステップ (requirements.txt ファイルにリストされている Lambda 要件を利用可能にするために必要です) を実行します。

AWS CDK で Docker ベースのバンドルを使用するには、Lambda 関数のソースコードへのパスを指定して bundling_configaws_lambda.Code.from_asset() メソッドに渡す必要があります。次のコード例は aws -kms-ethereum-accounts/aws_kms_lambda_ethereum/aws_kms_lambda_ethereum_stack.py ファイルにあります。

bundling_docker_image = core.BundlingDockerImage.from_registry(
"lambci/lambda:build-python3.8")

commands = [
"if [[ -f requirements.txt ]]; then pip install --target /asset-output -r requirements.txt; fi",
"cp --parents $(find . -name '*.py') /asset-output"
]

bundling_config = core.BundlingOptions( 
image=bundling_docker_image, command=["bash", "-xe", "-c", " && ".join(commands)]
) 

code = aws_lambda.Code.from_asset( 
path=dir, bundling=bundling_config 
)

このbundling_configは、Dockerイメージ、lambci/lambda:build-python3.8, および依存関係をバンドルするためにDockerコンテナで実行されるコマンドを指定します。

Golang などの他のプログラミング言語で Docker ベースのバンドルを使用する方法の詳細については、「AWS CDK によるアプリケーションのビルド、バンドル、デプロイ」を参照してください。

カスタムバンドル関数の代わりに、Amazon Lambda Python ライブラリ を使用できます。このライブラリは、必要なすべてのモジュールの Lambda 互換 Docker コンテナへのインストールを、ランタイムに従ってすぐに処理します。

Ethereum キー計算

CMK パブリックキーに基づいてEthereumパブリックアドレスを計算する方法の詳細な説明は、このシリーズの第 2 つ目の投稿「AWS KMS を使用して Ethereum ID を安全に管理する:パート 2」にあります。

クリーンアップ

今後料金が発生しないようにするには、リソースを削除してください。次のコマンドを使用して、AWS CDK でこれを行うことができます。

cdk destroy

AWS CDK によってデプロイされたスタックは、AWS CloudFormation コンソールを使用して削除することもできます。

結論

この投稿では、AWS CDK を使用して Lambda および CMK リソースを作成する方法を示しました。さらに、Python で AWS CDK オブジェクトにカスタム設定を適用して、Ethereum互換の CMK インスタンスを作成する方法を説明しました。また、AWS CDK によってポリシーとアクセス権限の設定と管理が可能になる方法についても詳しく説明しました。

この連載の 2 つ目の投稿には、Ethereum署名の仕組みと、この記事で作成したリソース (CMK インスタンスと関連する Lambda 関数) を使用して安全に計算する方法についての詳細な説明が含まれています。

著者について

David Dornseiferは、Amazon ProServe ブロックチェーンチームのブロックチェーンアーキテクトです。彼は、顧客がエンドツーエンドのブロックチェーンソリューションを設計、展開、拡張するのを支援することに重点を置いています。

このブログは、ソリューションアーキテクトの渡邊英士が翻訳しました。