この記事の目的
メールで数GBのファイルを送りたいが、サイズ制限で送れない。
送るデータにはセキュリティをかけて、第三者にダウンロードさせたくない。
ファイル転送サービスやクラウドストレージを使えば簡単だが、社内的にまずい。
そんなときに、S3+デバイス証明書だけでファイル転送サービスを自作する方法があります。
構成図
AWSの構成図は以下の通りです。LambdaやCloudFrontは不要です。
デバイス証明書からアクセス権限を貸与することで、図の赤線部分の認証が通るようになります。
利用フロー
利用の流れは以下の通りです。
受け取る人のPCに、特別なアプリケーションやソフトをインストールする必要はありません。
(※Windowsの標準機能だけで動きます)
セキュリティ
証明書にはパスワード付きpfxを使います(※AWS IoTの「1-Click 証明書作成」で作成した証明書と秘密鍵とルート証明書を1つのファイルにまとめて、パスワードをかけたものです)
S3のセキュリティにpfx証明書を使うことで、以下の状態になります。
また、1つの証明書が許可する範囲は、S3にある1つのファイルパスだけです。
設定の手順
手順が複雑なため、GithubにCloudFormationとバッチファイルを用意しました。
https://github.com/ShotaOki/s3FileDeriver
先に簡単な手順を説明して、後でAWS純正で作業するときの手順を説明します。
サクッと簡単に作る
前提条件
- AWSアカウントがあること
- メールの送信側と受信側、どちらもWindows10であること
送信側に必要なもの
- AWS アカウント
- Python(3.8で動作検証しています。PyOpenSSLを使います)
- AWS CLI
受信側に必要なもの
- なし(PowerShellが動けばOKです)
事前準備
GithubからバッチファイルをCloneしてきます。
git clone https://github.com/ShotaOki/s3FileDeriver.git
Cloneしたら、フォルダの直下を開きます。
pythonに依存ライブラリをインストールします。
pip install -r .\requirements.txt
リソースを作ります
目的:デバイス証明書、ロール、ポリシーを作ります。
手順:フォルダにあるcreate.bat
をダブルクリックします。
ダブルクリックすると対話型コンソールが出てきて、
- ダウンロードを許可するS3のバケット名
- ダウンロードを許可するS3のファイル名
- AWSアカウントのプロファイル名
を入力します。CloudFormationのリソース作成が2分くらいかかるので、気長に待ちます。
完了すると、outputs
の下にファイルが作られます。
各証明書をpfx形式にまとめます
目的:「秘密鍵 + PEM + ルート証明書」をpfx形式にまとめます。
手順:フォルダにあるconvert.bat
を実行します。
ダブルクリックすると対話型コンソールが出てきて、
-
create.bat
で作成したデプロイ情報ファイル(deploy-info.json)のパス - 証明書の保護パスワード(好きな文字列)
を入力します。
送信する
convert.bat
が完了すると、exports
の下に3つのファイルが作られます。
このexports
フォルダを圧縮して、メールで渡して、保護パスワードを伝えれば、他のPCからダウンロードすることができます。
ダウンロードする
Zipファイルを受け取った人は、展開してフォルダを開きます。
ここにあるdownload.bat
をダブルクリックすると、パスワードの入力画面が出ます。
送信側がconvert.bat
で設定したパスワードと一致すると、S3からのダウンロードが始まります。
ダウンロード後の後始末
目的:不要になったリソースを削除します
手順:delete.bat
を実行します
バッチを実行すると、証明書の失効、AWS上のリソースの削除、ローカルファイルの削除をします。
解説:AWSコンソールで作る場合の手順
自動化せず、AWSコンソールでやる場合の手順について、概要だけ説明します。
どんな仕組みで、何をやっているのか
制限されたS3に、デバイス証明書でアクセスする
AWSのサービスの認証にはACCESS_KEY_ID
とSECRET_ACCESS_KEY
を使った署名が必要です。
AWS IoTにだけは特例があり、デバイス証明書(X.509)で認証することができます。
デバイスは、TLS 相互認証プロトコルを使用して AWS IoT Core に接続するために X.509 証明書を使用することができます。他の(※IoT以外の) AWS サービスは証明書ベースの認証をサポートしていません
デバイス証明書の認証はIoTでしか使えないのですが、IoTには以下の機能があります。
X.509 証明書を使用して発信者を認証し、一時的で制限された権限のセキュリティトークンを発行します。このトークンは、すべての AWS リクエストに署名して認証するために使用できます。
つまり、デバイス証明書からACCESS_KEY_ID
とSECRET_ACCESS_KEY
を発行できます。
ここで発行した一時的なトークンは、ポリシーでアクセス範囲を制限されます。
※ポリシーで許可したAWSのサービスを実行できる、Lambdaと同じ状態です。
参考:AWS のサービスの直接呼び出しの認証
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html
SDKなしで、証明書の認証とAWSへの接続をする
実行するために、以下のことをします。
- デバイス証明書と秘密鍵を使ったIoTエンドポイントとのやり取りをする。
- デバイス証明書から
ACCESS_KEY_ID
とSECRET_ACCESS_KEY
を発行(AssumeRole)する。 - 受け取った
ACCESS_KEY_ID
とSECRET_ACCESS_KEY
を使って、S3からファイルをダウンロードする。
普通はboto3みたいなAWS SDK
でやり取りをするのですが、「AWS SDK
は必ずインストールしないといけないほど特別な処理をしているのか?」というとそうでもなく、ほとんどのAPIは署名の計算をしてURLを投げているだけです。署名の仕様も公開されていますので、BashやPowerShellで同じ処理を書くことができます。
今回のGithubのソースでは、PowerShellで上の3つを実施しています。
Github:上記の処理をしているPowerShellのバッチ
https://github.com/ShotaOki/s3FileDeriver/blob/main/powershell/download_with_device_cert.ps1
参考:署名バージョン 4 署名プロセス
https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-version-4.html
参考:AWS-SDKをbashで自作してみる(過去記事。Bashでv4署名する)
https://qiita.com/ShotaOki/items/0487828b19319bb90411
PEMをpfxに変換する理由
PowerShellのInvoke-RestMethodは「PEM + 秘密鍵 + ルート証明書」のリクエストに対応していないため、pfxに変換します。
Bashで実施する場合は、以下のコマンドを実行します。
openssl pkcs12 -export -in abcdef-certificate.pem.crt -inkey abcdef-private.pem.key -certfile AmazonRootCA1.pem -out abcdef.pfx
記事中の画像リソースについて
記事中の画像は規約の範囲内で利用しています。
ファンキット利用規約
https://arknights.jp/fankit/precautions