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]