ちょっと話題の記事

MediaPackage v2をUser-Agent HeaderでCDN認証してみた

MediaPackage v2のEndpoint policyでaws:UserAgentの値を基準とするCDN認証をやってみました。ほかの条件をORで追加することも可能で、MediaPackage v1のころよりも柔軟なアクセス制御が行なえます。
2023.06.03

はじめに

清水です。先日リリースされたAWS Elemental MediaPackage v2、これまでよりも低遅延なライブストリーミングが実現可能なLow-Latency HLS (LL-HLS)対応のほか、Policyを用いてよりAWSらしくセキュリティを高めることができるのが魅力です。

ライブオリジンとしてCDN連携時には必須であるEgress側Endpointのセキュリティの保護には、「Endpoint policy」を利用します。MediaPackage v1の個別の設定項目と異なり、IAMなどほかのAWSサービスと同様のJSON形式で記述することができます。

Amazon CloudFrontと連携させるのであれば、CloudFront側にOrigin Access Control (OAC)を設定してMediaPackage v2 Endpoint policyでアクセス許可する、というのがベストな構成に思えます。しかし現時点(2023/06/02)でMediaPackage v2のOAC(もしくはOrigin Access Identity (OAI))対応はされていない認識です。(まだMediaPackage v2がリリースされて日も浅く、CloudFront側のアップデート待ちなのかな、と推測しています。)

MediaPackage v2がまだCloudFront OACに対応してない現状で、CloudFrontからのアクセスをMediaPackage v2で許可させたい場合を考えてみます。MediaPackage v2 User Guide「Origin endpoint authorization」を確認すると、IPアドレスによる許可設定が確認できます。しかしCloudFrontが利用するIPアドレスは膨大な数になり、また可変であるためIPアドレスを基準としてアクセス許可を設定するのは現実的ではありません。ほかに考えられる手段である、MediaPackage v1でも行っていたような指定の文字列をCloudFrontからアクセスする際のCustom headerとして指定、MediaPackage側でこの文字列をチェックする方法を採りたいと思います。

MediaPackage v2側ではEndpoint policyでこの文字列をチェックするかたちです。このEndpoint policyではIAM JSON policyの条件キーが利用できるので、リクエスト時のUserAgent Headerをaws:UserAgentで比較してチェックすることにしました。ちょうどMediaStoreがOACに対応していなかったころのCDN認証のやり方と同じですね。

なお、UserAgent HeaderのほかにもReferer Header(aws:Referer)を利用する方法も取れるかと思います。

MediaPackage v2をUser-Agent HeaderでCDN認証してみた

それでは実際にやっていきます。MediaPackage v2のリソースについては以下エントリで作成したものを使用しました。作成方法については該当ブログエントリをご確認ください。

CDN認証用のコードの作成

まずはCDN認証用のコードを作成します。MediaPackage v1のUser GuideでUUID version 4を利用することを推奨していたことを思い出し、今回もCDN認証コードとしてUUID version 4を生成しました。

% python3
Python 3.9.13 (main, May 24 2022, 21:13:51)
[Clang 13.1.6 (clang-1316.0.21.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import uuid
>>> str(uuid.uuid4())
'6ea0c32c-2a9b-4b3c-9875-dcb7ac4fa2b5'

この6ea0c32c-2a9b-4b3c-9875-dcb7ac4fa2b5を本エントリでは認証コードとして使用します。

MediaPackage v2のEndpoint policyの設定

CDN認証用のコードが作成できたら、続いてMediaPackage v2のOrigin endpointでEndpoint policyを設定します。以下はPublic policyが設定されている状態ですね。

[Edit]ボタンでOrigin endpointの編集画面に進み、Endpoint policyの項目を編集します。「Attach a custom policy」を選択、以下のPolicyを入力しました。ハイライトしている13行めでaws:UserAgentとして先ほど作成した認証コードを指定しています。

{
  "Version": "2012-10-17",
  "Id": "RestrictAccessPolicy",
  "Statement": [
    {
      "Sid": "AllowFromCloudFrontRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "mediapackagev2:GetObject",
      "Resource": "arn:aws:mediapackagev2:ap-northeast-1:123456789012:channelGroup/mediapackage-v2-channel-group/channel/mediapackage-v2-channel/originEndpoint/mediapackage-v2-origin-endpoint",
      "Condition": {
        "StringEquals": {
          "aws:UserAgent": "6ea0c32c-2a9b-4b3c-9875-dcb7ac4fa2b5"
        }
      }
    }
  ]
}

[Update]ボタンでOrigin endpointの設定変更を反映させます。

CloudFrontのCustom headerでUser-Agentを指定

続いてCloudFront側でCustom headerを指定し、先ほど作成した認証コードをUser-Agent HeaderとしてオリジンとなるMediaPackage v2のOrigin endpointにアクセスするように設定します。今回はCloudFront distributionの作成からはじめました。

なお、MediaPackage v2でのCDN利用についてはUser Guideの以下などに注意点などの記載があります。

今回はCDN認証の検証が目的ですので、コンテンツのキャッシュは行わず、またHost header以外をオリジンに転送する設定としました。本番環境で利用する際は適切な設定を行いましょう。

CloudFrontのマネジメントコンソール、[Create distribution]ボタンから進めていきます。Originの「Origin domain」の項目にはMediaPackage v2 Origin endpointのManifest settingsタブで確認できるmanifestのURLのドメインを入力します。(MediaPackage Containerとして選択できればよかったのですが、まだMediaPackage v2のリソースは表示されないようでした。)「Protocol」の項目は「HTTPS only」になっているいることを確認しましょう。

「Add custom header」の項目で、CloudFrontが指定のでUser-Agent HeaderでオリジンとなるMediaPackage v2にアクセスするように設定します。[Add header]ボタンを謳歌して入力フィールドを出現させ、「Header name」にはUser-Agentを入力します。「Value」は先ほどの作成した認証コードである6ea0c32c-2a9b-4b3c-9875-dcb7ac4fa2b5を入力します。

その他の項目として「Cache key and origin requests」では、「Cache policy」としてCachingDisabledを、また「Origin request policy」ではAllViewerExceptHostHeaderを選択し、Distributionを作成しました。

User-Agent HeaderによるCDN認証の確認

CloudFront distributionが作成できたら、実際にMediaLiveからMediaPackageに映像を送信し、MediaPackageのEndpointにアクセスしてみましょう。MediaLiveの設定は以前のエントリ同様の設定を行いました。

まずはOrigin endpointのmanifest URLに直接アクセスしてみましょう。HLS manifestとLow-latency HLS manifestの2つがあり、どちらにアクセスしても結果は同様となりますが、今回はHLS manifestでの検証結果をまとめてみます。

Endpoint policyでCloudFrontを経由しない(指定したUser-Agent Headerを持たない)アクセスは許可していないので、この場合は403が返ります。

 % curl -i https://xxxxxx.egress.xxxxxx.mediapackagev2.ap-northeast-1.amazonaws.com/out/v1/mediapackage-v2-channel-group/mediapackage-v2-channel/mediapackage-v2-origin-endpoint/hls-index.m3u8
HTTP/2 403
date: Fri, 02 Jun 2023 14:01:24 GMT
content-type: application/json
content-length: 28
x-amzn-requestid: 7cd9xxxx-xxxx-xxxx-xxxx-xxxxxxxx9eec
access-control-allow-origin: *
x-amzn-errortype: AccessDeniedException
access-control-expose-headers: x-amzn-requestid,x-amzn-errortype,x-amzn-mediapackage-last-sequence,x-amzn-mediapackage-last-updated,Date
access-control-allow-credentials: true

{"Message":"Access denied."}

マネジメントコンソールのPrevewボタンから視聴を確認してみても、Playerでは「ぐるぐるマーク」が表示されるだけでライブストリーミングの視聴はできませんね。Errorの項目には403でマニフェストファイルがロードできない旨が記載されています。

続いてCloudFront経由でアクセスしてみます。先ほど作成したCloudFront DistributionのDomain nameを確認しましょう。

このDomain nameでmanifest URLのドメイン部分を書き換えます。

  • 書き換え前
    • https:// xxxxxx.egress.xxxxxx.mediapackagev2.ap-northeast-1.amazonaws.com /out/v1/mediapackage-v2-channel-group/mediapackage-v2-channel/mediapackage-v2-origin-endpoint/hls-index.m3u8
  • 書き換え後
    • https:// d3f9xxxxxxxxxx.cloudfront.net /out/v1/mediapackage-v2-channel-group/mediapackage-v2-channel/mediapackage-v2-origin-endpoint/hls-index.m3u8

それではCloudFront経由でmanifest URLにアクセスしてみます。無事にアクセスができ、ステータスコード200が返りましたね。

% curl -i https://d3f9xxxxxxxxxx.cloudfront.net/out/v1/mediapackage-v2-channel-group/mediapackage-v2-channel/mediapackage-v2-origin-endpoint/hls-index.m3u8
HTTP/2 200
content-type: application/vnd.apple.mpegurl
content-length: 382
date: Fri, 02 Jun 2023 14:09:46 GMT
x-amzn-requestid: 407axxxx-xxxx-xxxx-xxxx-xxxxxxxxaccc
x-amzn-mediapackage-endpoint-id: mediapackage-v2-origin-endpoint
access-control-allow-origin: *
x-amzn-mediapackage-channel-uniqueid: f310xxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amzn-mediapackage-endpoint-uniqueid: 2a85xxxxxxxxxxxxxxxxxxxxxxxxxxxx
cache-control: max-age=3
access-control-expose-headers: x-amzn-requestid,x-amzn-errortype,x-amzn-mediapackage-last-sequence,x-amzn-mediapackage-last-updated,Date
x-amzn-mediapackage-active-input: 1
x-amzn-mediapackage-channel-id: mediapackage-v2-channel
access-control-allow-credentials: true
x-cache: Miss from cloudfront
via: 1.1 0a1cxxxxxxxxxxxxxxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)
x-amz-cf-pop: KIX50-P2
x-amz-cf-id: 1KMHxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxqA==

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:CODECS="avc1.640028,mp4a.40.2",AVERAGE-BANDWIDTH=5711200,RESOLUTION=1920x1080,VIDEO-RANGE=SDR,FRAME-RATE=30.0,BANDWIDTH=5931200
hls-variant_1.m3u8
#EXT-X-STREAM-INF:CODECS="avc1.64001F,mp4a.40.2",AVERAGE-BANDWIDTH=3511200,RESOLUTION=1280x720,VIDEO-RANGE=SDR,FRAME-RATE=30.0,BANDWIDTH=3643198
hls-variant_2.m3u8

ライブストリーミングの視聴も確認してみましょう。マネジメントコンソールのPrevewボタンで遷移できるページから、参照するURL部分を書き換えます。

無事に再生ができました!

特定のIPだけはCDNを経由しなくてもアクセス可能にしてみる

MediaPackage v2のEndpoint policyにUser-Agent HeaderでCDN認証するよう設定し、CloudFrontからのみOrigin endpointへのアクセスを許可するよう設定してみました。

ここではもうひとつ、User-Agent HeaderでCDN認証をしつつ、特定のIPアドレスからはOrigin endpointへのアクセスを許可するよう設定してみます。

Endpoint policyを以下のように書き換えましょう。ハイライト部分が追加したStatementです。User-Agent HeaderによるCDN認証は残しつつ、「OR」条件で特定のIPアドレスからのアクセスを許可するので、Statement要素を追加しているかたちですね。

{
  "Version": "2012-10-17",
  "Id": "EndpointPolicy",
  "Statement": [
    {
      "Sid": "AllowFromCloudFrontRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "mediapackagev2:GetObject",
      "Resource": "arn:aws:mediapackagev2:ap-northeast-1:123456789012:channelGroup/mediapackage-v2-channel-group/channel/mediapackage-v2-channel/originEndpoint/mediapackage-v2-origin-endpoint",
      "Condition": {
        "StringEquals": {
          "aws:UserAgent": "6ea0c32c-2a9b-4b3c-9875-dcb7ac4fa2b5"
        }
      }
    },
    {
      "Sid": "AllowFromSpecifiedIPAddress",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "mediapackagev2:GetObject",
      "Resource": "arn:aws:mediapackagev2:ap-northeast-1:123456789012:channelGroup/mediapackage-v2-channel-group/channel/mediapackage-v2-channel/originEndpoint/mediapackage-v2-origin-endpoint",
      "Condition": {
        "IpAddress":{
          "aws:SourceIp": "xxx.xxx.xxx.xxx/32"
        }
      }
    }
  ]
}

指定したIPアドレスからは直接Origin endpointのmanifest URLにアクセスすることができました。

% curl -i https://xxxxxx.egress.xxxxxx.mediapackagev2.ap-northeast-1.amazonaws.com/out/v1/mediapackage-v2-channel-group/mediapackage-v2-channel/mediapackage-v2-origin-endpoint/hls-index.m3u8
HTTP/2 200
date: Fri, 02 Jun 2023 14:32:07 GMT
content-type: application/vnd.apple.mpegurl
content-length: 382
x-amzn-requestid: 779exxxx-xxxx-xxxx-xxxx-xxxxxxxxc435
x-amzn-mediapackage-endpoint-id: mediapackage-v2-origin-endpoint
access-control-allow-origin: *
x-amzn-mediapackage-channel-uniqueid: f310xxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amzn-mediapackage-endpoint-uniqueid: 2a85xxxxxxxxxxxxxxxxxxxxxxxxxxxx
cache-control: max-age=3
access-control-expose-headers: x-amzn-requestid,x-amzn-errortype,x-amzn-mediapackage-last-sequence,x-amzn-mediapackage-last-updated,Date
x-amzn-mediapackage-active-input: 1
x-amzn-mediapackage-channel-id: mediapackage-v2-channel
access-control-allow-credentials: true

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:CODECS="avc1.640028,mp4a.40.2",AVERAGE-BANDWIDTH=5711200,RESOLUTION=1920x1080,VIDEO-RANGE=SDR,FRAME-RATE=30.0,BANDWIDTH=5931200
hls-variant_1.m3u8
#EXT-X-STREAM-INF:CODECS="avc1.64001F,mp4a.40.2",AVERAGE-BANDWIDTH=3511200,RESOLUTION=1280x720,VIDEO-RANGE=SDR,FRAME-RATE=30.0,BANDWIDTH=3643198
hls-variant_2.m3u8

なお、MediaPackage v1ではこのような「OR」条件によるアクセス制限はできませんでした。CDN認証用のカスタムヘッダX-MediaPackage-CDNIdentifierを用いたアクセス制限は行なえますが、これとIPアドレスのアクセス許可リストは「AND」条件となります。CloudFrontからのアクセスを許可しつつ特定のIPアドレスによるアクセスも許可する、ということを考えた場合、IP allowlistにCloudFrontが利用する膨大な数のIPアドレスを追加する必要があります。いくつのIPアドレスやCIDRブロックを追加できるのかはわかりませんが、先に述べた通りCloudFrontの利用するIPアドレスが可変でもあることから、現実問題として実現は難しかったのかと思います。

まとめ

AWS Elemental MediaPackage v2でUser-Agent Headerを利用したCDN認証を行い、Origin endpointのURLにはCustom headerを設定したCloudFront distributionからしかアクセスを許可しないよう設定してみました。Endpoint policyを使い、AWS利用者であれば慣れ親しんでいるJSONでの記述でアクセス許可を設定することができます。また特定のIPアドレスだけは直接Origin EndpointのURLにアクセス可能にする、といったことも可能です。MediaPackage v1のころよりも柔軟なセキュリティ設定が容易に行なえます。

なお冒頭にも記載しましたが、理想的なかたちはCloudFrontのOACがMediaPackage v2で利用できるようになることと考えます。今回のようなCustom headerによる認証だと、例えば認証のローテーションなどが大変ですよね。AWS Elemental MediaStoreがOACに対応していることを考えると、きっとMediaPackage v2もOACに対応してくれるだろうと思います。しかし取り急ぎMediaPackage v2をCloudFrontとともに利用する場合は、このようなCDN認証を設定しておきましょう。