目录
概述
配置存储桶和 Cloud CDN
通过 CDN 访问文件
使用 URL 前缀签名
使用 Cookie 签名
总结
概述
在 Google Cloud 上,使用对象存储 Cloud Storage 作为源站,利用 Cloud CDN 做文件的快速分发和缓存,是一个常见的使用场景。在某些情况下,需要对存储桶上的文件访问进行保护,或者限制 CDN 上缓存的任意访问,可以使用 Cloud CDN 的 URL 签名验证功能(Signed URL)。这样可以实现存储桶只开启私有访问,而通过 CDN 访问存储桶的资源的请求,全都经过签名鉴权才能放行。下面介绍这个功能的示例配置。
配置存储桶和 Cloud CDN
首先创建一个存储桶,在存储类型配置中关闭公开访问(Public Access)。也可以在现有的桶中更改这一配置。

然后在控制台 Network services -> Load balancing 页面点击“CREATE LOAD BALANCER”,创建一个新的 HTTP(S) Load Balancing 以开通 CDN 服务。在其 Backend configuration 中选择创建一个新的 backend bucket,来指向上面创建的存储桶。注意在配置中选择 Enable Cloud CDN,以及选择希望使用的缓存模式。

在同页面的配置中找到 Restricted content 配置,并选中“Restrict access using signed URLs and signed cookies” 来打开 Signed URL 访问。

在“URL signing keys”下面,点击“ADD SIGNING KEY”,来添加一个密钥。

页面上显示的密钥内容,拷贝并保存在一个本地文件。注意此密钥内容只有这一个在控制台查看的机会,如果此时没有保存,后面只能创建新的密钥来使用相应功能。本例保存到以下文件。
/Users/eugeneyu/work/gcp/products/CDN/cdn-key-hk-secure-file
点击保存,并确认 HTTP Load Balancer 的配置里选中刚创建的 Backend Bucket 为后端。

在 LB 的路由规则及前端配置里保留使用缺省配置即可。然后点击创建。

稍作等待,可以观察到刚创建的 Cloud CDN 完成了初始化,并分配了访问 IP。

为了允许 Cloud CDN 访问私有的存储桶文件,还要对 Cloud CDN 的服务账号进行授权。在 Cloud Shell 中运行以下命令。在 Cloud Shell 中运行。
gsutil iam ch serviceAccount:service-247839977271@cloud-cdn-fill.iam.gserviceaccount.com:objectViewer gs://hk-secure |
其中服务账号 service-<PROJECT_NUMBER>@cloud-cdn-fill.iam.gserviceaccount.com 需要将 <PROJECT_NUMBER> 替换成资源所在项目的项目号。项目号可以在控制台上如下位置找到。


通过 CDN 访问文件
在存储桶中上传图片 kitten.png,并尝试在浏览器打开以下地址。
http://34.149.198.174/kitten.png
此时在浏览器中会发访问被阻止。显示的错误信息来自于 Cloud Storage。这是因为,访问请求中不带有 URL 签名,Cloud CDN 不做签名验证而将请求直接发给源站 Cloud Storage,但是 Cloud Storage 不允许不经过 URL 签名验证的请求访问其私有文件,因此拒绝访问。

用以下命令创建一个签名的 CDN 访问链接。
gcloud compute sign-url --key-name cdn-key-hk-secure-1 --expires-in 10m --key-file /Users/eugeneyu/work/gcp/products/CDN/cdn-key-hk-secure-file --validate "http://34.149.198.174/kitten.png" |
另外注意,创建 URL 签名也可以用各种编程语言按照官方算法来实现。
命令执行成功,会返回以下结果。
signedUrl: http://34.149.198.174/kitten.png?Expires=1646711085&KeyName=cdn-key-hk-secure-1&Signature=VPBMQNMitKVWa0Z8V2K4QM5f9BA= validationResponseCode: 403 |
使用返回的签名 URL 来下载文件。可以看到请求成功。
curl -v http://34.149.198.174/kitten.png?Expires=1646711085&KeyName=cdn-key-hk-secure-1&Signature=VPBMQNMitKVWa0Z8V2K4QM5f9BA=
* Trying 34.149.198.174:80... * Connected to 34.149.198.174 (34.149.198.174) port 80 (#0) > GET /kitten.png?Expires=1646711085&KeyName=cdn-key-hk-secure-1&Signature=VPBMQNMitKVWa0Z8V2K4QM5f9BA= HTTP/1.1 > Host: 34.149.198.174 > User-Agent: curl/7.77.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Content-Type: image/png < Age: 162 < Cache-Control: public,max-age=86400
|
使用 URL 前缀签名
Cloud CDN 支持使用 URL 前缀签名,这样签一次名,可以作用于多个同一前缀的文件请求。
可以按以下步骤测试。其中 Python 脚本的命令说明可以查看 Github 上的代码。
wget https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/91cecb2352757abc8fca39a9e353a3f4bfd18f43/cdn/snippets.py
python snippets.py sign-url-prefix http://34.149.198.174/kitten.png http://34.149.198.174/ cdn-key-hk-secure-1 `cat /Users/eugeneyu/work/gcp/products/CDN/cdn-key-hk-secure-file` 1646729745
http://34.149.198.174/kitten.png?URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==&Expires=1646729745&KeyName=cdn-key-hk-secure-1&Signature=BcE2KbFH_D1dT5BmmhlJY3iz1zY= |
利用产生的签名来请求两个不同文件 kitten.png和image1.png。
curl -v http://34.149.198.174/kitten.png?URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==&Expires=1646729745&KeyName=cdn-key-hk-secure-1&Signature=BcE2KbFH_D1dT5BmmhlJY3iz1zY=
curl -v http://34.149.198.174/image1.png?URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==&Expires=1646729745&KeyName=cdn-key-hk-secure-1&Signature=BcE2KbFH_D1dT5BmmhlJY3iz1zY= |
可以在结果中看到,两个文件的请求都成功了。
使用 Cookie 签名
Cloud CDN 支持使用 URL 前缀签名,并将签名放在请求的 Cookie 中进行验证。利用这一签名方式可以方便地为单个用户生成签名并放在用户的会话 Cookie 中,让支持 Cookie 的客户端自动应用签名,也可以保持请求 URL 及其参数不因为签名而变化。
可以按以下步骤测试。其中 Python 脚本的命令说明可以查看 Github 上的代码。
python snippets.py sign-cookie http://34.149.198.174/ cdn-key-hk-secure-1 `cat /Users/eugeneyu/work/gcp/products/CDN/cdn-key-hk-secure-file` 1646816145
Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==:Expires=1646816145:KeyName=cdn-key-hk-secure-1:Signature=3ESxng998sxO7Z9LgBNfpNB4M4k=
curl -v --cookie "Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==:Expires=1646816145:KeyName=cdn-key-hk-secure-1:Signature=3ESxng998sxO7Z9LgBNfpNB4M4k=" http://34.149.198.174/kitten.png
curl -v --cookie "Cloud-CDN-Cookie=URLPrefix=aHR0cDovLzM0LjE0OS4xOTguMTc0Lw==:Expires=1646816145:KeyName=cdn-key-hk-secure-1:Signature=3ESxng998sxO7Z9LgBNfpNB4M4k=" http://34.149.198.174/image2.png |
可以在结果中看到,两个文件的请求都成功了。
总结
对于使用GCS私有桶做源站,用 URL 签名方式访问 Cloud CDN 的方法,我们需要注意以下要点。
如果不使用 Sign URL,CDN 只能访问 bucket 中 public 的文件
如果使用 Sign URL,并且 CDN 的 service account 被正确赋权,CDN 可以访问 bucket 中 private 的文件
在 backend 设置的 expire 期限内,即使 sign URL 变化,仍然可以命中同一份 sign url cache
Cache entry maximum age 最多可以设置为3天,可以用 gcloud 设置--signed-url-cache-max-age 来更改
Response 里面的 Cache-Control 还是遵循源站
如果源站是 GCS,那么不带 URL 前面参数请求会失败