S3 へのアクセスを TLS 1.2 に限定! 新しく追加された IAM 条件キー s3:TlsVersion を使ってみた

S3 へのアクセスを TLS 1.2 に限定したいあなたへ。

コンバンハ、千葉(幸)です。

2020年12月のアップデートで、以下の Amazon S3 の IAM 条件キーが追加されました。

  • s3:ResourceAccount
  • s3:TLSVersion

前者は使い方がパッと分かったのですが、後者はいまいち分からず……。

手探りで確認した結果、大まかに使い方が分かりましたので、ご紹介します。

まとめ

  • "s3:TlsVersion": "1.x"の書式で使用する
  • 以下クライアントによる S3 アクセスは基本的に TLS 1.2 で行われる
    • AWS マネジメントコンソール
    • AWS CLI
    • AWS サービス
    • AWS 製エージェント
  • 条件キーの想定される主な用途は S3 アクセスを TLS 1.2 に限定すること

TLS 1.2 でない場合 GetObject を禁止する」という S3 バケットポリシー例は以下の通り。

バケットポリシー例

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<バケット名>/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:TlsVersion": "1.2"
                }
            }
        }
    ]
}

s3:TlsVersion の用途

s3:TlsVersionは、以下のいずれでも使用できる条件キーです。

  • IAM ポリシー
  • VPC エンドポイントポリシー
  • S3 バケットポリシー

conditionkey

どのパターンでも、「クライアントが使用している TLS バージョン」によって S3 へのアクセスをフィルタリングするものとして機能します。

主な用途としては、「 アクセスをTLS 1.2 に限定したい(あるいは TLS 1.1 以下を禁止したい)」というものになると考えます。

IAM 条件キーs3:TlsVersionについては(ほとんど情報がありませんが)以下を参照してください。

S3 へのアクセスのパターン

S3 に対するアクセスとして、代表的なパターンを考えてみました。ここでは東京リージョンの S3 バケットを想定していますが、他のリージョンに置き換えても基本的な考え方は同じです。

s3access

  • マネジメントコンソール
  • AWS CLI
  • AWS SDK
  • AWS サービス
    • Cloud Trail、VPCフローログなどによるログ出力
    • AWS Config や Trusted Advisor による参照
  • アプリケーション
    • 各種エージェントなど
  • パブリックアクセス
    • CloudFront 経由
    • curl や wget、ブラウザアクセス など

画像右側のクライアントが個々のバケットまで矢印を伸ばしているのは、後述するエンドポイントのパターンで仮想ホスト形式でのアクセスが主であるように見受けられたからです。(2021年1月現在。例外あり。)

パス形式と仮想ホスト形式

余談ですが、S3 アクセスを行う際のエンドポイントには以下のパターンがあります。

  • パス形式
  • 仮想ホスト形式

2019年5月に「2020年9月以降 パス形式での API リクエストがサポートされなくなる」と発表がありましたが、非推奨化は延期されたようです。

REST API を使用したリクエストの実行 - Amazon Simple Storage Service

更新 (2020 年 9 月 23 日) – パス形式 URL の非推奨化を延期し、お客様が仮想ホスティング形式の URL への移行に必要な時間を確保できるようにすることを決定しました。詳細については、Amazon S3 Path Deprecation Plan – The Rest of the Storyを参照してください。

S3 のアクセスログから、どのエンドポイントにどういったパターンでアクセスが行われるか確認してみました。

パス形式

  • s3.ap-northeast-1.amazonaws.com/<バケット名>
    • マネジメントコンソールからの操作の際、ここに向けた API が多く呼び出されていました
      • ?policy
      • ?publicAccessBlock=
      • ?accelerate= など
  • s3.amazonaws.com/<バケット名>
    • AWS Config による/への HEAD が行われていました(ただしステータスコード 301 が返っている)

仮想ホスト形式

  • <バケット名>.s3.ap-northeast-1.amazonaws.com
    • 多くのアクセスパターンではここに向けて呼び出しを行っていました
      • /オブジェクトのパスを伴うオブジェクトへのアクセス
      • /?location /?policy /?aclなどの GET
  • <バケット名>.amazonaws.com
    • 限られたパターンで呼び出しが行われていました
      • AWS Config による/への HEAD(ただしステータスコード 400 が返っている)
      • CloudFront オリジンでこのエンドポイントを指定している場合、/オブジェクトのパスを伴うアクセス

各アクセスパターンで使用される TLS バージョン

上記のいくつかのパターンで使用される TLS のバージョンを、S3のアクセスログから確認してみました。

本来は1行で出力されるものですが、フィールドごとに改行を入れています。

マネジメントコンソール

特定バケットのログ出力ステータスを確認した際のログです。TLS 1.2 が使用されています。

(今回取り上げるログの中では唯一、接続先がs3.ap-northeast-1.amazonaws.comです。)

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
202.xx.xx.xx  #リモートIP
arn:aws:sts::000000000000:assumed-role/RoleName/SessionName  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.GET.LOGGING_STATUS  #オペレーション
- #キー
"GET /<バケット名>?logging= HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
243  #Bytes Sent
-  #オブジェクトサイズ
8  #トータルタイム
-  #ターンアラウンドタイム
"-"  #リファラ
"S3Console/0.4, aws-internal/3 aws-sdk-java/1.11.920 Linux/4.9.230-0.1.ac.223.84.332.metal1.x86_64 OpenJDK_64-Bit_Server_VM/25.275-b01 java/1.8.0_275 vendor/Oracle_Corporation"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-GCM-SHA256  #暗号スイート
AuthHeader  #認証タイプ
s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

マネジメントコンソールから操作した際に使用される TLS バージョンについて言及している公式資料は見つかりませんでしたが、基本的には TLS 1.2 が使用されるようです。

AWS CLI

特定のバケットからオブジェクトをaws s3 cpで取得した際のログです。

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
202.xx.xx.xx  #リモートIP
arn:aws:iam::000000000000:user/chiba-cli  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.HEAD.OBJECT  #オペレーション
test.txt #キー
"HEAD /test.txt HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
- #Bytes Sent
6  #オブジェクトサイズ
41  #トータルタイム
-  #ターンアラウンドタイム
"-"  #リファラ
"aws-cli/2.1.21 Python/3.7.4 Darwin/19.6.0 exe/x86_64 prompt/off command/s3.cp"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-GCM-SHA256  #暗号スイート
AuthHeader  #認証タイプ
<バケット名>.s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

AWS CLI のバージョン1を使用する際は、クライアントの設定次第では TLS 1.1 を使用する可能性もあるそうです。

こういったケースに備えて、 IAM ポリシーで「TLS 1.2 の場合のみ許可」としておく、というs3:TlsVersionの用途も考えられますね。

AWS SDK

実際のアクセスログを用いた確認は省略しました。

各言語のドキュメントを確認するに、「原則 TLS 1.2 が使用される」「最小バージョンを 1.2 に指定することも可能」という整理になりそうです。

以下は AWS SDK for java のドキュメントですが、各言語ごとに TLS 1.2 対応に関するドキュメントがあります。

AWS サービスによるログ出力

代表的なものとして、VPC フローログと CloudTrail ログの Put を確認してみました。どちらも TLS 1.2 が使用されていました。

言及されている資料はありませんが、原則 TLS 1.2 と考えて問題なさそうです。

VPCフローログ

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
-  #リモートIP
svc:delivery.logs.amazonaws.com  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.PUT.OBJECT  #オペレーション
AWSLogs/000000000000/vpcflowlogs/ap-northeast-1/2020/12/31/000000000000_vpcflowlogs_ap-northeast-1_fl-xxxxx00cd018e5f7b_20201231T2355Z_48107228.log.gz #キー
"PUT /AWSLogs/000000000000/vpcflowlogs/ap-northeast-1/2020/12/31/000000000000_vpcflowlogs_ap-northeast-1_fl-xxxxx00cd018e5f7b_20201231T2355Z_48107228.log.gz HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
-  #Bytes Sent
932  #オブジェクトサイズ
68  #トータルタイム
25  #ターンアラウンドタイム
"-"  #リファラ
"-"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-GCM-SHA256  #暗号スイート
AuthHeader  #認証タイプ
<バケット名>.s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

CloudTrail

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
-  #リモートIP
svc:cloudtrail.amazonaws.com  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.PUT.OBJECT  #オペレーション
AWSLogs/000000000000/CloudTrail/us-east-1/2020/12/31/000000000000_CloudTrail_us-east-1_20201231T2250Z_xxxxYXCVLaIUV8c6.json.gz #キー
"PUT /AWSLogs/000000000000/CloudTrail/us-east-1/2020/12/31/000000000000_CloudTrail_us-east-1_20201231T2250Z_xxxxYXCVLaIUV8c6.json.gz HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
-  #Bytes Sent
932  #オブジェクトサイズ
68  #トータルタイム
25  #ターンアラウンドタイム
"-"  #リファラ
"-"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-SHA  #暗号スイート
AuthHeader  #認証タイプ
<バケット名>.s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

アプリケーションによるログ出力

Systems Manager エージェントによる、セッションマネージャーログの出力に関するアクセスログを確認しました。

(ちなみにリモートIPには、インスタンスのプライベート IP が記録されていました。)

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
10.xx.xx.xx  #リモートIP
arn:aws:sts::000000000000:assumed-role/RoleName/i-xxxxxxxxxx  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.PUT.OBJECT  #オペレーション
session-manager/<SSM接続ユーザー名>-xxxxxx973da7940e.log #キー
"PUT /session-manager/<SSM接続ユーザー名>-xxxxxx973da7940e.log HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
-  #Bytes Sent
932  #オブジェクトサイズ
68  #トータルタイム
25  #ターンアラウンドタイム
"-"  #リファラ
"aws-sdk-go/1.25.41 (go1.12.11; windows; amd64) amazon-ssm-agent/ S3Manager"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-GCM-SHA256  #暗号スイート
AuthHeader  #認証タイプ
<バケット名>.s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

TLS 1.2 が使用されています。こちらも使用する TLS バージョンについて言及された資料は確認できませんでしたが、AWS 標準のものであれば 1.2 を使用するのでは、と考えています。

サードパーティのアプリケーションを使用する際に TLS 1.2 を使用するか分からないという場合には、使用する IAM ユーザー/ロールのポリシーで制限するという用途も考えられます。

CloudFront 経由のアクセス

CloudFront を経由したアクセスについて、おさらいも兼ねて整理してみました。

cloudfront-origin

今回確認すべきは、 ウェブサイトエンドポイントでない S3 をオリジンとし、 CloudFront 経由でアクセスが行われるケースです。

以下は補足です。

複数パターンでのアクセス

Cloudfront のオリジンの S3 バケットに/maintenance/test.txtがある環境で、以下のように接続を試みました。

curlによるアクセス

% curl https://xxxxxq540ruiz6.cloudfront.net/maintenance/test.txt --tls-max 1.2 -v
*   Trying 13.35.55.26:443...
* Connected to xxxxxq540ruiz6.cloudfront.net (13.35.55.26) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.cloudfront.net
*  start date: May 26 00:00:00 2020 GMT
*  expire date: Apr 21 12:00:00 2021 GMT
*  subjectAltName: host "xxxxxq540ruiz6.cloudfront.net" matched cert's "*.cloudfront.net"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert Global CA G2
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fe63d812e00)
> GET /maintenance/test.txt HTTP/2
> Host: xxxxxq540ruiz6.cloudfront.net
> user-agent: curl/7.74.0
> accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< content-type: text/plain
< content-length: 6
< date: Tue, 26 Jan 2021 12:07:19 GMT
< last-modified: Tue, 26 Jan 2021 09:57:26 GMT
< etag: "xxxx6ac92492d2347c6235b4d2611184"
< x-amz-server-side-encryption: AES256
< accept-ranges: bytes
< server: AmazonS3
< x-cache: Miss from cloudfront
< via: 1.1 xxxxbc835e000996f0b384c9db0412cc.cloudfront.net (CloudFront)
< x-amz-cf-pop: NRT20-C1
< x-amz-cf-id: _xxxxxSpdxQKQaYaUGuPPDKFCsQGkOwK0mTjEtTOFRBUa1udfogrvg==
<
hello
* Connection #0 to host xxxxxq540ruiz6.cloudfront.net left intact

上記ではビューワー/CloudFront 間を TLS 1.2 で接続していますが、オプションを変更して以下のバージョンでも確認してみました。

  • TLS 1.1(--tls-max 1.1
  • TLS 1.3 (--tlsv1.3

キャッシュヒットしないよう(都度 S3 にアクセスが行くよう)にして試したところ、上記 3ついずれのパターンでも、オリジンである S3 に対しては TLS 1.2 でアクセスされていました。(以下は Athena でクエリをかけた際の図)

Athena

アクセスログの例は以下の通りです。

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
52.46.54.104  #リモートIP
xxxxxxx0d581bfcd5867696742065d5e7b275713d019fec25f1f299a4b8909684f6f21061c1c8013df4aad22942c2259  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.GET.OBJECT  #オペレーション
maintenance/test.txt #キー
"GET /maintenance/test.txt HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
6  #Bytes Sent
6  #オブジェクトサイズ
68  #トータルタイム
67  #ターンアラウンドタイム
"-"  #リファラ
"Amazon CloudFront" #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
SigV4  #署名バージョン
ECDHE-RSA-AES128-GCM-SHA256  #暗号スイート
AuthHeader  #認証タイプ
<バケット名>.s3.amazonaws.com  #ホストヘッダ
TLSv1.2  #tlsバージョン

ビューワー/CloudFront間が HTTPS 通信の場合、CloudFront/オリジン S3 間は HTTPS(TLS1.2)でアクセスが行われるようです。

パブリックアクセス

curl やブラウザなどによる、エンドユーザーからのオブジェクトへのアクセスを想定しています。

あるバケットの/test.txtというオブジェクトを、 curl で TLS 1.1 を指定して GET した際のログが以下です。

S3アクセスログ

xxxxxxxxxxxxxxxxxxxxa0ba501ad4f6e6513e8620f29b3f25cxxxx92a3a24a3 #バケット所有者
<バケット名> #バケット
[31/Dec/2020:23:56:19 +0000] #時間
202.xx.xx.xx  #リモートIP
-  #リクエスタ
xxxxx310EC0A884E  #リクエストID
REST.GET.OBJECT  #オペレーション
test.txt #キー
"GET /test.txt HTTP/1.1"  #リクエストURI
200  #HTTPステータス
-  #エラーコード
6  #Bytes Sent
6  #オブジェクトサイズ
18  #トータルタイム
18  #ターンアラウンドタイム
"-"  #リファラ
"curl/7.74.0"  #User-Agent
-  #バージョンID
xxxxxxxxxIGx7EisJxh3RjWzDTOJs09ifXRQoM6M8W5/ZcrkweGQBzrCBcW61Is3OhZLV4s7uYI=  #ホストID
-  #署名バージョン
ECDHE-RSA-AES128-SHA  #暗号スイート
-  #認証タイプ
<バケット名>.s3.ap-northeast-1.amazonaws.com  #ホストヘッダ
TLSv1.1  #tlsバージョン

これまでのパターンと比較して、値が含まれないフィールドがいくつかあります。

また、当然ながら今回のケースではTLSv1.1で記録されています。明示的にバージョンを指定しなかったり、ブラウザによるアクセスを行った際には TLS 1.2 が記録されます。

改めて s3:TlsVersion の用途

ここまでの確認で、どのパターンでも大まかには TLS 1.2 によるアクセスが行われることが確認できました。あえてs3:TlsVersionを使用するモチベーションとしては、以下があるかと考えます。

  • 原則 TLS 1.2 が使用されるパターンでも、S3 バケットポリシーで明示的に TLS 1.2 を指定する
  • TLS 1.1 が使用される可能性のあるクライアントでは、 IAM ポリシーによって TLS 1.1 アクセスを制限する
  • こちらでコントロールできないエンドユーザーからのアクセスに備えて、S3 バケットポリシーで TLS 1.2 に限定する
  • VPC 内のリソースによる S3 へのアクセスを VPC エンドポイントポリシーで一律的に制限する

社内のセキュリティポリシーで「通信は全て TLS 1.2 とすること」といった項目が定められている環境もあるかと思います。

そのような厳格なコントロールが求められる場合に今回の IAM 条件キーの追加はマッチするのではないでしょうか。

やってみた

長々と理屈を語ってきましたが、最後にさっとやって終わりにします。

今回は以下の構成で行います。

curltls

  • オブジェクト ACL でパブリックアクセスを許可したオブジェクトがある
  • S3 バケットポリシーで「 TLS 1.2 でなければ Get 不可 」を定義
  • curl で TLS バージョンを切り替えて Get を試行

バケットポリシー設定 (指定なし)の時点では、http(sでない)も含めて任意の手段でアクセス可能です。

chibayuki-pub_s3-ap-northeast-1_amazonaws_com_test_txt

S3 バケットに冒頭に載せたものと同じ 以下のポリシーを設定し、以降のアクセスを行います。

バケットポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::chibayuki-pub/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:TlsVersion": "1.2"
                }
            }
        }
    ]
}

(なお、今回はActions3:GetObjectのみを指定していますが、実際に運用を行う上で他のアクションを追加したいこともあるかと思います。意図せず S3 バケット全体の操作が不可となる、といったことがないよう、事前に IAM ポリシー側で検証を行うなどの対応を行ってください。)

TLS 1.1 でアクセス

--tls-max 1.1を指定し curl を実行します。

curlによるアクセス

% curl -v --tls-max 1.1 https://chibayuki-pub.s3-ap-northeast-1.amazonaws.com/test.txt
*   Trying 52.219.68.163:443...
* Connected to chibayuki-pub.s3-ap-northeast-1.amazonaws.com (52.219.68.163) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.1 (OUT), TLS handshake, Client hello (1):
* TLSv1.1 (IN), TLS handshake, Server hello (2):
* TLSv1.1 (IN), TLS handshake, Certificate (11):
* TLSv1.1 (IN), TLS handshake, Server key exchange (12):
* TLSv1.1 (IN), TLS handshake, Server finished (14):
* TLSv1.1 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.1 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.1 (OUT), TLS handshake, Finished (20):
* TLSv1.1 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.1 / ECDHE-RSA-AES128-SHA
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.s3-ap-northeast-1.amazonaws.com
*  start date: Aug 27 00:00:00 2020 GMT
*  expire date: Sep  1 12:00:00 2021 GMT
*  subjectAltName: host "chibayuki-pub.s3-ap-northeast-1.amazonaws.com" matched cert's "*.s3-ap-northeast-1.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> GET /test.txt HTTP/1.1
> Host: chibayuki-pub.s3-ap-northeast-1.amazonaws.com
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< x-amz-request-id: xxx6F4ACFC25936F
< x-amz-id-2: xxxMGwW+ra3bQ5CxoBswvb+AqqBJDpdc6eVhqoQVIdE4yu7Dyf08FDu29IRSO4WUzf55YZ+ASfM=
< Content-Type: application/xml
< Transfer-Encoding: chunked
< Date: Tue, 26 Jan 2021 17:27:29 GMT
< Server: AmazonS3
<
<?xml version="1.0" encoding="UTF-8"?>
* Connection #0 to host chibayuki-pub.s3-ap-northeast-1.amazonaws.com left intact
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>xxx6F4ACFC25936F</RequestId><HostId>xxxMGwW+ra3bQ5CxoBswvb+AqqBJDpdc6eVhqoQVIdE4yu7Dyf08FDu29IRSO4WUzf55YZ+ASfM=</HostId></Error>

アクセスが拒否されました。

TLS 1.2 でアクセス

--tls-max 1.2を指定して実行します。

curlによるアクセス

% curl -v --tls-max 1.2 https://chibayuki-pub.s3-ap-northeast-1.amazonaws.com/test.txt
*   Trying 52.219.68.171:443...
* Connected to chibayuki-pub.s3-ap-northeast-1.amazonaws.com (52.219.68.171) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=Washington; L=Seattle; O=Amazon.com, Inc.; CN=*.s3-ap-northeast-1.amazonaws.com
*  start date: Aug 27 00:00:00 2020 GMT
*  expire date: Sep  1 12:00:00 2021 GMT
*  subjectAltName: host "chibayuki-pub.s3-ap-northeast-1.amazonaws.com" matched cert's "*.s3-ap-northeast-1.amazonaws.com"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert Baltimore CA-2 G2
*  SSL certificate verify ok.
> GET /test.txt HTTP/1.1
> Host: chibayuki-pub.s3-ap-northeast-1.amazonaws.com
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< x-amz-id-2: xxxhXQ7IQd1MC0lWZqeslqoV0xLD8Psn3qR0wU+4ms0fAd4kxOC6m89evfgnxlrczX7W3pqyl7Y=
< x-amz-request-id: xxxFC4A1D98E678C
< Date: Tue, 26 Jan 2021 17:28:29 GMT
< Last-Modified: Wed, 20 Jan 2021 08:09:10 GMT
< ETag: "xxx46ac92492d2347c6235b4d2611184"
< Accept-Ranges: bytes
< Content-Type: text/plain
< Content-Length: 6
< Server: AmazonS3
<
hello
* Connection #0 to host chibayuki-pub.s3-ap-northeast-1.amazonaws.com left intact

問題なくオブジェクトの中身を参照できました。

TLS 1.3 でアクセス

--tlsv1.3を使用し、下限を TLS 1.3 にして実行します。

curlによるアクセス

% curl -v --tlsv1.3 https://chibayuki-pub.s3-ap-northeast-1.amazonaws.com/test.txt
*   Trying 52.219.68.139:443...
* Connected to chibayuki-pub.s3-ap-northeast-1.amazonaws.com (52.219.68.139) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, close notify (256):
* OpenSSL SSL_connect: SSL_ERROR_ZERO_RETURN in connection to chibayuki-pub.s3-ap-northeast-1.amazonaws.com:443
* Closing connection 0
curl: (35) OpenSSL SSL_connect: SSL_ERROR_ZERO_RETURN in connection to chibayuki-pub.s3-ap-northeast-1.amazonaws.com:443

S3 のエンドポイントが TLS 1.3 に対応していないため、コネクションの時点で失敗します。今回設定したバケットポリシーの評価が行われる前に、そもそもの接続ができないという結果となりました。

ちなみに

HTTP(Sでない)でアクセスを試みたところ、もちろん拒否されました。

curlによるアクセス

% curl -v http://chibayuki-pub.s3-ap-northeast-1.amazonaws.com/test.txt
*   Trying 52.219.4.55:80...
* Connected to chibayuki-pub.s3-ap-northeast-1.amazonaws.com (52.219.4.55) port 80 (#0)
> GET /test.txt HTTP/1.1
> Host: chibayuki-pub.s3-ap-northeast-1.amazonaws.com
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< x-amz-request-id: 8R3X8H6KCGDPDQ3J
< x-amz-id-2: mKtp44QjUtns21lbjVs7MUR1GVPQv240DtgCqPcOqSGbtFO7bmhG7Zu/NnBR+jhuOMTVetUXmDE=
< Content-Type: application/xml
< Transfer-Encoding: chunked
< Date: Wed, 27 Jan 2021 02:59:38 GMT
< Server: AmazonS3
<
<?xml version="1.0" encoding="UTF-8"?>
* Connection #0 to host chibayuki-pub.s3-ap-northeast-1.amazonaws.com left intact
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8R3X8H6KCGDPDQ3J</RequestId><HostId>mKtp44QjUtns21lbjVs7MUR1GVPQv240DtgCqPcOqSGbtFO7bmhG7Zu/NnBR+jhuOMTVetUXmDE=</HostId></Error>

ちなみに 2

TLS バージョンを複数指定したい場合はset 演算子ForAnyValueを付与して以下のように書きます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::chibayuki-pub/*",
            "Condition": {
                "ForAnyValue:StringNotEquals": {
                    "s3:TlsVersion": [
                        "1.2",
                        "1.1"
                    ]
                }
            }
        }
    ]
}

終わりに

Amazon S3 の IAM 条件キーs3:TLSVersionの使い方についてでした。

珍しく公式ドキュメントにサンプルが載っていなかったので、使い方わからん……となりながら手探りで検証しました。

また、TLS バージョンを指定できると何が嬉しいんだ?という部分も初めはピントこず、アクセスパターンを整理していく中でぼんやりと見えてきました。

SecurityHub にも含まれる AWS Config ルール s3-bucket-ssl-requests-onlyでは「HTTP(Sではない)のリクエストが禁止されているか」という観点のチェックが行われますが、s3:TLSVersionと組み合わせることでより踏み込んだ制御ができそうですね。

高いセキュリティレベルが求められる環境で働くあなたの参考になれば幸いです。

以上、千葉(幸)がお送りしました。