Cloud Storageに格納したファイルをCDNで配信する時にアクセスを限定したい場合があります
その際に署名付きCookie、署名付きURLが使えます
設定自体はterraformなどで、自動で設定して置けるようにすると楽です
resource "google_storage_bucket" "public-images" { project = var.project_id name = "public-images-${var.project_id}" location = "asia-northeast1" force_destroy = true cors { origin = ["https://${var.domain}"] method = ["GET"] response_header = ["*"] } } resource "google_storage_bucket_iam_member" "signed-url-user" { bucket = google_storage_bucket.public-images.name role = "roles/storage.objectViewer" member = "serviceAccount:service-${var.project_number}@cloud-cdn-fill.iam.gserviceaccount.com" depends_on = [ google_compute_backend_bucket_signed_url_key.backend_key ] } resource "google_compute_backend_bucket" "cdn-backend-bucket" { project = var.project_id name = "backend-${google_storage_bucket.public-images.name}" bucket_name = "public-images-${var.project_id}" enable_cdn = true } resource "random_id" "url_signature" { byte_length = 16 } resource "google_compute_backend_bucket_signed_url_key" "backend_key" { project = var.project_id name = "public-backend-bucket-key" key_value = random_id.url_signature.b64_url backend_bucket = google_compute_backend_bucket.cdn-backend-bucket.name } resource "google_compute_url_map" "cdn" { project = var.project_id name = "public-images-cdn" default_service = google_compute_backend_bucket.cdn-backend-bucket.id } resource "google_compute_target_https_proxy" "cdn-proxy" { project = var.project_id name = "proxy-${var.project_id}" url_map = google_compute_url_map.cdn.id ssl_certificates = [google_compute_managed_ssl_certificate.tls.id] } resource "google_compute_managed_ssl_certificate" "tls" { project = var.project_id provider = google-beta name = "public-images-${var.project_id}" managed { domains = [var.domain] } } resource "google_compute_global_forwarding_rule" "cdn" { project = var.project_id name = "public-images-${var.project_id}" target = google_compute_target_https_proxy.cdn-proxy.self_link port_range = 443 }
生成された鍵はSecretManagerなどに登録しておくといいと思います
署名付きCookie
署名付きCookieは署名付きリクエストの鍵を使ってCookieに署名します
生成した値はCloud-CDN-Cookieに指定して利用できます
import crypto from 'crypto'; import dayjs from 'dayjs'; export const ENCODED_URL_PREFIX = (Buffer.from(process.env.URL_PREFIX)) .toString("base64") .replace(/\+/g, "-") .replace(/\//g, "_"); export const signedCookie = (seconds: number) => { const unixTimestamp = dayjs().add(seconds, 'seconds').unix(); const input = `URLPrefix=${process.env.ENCODED_URL_PREFIX}:Expires=${unixTimestamp}:KeyName=${process.env.SIGNED_URL_KEY_NAME}`; const keyValue = Buffer.from(process.env.SIGNED_URL_KEY_VALUE, 'base64') const signature = crypto.createHmac('sha1', keyValue) .update(input) .digest('base64') .replace(/\+/g, "-") .replace(/\//g, "_"); return `${input}:Signature=${signature}`; }
署名付きCookieの場合はCookieのパスの指定ミスなどで期待通りブラウザがCookie送ってくれなかったり、原因の切り分けを早くできた方がいいので、 実装が済んでしまったら先に正しい署名がCookieについているか確認しておくと良いと思います
curl --cookie='Cloud-CDN-Cookie=[生成した値]' \ -D dump-headers.txt \ --output=example.jpg \ https://cdn.example.com/example.jpg
署名付きURL
署名付きURLはURLに対して署名を付加できるものです
Cookieと違って対象のURLが多い場合面倒ですが、実装自体は簡単なので導入しやすいです
import crypto from 'crypto'; import dayjs from 'dayjs'; export const signedURL = (url: string, seconds: number) => { const unixTimestamp = dayjs().add(seconds, 'seconds').unix(); const segments = [ `?Expires=${unixTimestamp}`, `&KeyName=${process.env.SIGNED_URL_KEY_NAME}` ]; const signURL = `${url}${segments.join('')}` const keyValue = Buffer.from(process.env.SIGNED_URL_KEY_VALUE, 'base64') const signature = crypto.createHmac('sha1', keyValue) .update(signURL) .digest('base64') .replace(/\+/g, "-") .replace(/\//g, "_"); segments.push(`&Signature=${signature}`); const singedQuery = segments.join(''); return `${url}${singedQuery}`; }
こちらも実装が済んだら検証しておきます
curl --output=example.jpg [署名付きURL]