AWS CloudFront でプライベートコンテンツを配信したい。 このために Signed Cookie を使う方法があるが、これを golang を使用して生成したい。
CONTENTS
- AWS CloudFront Signed Cookie
- カスタムポリシーを使用した Signed Cookie
- カスタムポリシーを作成する
- カスタムポリシーを base64 エンコードする
- キーペアを使用して署名
- golang で署名
- まとめ
- 参考資料
SOURCES
ENVIRONMENTS
- golang : 1.14.4
AWS CloudFront Signed Cookie
CloudFront でプライベートコンテンツを配信するのはこの記事とこの記事を参考にして特につまづくことなくできた。 この Signed Cookie を生成するパッケージを作るのに苦労したのでまとめておく。
プライベートコンテンツを配信する方法は Signed URL と Signed Cookie の2つの方法が用意されている。 このうち Signed Cookie を選択した。
理由は Cookie を使用する方法なら HttpOnly
にすることで、クライアントがトークンを扱うことなくアクセスできるから。
カスタムポリシーを使用した Signed Cookie
Signed Cookie には、既定のポリシーを使用するものと、カスタムポリシーを使用するものの2つが用意されている。 このうち、カスタムポリシーを使用するほうを選択した。
理由は、既定のポリシーを使用するほうは単一のファイルのみの対応のため。 別なファイルにアクセスするには Cookie を再発行しなければならない。
これだと Cookie を受け取ってからファイルにアクセスするまでに、別なタブとかで新しい Cookie を受け取ってしまうとアクセスに失敗する。 これではうまくいかない。
複数のリソースを取得できるようにするにはカスタムポリシーを使用する必要がある。
カスタムポリシーを作成する
まず、カスタムポリシーを作成する。
{ "Statement": [ { "Resource":"URL of the file", "Condition":{ "DateLessThan":{"AWS:EpochTime":"required ending date and time in Unix time format and UTC"}, "DateGreaterThan":{"AWS:EpochTime":"optional beginning date and time in Unix time format and UTC"}, "IpAddress":{"AWS:SourceIp":"optional IP address"} } } ] }
- Resource : ファイルの URL。ワイルドカードを指定可能
- DateLessThan : ここで指定した Unix time までの間、リソースにアクセスできる
- DateGreaterThan : ここで指定した Unix time から、リソースにアクセスできる
- IpAddress : ここで指定した IP アドレスからのみ、リソースにアクセスできる
以下がカスタムポリシーの例。
{"Statement":[{"Resource":"https://example.getto.systems/*","Condition":{"DateLessThan":{"AWS:EpochTime":1591811666}}}]}
空白や改行の削除は必須なので注意。
カスタムポリシーを base64 エンコードする
さらにこいつを base64 でエンコードする。 そして、以下の文字の置き換えを行わなければならない。
+
→-
(ハイフン)=
→_
(アンダースコア)/
→~
(チルダ)
base64 の url エンコードとは微妙に異なるので注意。 ただ、デコードはできるのでこういう仕様もあるのだろう。
キーペアを使用して署名
base64 でエンコードした文字列を手に入れたら、あとは署名するだけ。
署名にはキーペアのプライベートキーを使用する。 この記事を参考にしてダウンロードしておく。
署名までできたら、Cookie に以下の値を設定する。
- CloudFront-Policy : base64 でエンコードしたポリシー
- CloudFront-Signature : ポリシーを署名した結果
- CloudFront-Key-Pair-Id : 使用したキーベアの ID
これでプライベートコンテンツにアクセスできる。
golang で署名
golang でプライベートキーを扱う方法はこの gist を参考にした。
署名するコードは以下の通り。
import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/pem" "io/ioutil" "log" ) privateKey := ioutil.ReadFile("path/to/key_pair/pk.pem") policy := byte[]("base64-encoded-policy") block, _ := pem.Decode(privateKey) key, err := x509.ParsePKCS1PrivateKey(block.Bytes) if err != nil { log.Fatal(err) } rng := rand.Reader hashed := sha1.Sum(policy) signed, err := rsa.SignPKCS1v15(rng, key, crypto.SHA1, hashed[:]) if err != nil { log.Fatal(err) } // signed : signed cookie
作ったパッケージは GitHub に置いてある。
まとめ
ポリシーの作成方法や base64 エンコードの癖があったために難航したけれど、何とか signed cookie でアクセスできるところまでたどり着けた。