APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

AWS S3 のファイルを社内からのみ URL でダウンロード可能にする(パブリックアクセスブロック有効)

はじめに

こんにちは。クラウド事業部の野本です。

業務でモックサーバを作る際に、静的なファイルをふつうに URL でアクセスしてダウンロードできるようにする必要がありました。この用途に AWS の S3 を使いたいものの、バケットの設定を間違えると全世界に公開されてしまいそうで、公式ドキュメントを調べながら恐る恐る設定しました。

調べた結果、バケットポリシーで適切なアクセス制限を掛けるならパブリックアクセスブロック機能は有効のままでもいいことがわかりました。その設定方法や考え方について纏めます。

設定方法

S3 のオブジェクトを URL 直アクセスでダウンロードできるようにするには、 REST API GetObject を全員に許可するようにバケットポリシーを設定します。
リクエスト元を制限する際にポリシーが「非パブリック」と判定されるよう設定すれば、パブリックアクセスブロック機能はオンのままで構いません。

例えば特定のグローバル IP (会社のネットワークなど)からのみアクセスできるようにするなら、バケットポリシーは以下のようになります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": ["s3:GetObject"],
            "Resource": ["arn:aws:s3:::<bucket-name>/*"],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": ["<IP-address>/32"]
                }
            }
        }
    ]
}
  • Principal を * にして、 AWS アカウントを持っていない人でも REST API を実行できるようにします
    • 安全のため、許可する Action や Resource は必要最小限にします
  • Condition でアクセス制限を掛ける際に、ポリシーが「非パブリック」になるよう条件を調節します
    • ほとんどの場合にアスタリスクは使えません
    • 送信元 IP で制限する場合は、 CIDR を /8 以上に細かくします
    • VPC エンドポイント経由で S3 にアクセスするなら、送信元 VPC などで制限することもできます
    • 正確な条件は S3 の『「パブリック」の意味』を参照してください

設定内容を理解する

「URL でダウンロード」の裏側

今回したいことは、単純にブラウザなどで URL に直アクセスしてファイルを取得することだけです。しかしドキュメントを見ても、 S3 コンソールや REST API などを使う方法しかなく、直アクセスする感じのものが見当たりませんでした。(強いて言えば、「署名付き URL」はブラウザに入力できるようです)
オブジェクトのダウンロード - Amazon Simple Storage Service

悩んで調べ続けた結果、目的の操作は REST API の GetObject を署名なし(未認証)で実行するということだとわかりました。署名がない場合は AWS アカウントを持っていない匿名ユーザとして解釈されます。
アクセス管理の概要 - Amazon Simple Storage Service

したがって、バケットポリシーなどでオブジェクトに対して匿名ユーザのアクセスを許可すれば、 URL 直アクセスでダウンロードできることになります。実態が REST API なので、書き込みなどを許可する権限を不用意に与えないよう注意が必要です。(例えば s3:PutObject を許可するとアップロードできます)


URL でダウンロードするのは S3 バケットを静的ウェブサイトとして利用する場合に重要なので、そちらにはより詳細な設定方法が書いてあります。

ウェブサイトアクセスのアクセス許可の設定 - Amazon Simple Storage Service

バケットを静的ウェブサイトとして設定する場合、このウェブサイトを公開するときは、パブリック読み取りアクセスを許可できます。バケットをパブリックに読み取り可能にするには、バケットのパブリックアクセスブロック設定を無効にし、パブリック読み取りアクセスを許可するバケットポリシーを記述する必要があります。

バケット内のオブジェクトをパブリックに読み取り可能にするには、すべてのユーザーに s3:GetObject アクセス許可を付与するバケットポリシーを作成する必要があります。

S3 のパブリックアクセスブロック設定を編集した後で、バケットへのパブリック読み取りアクセスを許可するバケットポリシーを追加できます。パブリック読み取りアクセスを許可すると、インターネット上のだれでもバケットにアクセスできるようになります。

パブリックアクセスブロック機能との関係

前節に挙げたドキュメントには、パブリックアクセスブロックを無効にするよう書いてあります。実際、ブロックが有効のままでは、ドキュメントにあるバケットポリシーの追加ができません。

しかしパブリックアクセスブロックをよく調べてみると、一定の条件を満たせば、この機能がオンのままでも構わないようでした。
Amazon S3 ストレージへのパブリックアクセスのブロック - Amazon Simple Storage Service

4つの設定についての説明を読むと、 BlockPublicPolicyRestrictPublicBuckets のところに、「パブリックアクセスが許可されている場合」や「パブリックポリシーを持つアクセスポイントやバケットへのアクセスは」といった前置きがあります。パブリックというのは匿名アクセスやインターネットアクセスなどをそのまま指しているわけではなさそうですし、パブリックでなければブロック機能の対象外のようです。

そしてドキュメントには『「パブリック」の意味』がきちんと書いてあります。

バケットポリシーを評価する場合、 Amazon S3 はまずポリシーがパブリックであると想定します。その後、ポリシーを評価して非パブリックとしての資格があるかどうかを判断します。非パブリックと見なすには、バケットポリシーで、次のうち 1 つ以上の固定値(ワイルドカードを含まない値または AWS Identity and Access Management ポリシー変数)にのみアクセスを許可する必要があります。

  • AWS プリンシパル、ユーザー、ロール、またはサービスプリンシパル(例: aws:PrincipalOrgID
  • aws:SourceIp を使用した一連のクラスレスドメイン間ルーティング(CIDR)。 CIDR の詳細については、 RFC Editor のウェブサイトで RFC 4632 を参照してください。

つまり「パブリック」というのはバケットポリシーの評価結果を指しています。そして評価方法によると、 Principal が * (匿名ユーザ含め全員)であっても他の条件で「非パブリック」になることがあります。(ドキュメントには aws:SourceVpc での具体例が載っています)

したがって、バケットポリシーに適切な条件を書き「非パブリック」になれば、パブリックアクセスブロック機能はオンのままでも何も影響しません。

実際に試す

ネットワーク環境の用意

今回は送信元 IP によってダウンロードを制限することを試します。グローバル IP の異なる2つの環境を用意し、 https://ifconfig.io/ などにアクセスして IP を調べておきます。

以降では以下の環境を仮定して進めます。

環境 グローバル IP
会社に VPN 接続している PC 203.0.113.11
会社に VPN 接続していない PC 203.0.113.22

サンプルバケットとオブジェクトの作成

検証用のバケットを作成し、適当なファイルをアップロードしておきます。
※ 安全のため、全世界に公開されても問題ない名前や内容にします。

  • バケット(リージョンと名前以外はデフォルト)
    • 一般的な設定
      • AWS リージョン : アジアパシフィック (東京) ap-northeast-1
      • バケットタイプ : 汎用
      • バケット名 : test-public-a9688bc
    • オブジェクト所有者
      • ✅ ACL 無効 (推奨)
    • このバケットのブロックパブリックアクセス設定
      • ✅ パブリックアクセスをすべてブロック
  • オブジェクト(適当に用意したファイル)
    • 名前 : sample.html
    • タイプ : text/html
<html>
<body>
  <h2>Hello, S3!</h2>
</body>
</html>

アップロードしたオブジェクトを確認すると、概要のところに URL が書いてあります。

しかし URL に直接アクセスしても、この時点では 403 エラーになります。

バケットポリシーの設定

バケットの設定画面を開きます。パブリックアクセスブロックは全てオンにしたままにしておきます。

以下のバケットポリシーを設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": ["s3:GetObject"],
            "Resource": ["arn:aws:s3:::test-public-a9688bc/*"],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": ["203.0.113.11/32"]
                }
            }
        }
    ]
}

すると、指定した IP からは URL にアクセスしてファイルをダウンロードできる(あるいはブラウザ上で見られる)ようになります。

一方で指定していない IP からアクセスすると相変わらず 403 エラーです。

パブリックアクセスブロック機能の確認

前節の状態でバケットポリシーの CIDR を 0.0.0.0/0 などに変えようとすると、ポリシーの更新自体が失敗します。これは、パブリックアクセスブロックのうち3番目の BlockPublicPolicy が、パブリックなポリシーの新規適用を拒否するからです。

また、3番目のチェックを一時的に外して 0.0.0.0/0 に変えられたとしても、今度はファイルへのアクセスができなくなります( IP 制限を緩めたはずなのに)。これは、4番目の RestrictPublicBuckets が、パブリックなポリシーを持つバケットへのクロスアカウントアクセスを拒否するからです。

4番目のチェックを外すと、晴れて 0.0.0.0/0 の条件が効き、どのグローバル IP からでもファイルへアクセスできるようになります。

このように、パブリックアクセスブロック(のうちポリシーを対象とした設定)は、「パブリック」と判定されるポリシーに対して効果を発揮します。逆に言うと、ポリシーが「非パブリック」である限りは何も追加ブロックしません。

まとめ

S3 のファイルを URL でダウンロード可能にする設定について、その背後の意味を含めて調べ、また実際に動作を試してみました。

特にパブリックアクセスブロックは、調べてみると「パブリック」の意味するものが予想と違っていて、今回のようにリクエスト元を制限する事例では有効なままでいいことに驚きました。

パブリックアクセスブロック機能はあくまで防波堤のひとつであり、ポリシーが正確ならブロックを無効化しても安全ですし、逆にポリシーに変な欠陥があればブロックを有効化していてもすり抜けることがあります。とはいえ有効化できるに越したことはないため、今後もポリシーを設定する際は上手に使っていこうと思います。