デバイス証明書を使って、S3をダウンロード制限付きのファイル転送サービスとして使う方法


この記事の目的

メールで数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してきます。

ソースコードをCloneする
git clone https://github.com/ShotaOki/s3FileDeriver.git

Cloneしたら、フォルダの直下を開きます。

pythonに依存ライブラリをインストールします。

OpenSSLをインストールする
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_IDSECRET_ACCESS_KEYを使った署名が必要です。
AWS IoTにだけは特例があり、デバイス証明書(X.509)で認証することができます。

デバイスは、TLS 相互認証プロトコルを使用して AWS IoT Core に接続するために X.509 証明書を使用することができます。他の(※IoT以外の) AWS サービスは証明書ベースの認証をサポートしていません

デバイス証明書の認証はIoTでしか使えないのですが、IoTには以下の機能があります。

X.509 証明書を使用して発信者を認証し、一時的で制限された権限のセキュリティトークンを発行します。このトークンは、すべての AWS リクエストに署名して認証するために使用できます。

つまり、デバイス証明書からACCESS_KEY_IDSECRET_ACCESS_KEYを発行できます。
ここで発行した一時的なトークンは、ポリシーでアクセス範囲を制限されます。

※ポリシーで許可したAWSのサービスを実行できる、Lambdaと同じ状態です。

参考:AWS のサービスの直接呼び出しの認証
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html

SDKなしで、証明書の認証とAWSへの接続をする

実行するために、以下のことをします。

  • デバイス証明書と秘密鍵を使ったIoTエンドポイントとのやり取りをする。
  • デバイス証明書からACCESS_KEY_IDSECRET_ACCESS_KEYを発行(AssumeRole)する。
  • 受け取ったACCESS_KEY_IDSECRET_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