Vinc3nt's Life

使用 Cloudflare + AWS S3 搭建自己的 HTTPS 圖床

2024-03-24
tools
cloudflare
aws-s3
https
image-hosting
最後更新:2025-01-26
7分鐘
1205字

開始寫部落格後,我對於圖床的要求開始變得急迫。 以前測試過不少第三方圖床,一來免費的服務限制多到不好用,要不就是付費的貴到用不起。 於是,我決定自己來,既可避免圖片遺失的風險(只怕我的信用卡刷爆)。

本來計劃利用 Cloudfront + S3 組合滿足所有需求,但是沒想到 AWS 帳號第一次使用 Cloudfront 需要審核(之前都是用公司的帳號操作)。這讓我不得不暫時轉向 Cloudflare。

幸運的是,使用 Cloudflare 也不失為一個好選擇。我的域名正好由 Cloudflare 管理,其 DNS Records 的 Proxied 功能提供 CDN 服務和自動識別靜態內容的緩存,還能利用 Cloudflare WAF 防止盜連。

接下來,我將分享我的搭建過程,使用域名 example.com 作為示例。

目標

  1. 建立圖床 - AWS S3 Bucket
  2. 透過 Cloudflare:
  • 自定義域名 images.example.com 指向 S3 圖床(DNS Records)
  • 啟用 CDN 緩存以加速訪問(Caching)
  • 實現防盜連功能(WAF)

為什麼要 CDN

Content Distribution Network (CDN) 透過分布在多個地點的節點,優化內容傳輸,減少供應者的頻寬成本,提升使用者的下載速度和系統穩定性。

default - Cloudflare

事實上現在的 CDN 服務所做的優化不只如此,比如 Cloudflare 的 CDN 不僅如此,其節點會根據設定,暫存請求內容,減少對原始服務器的請求次數,進一步優化性能和成本。

思考以下的情境:

  • AWS S3 Bucket 的服務是建立在單一區域的服務,假設我們將 S3 建立在 美國,而在地球另一端的台灣客戶端要訪問這個 S3 的資源,由它所發起的請求就需要跨越半個地球的距離來回交握,及大的增加延遲、降低用戶體驗。CDN 便可優化這一過程。

  • AWS S3 Bucket 是以流量計費,使用者向 S3 請求了一個 1MB 檔案 10 次,流量就是 10MB;如果有 CDN Cache 這份檔案的的情況下,同樣的案例, S3 只會計算 1MB 的流量 ─ 因為 S3 實際上只接收到第一次的請求,剩下的九次請求都會回覆 Cache 的內容。

為什麼要防盜連

除了內容保護的因素外,主要是為了減少不必要的流量消耗。想像一下: 你的部落格中的圖片被他人擅自引用,導致大量請求直接指向你的存儲空間,不僅增加了流量成本,也可能影響服務的穩定性。透過設定防盜連,可以有效控制這一問題。

所以應用防盜連機制是有必要的,目前常見的機制是 referer。Cloudflare WAF 可以簡單設定完成。

default - Cloudflare

搭建步驟

S3

  • 在 S3 創建 Bucket,命名為mybucket,Region 選擇 ap-northeast-1;不需要將 Bucket 公開。
  • 到 Bucket -> 分頁 Properties 啟用 Static website hosting;endopint 應該會是 http://mybucket.s3-website-ap-northeast-1.amazonaws.com
  • 上傳一張測試圖片 test.jpg,確認可以訪問 http://mybucket.s3-website-ap-northeast-1.amazonaws.com/test.jpg

(可選) 因為未來打算只透過 Cloudflare 的域名訪問 s3 bucket 的 endpoint,可以考慮進一步收緊權限。

進入 mybucket -> 分頁 Permissions -> Bucket policy 面板,加入以下 policy:

1
{
2
"Version": "2012-10-17",
3
"Id": "restrict-s3-bucket",
4
"Statement": [
5
{
6
"Sid": "AllowRequestsOnlyFromCloudFlare",
7
"Effect": "Allow",
8
"Principal": "*",
9
"Action": "s3:GetObject",
10
"Resource": "arn:aws:s3:::mybucket/*",
11
"Condition": {
12
"IpAddress": {
13
"aws:SourceIp": [
14
"103.21.244.0/22",
15
"103.22.200.0/22",
47 collapsed lines
16
"103.31.4.0/22",
17
"104.16.0.0/13",
18
"104.24.0.0/14",
19
"108.162.192.0/18",
20
"131.0.72.0/22",
21
"141.101.64.0/18",
22
"162.158.0.0/15",
23
"172.64.0.0/13",
24
"173.245.48.0/20",
25
"188.114.96.0/20",
26
"190.93.240.0/20",
27
"197.234.240.0/22",
28
"198.41.128.0/17"
29
]
30
}
31
}
32
},
33
{
34
"Sid": "ExplicitDeny",
35
"Effect": "Deny",
36
"Principal": "*",
37
"Action": "s3:GetObject",
38
"Resource": "arn:aws:s3:::mybucket/*",
39
"Condition": {
40
"NotIpAddress": {
41
"aws:SourceIp": [
42
"103.21.244.0/22",
43
"103.22.200.0/22",
44
"103.31.4.0/22",
45
"104.16.0.0/13",
46
"104.24.0.0/14",
47
"108.162.192.0/18",
48
"131.0.72.0/22",
49
"141.101.64.0/18",
50
"162.158.0.0/15",
51
"172.64.0.0/13",
52
"173.245.48.0/20",
53
"188.114.96.0/20",
54
"190.93.240.0/20",
55
"197.234.240.0/22",
56
"198.41.128.0/17"
57
]
58
}
59
}
60
}
61
]
62
}

Cloudflare

設定子域名和 CDN

  • 在 DNS Records 中新增一條 CNAME 記錄,指向 S3 Bucket 的 endpoint。
1
type: CNAME
2
name: image
3
content: mybucket.s3-website-ap-northeast-1.amazonaws.com
4
Proxy: proxied
  • 使用 1.1.1.1 Purge 刷新功能確保設定生效。
  • 測試 images.example.com/test.jpg,確認可正常訪問 S3 存儲的資源。

設定 WAF

  • 在 Security -> WAF 中新增一條規則,設定不允許非指定 referer 的請求通過,如不來自 blog.example.com
  • (Options) 在規則中加入另一個 referer=“localhost”,方便本地環境瀏覽測試。
  • 使用 Referrer Policy Test 檢查images.example.com/test.jpg,確認設置了 referrer-policy header
  • 確認用瀏覽器訪問 images.example.com/test.jpg 會被阻擋。

What’s next

這是我目前可用資源中的最佳解決方案,也是最省錢的方案。 等到 AWS 的 Cloudfront 服務可用後,且圖庫流量大到我無法負擔,可能會再重構新的解決方案。

參考

本文標題:使用 Cloudflare + AWS S3 搭建自己的 HTTPS 圖床
文章作者:Vincent Lin
發布時間:2024-03-24