整合 Google 登入至 Strapi:在 Kubernetes 上解決「Secure Cookie over Unencrypted Connection」的實戰紀錄

前言 在現代 Web 應用開發中,提供第三方登入(Social Login)已經成為標準配備。相比傳統的帳號密碼註冊流程,使用 Google、Facebook、GitHub 等服務登入不僅能降低使用者註冊門檻,還能提升安全性(由大廠處理密碼儲存與驗證)。 當我們決定為 Strapi CMS 後台加入 Google OAuth 登入時,原本以為只是個簡單的設定任務: 在 Google Cloud Console 建立 OAuth 2.0 憑證 在 Strapi 填入 Client ID 和 Client Secret 點擊「Login with Google」按鈕,完成! 但現實總是更複雜。當應用部署到 Kubernetes 叢集後,我們遇到了一個令人困惑的錯誤訊息: Error: Cannot send secure cookie over unencrypted connection 這個錯誤訊息背後,牽涉到 HTTP/HTTPS 協定、Proxy Trust 機制、Kubernetes Ingress 架構、以及瀏覽器 Cookie 安全策略等多層知識。這篇文章將完整記錄我如何一步步拆解問題、理解根本原因、並最終在生產環境中實現安全可靠的 Google 登入功能。 OAuth 2.0 授權碼流程基礎 在深入問題之前,我們先理解 Google OAuth 登入的完整流程。OAuth 2.0 提供了多種授權模式(Grant Types),而 Web 應用最常使用的是「授權碼模式(Authorization Code Flow)」。 這個流程中有幾個關鍵點: ...

May 16, 2025 · 13 分鐘 · Peter

解決 Strapi CMS 正式環境空白頁的踩坑經驗分享

前言:一個簡單的環境變數引發的災難 在部署 Strapi CMS 到 Kubernetes 正式環境時,只是加了一行看似無害的環境變數設定: env: - name: NODE_ENV value: production # 就是這一行! 結果卻導致整個管理後台變成一片空白,連登入頁面都看不到。更詭異的是: ✅ API 完全正常,GraphQL 和 REST 都能回應 ✅ Pod 狀態正常,沒有任何錯誤訊息 ✅ 日誌顯示 Strapi 成功啟動 ❌ 瀏覽器打開 /admin 卻是一片空白 這種「Schrodinger 的服務」(同時正常又不正常)讓人抓狂。經過一番排查,終於發現罪魁禍首是 CSP (Content Security Policy) 在作怪。 本文將深入探討: 為什麼正式環境會出現空白頁 CSP 的工作原理與安全機制 完整的問題排查步驟 如何正確配置 Strapi 的安全策略 生產環境的安全最佳實踐 問題背景:開發正常,正式環境空白 環境差異對比 問題現象詳細描述 Kubernetes Deployment 設定: apiVersion: apps/v1 kind: Deployment metadata: name: strapi-prod namespace: default spec: replicas: 1 selector: matchLabels: app: strapi template: metadata: labels: app: strapi spec: containers: - name: strapi image: myregistry.com/strapi:v5.0.0 env: - name: NODE_ENV value: production # 問題的起點 - name: DATABASE_HOST valueFrom: secretKeyRef: name: strapi-db-secret key: host - name: ADMIN_JWT_SECRET valueFrom: secretKeyRef: name: strapi-admin-secret key: jwt-secret ports: - containerPort: 1337 部署後的症狀: ...

May 7, 2025 · 8 分鐘 · Peter

解決 Kubernetes 多餘 Pod 問題與 CrashLoopBackOff 的實戰心得

前言:一次神秘的 Pod 複製事件 在一次例行的 Strapi CMS 更新部署到 AWS EKS 時,遇到了一個詭異的現象:明明 Deployment 設定檔中清楚寫著 replicas: 1,但實際運行的 Pod 卻有兩個!更奇怪的是,其中一個 Pod 持續處於 CrashLoopBackOff 狀態,而另一個則正常運行。 無論怎麼刪除多餘的 Pod,它總是會像打不死的蟑螂一樣再次出現。這種「靈異事件」讓我開始懷疑 Kubernetes 是不是有自己的想法… 本文將深入探討: 為什麼會出現多餘的 Pod CrashLoopBackOff 背後的機制 Kubernetes Deployment 和 ReplicaSet 的運作原理 實戰排查步驟與解決方案 Secret 編碼陷阱與預防措施 問題背景:Deployment 說一個,實際卻有兩個 問題現象 預期行為: # my-strapi-prod-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-strapi-prod spec: replicas: 1 # 只要 1 個 Pod selector: matchLabels: app: my-strapi-prod 實際情況: $ kubectl get pods -n default | grep my-strapi-prod NAME READY STATUS RESTARTS AGE my-strapi-prod-7d4b5c8f9d-x2k4p 1/1 Running 0 10m my-strapi-prod-8c9a6d7e5f-w8n2q 0/1 CrashLoopBackOff 5 5m 兩個 Pod 同時存在,卻只有一個正常運行! ...

May 6, 2025 · 8 分鐘 · Peter

EKS Pod 卡在 Pending?從 Too Many Pods 到 ENI / CNI 限制全面解析

前言:一個讓人懷疑人生的 Pending 狀態 最近部署 Strapi CMS 到 AWS EKS 時,遇到一個詭異的情況: $ kubectl get pods -n default NAME READY STATUS RESTARTS AGE mycompany-strapi-prod-695854fbd4-dzw66 0/1 Pending 0 3h42m 一個 Pod 卡在 Pending 狀態超過三小時,CPU 和 Memory 明明還很充足,但就是起不來。 如果你曾經盯著 kubectl get pods 看著那個永遠不會變成 Running 的 Pending 狀態,同時懷疑是不是 Kubernetes 在跟你開玩笑——恭喜你,你不孤單。 在嘗試了 Google 前五個搜尋結果、檢查了三次 YAML 設定、並認真考慮是否該轉行當咖啡師之後,我終於找到了問題的根源… ⚠️ 劇透警告:問題的根源不是 CPU、不是 Memory,而是一個你可能從沒注意過的限制——網卡(ENI)和 IP 數量。 問題診斷:一步步找出真兇 Step 1:查看 Pod 事件 遇到 Pending 狀態,第一步當然是看看 Kubernetes 到底在抱怨什麼: $ kubectl describe pod mycompany-strapi-prod-695854fbd4-dzw66 -n default 輸出內容很長,但最重要的是 Events 區塊: ...

June 15, 2024 · 10 分鐘 · Peter

全端專案 AWS EKS 雲端架構深度解析

前言:從本地開發到雲端生產環境 本文將深入解析一個全端專案在 AWS 上的完整基礎設施架構,展示如何透過 Kubernetes (EKS) 實現高可用性、可擴展性和成本效益的生產環境。這個平台提供線上課程、預約服務、會員管理和金流整合等功能。 本文涵蓋內容: 完整的 AWS EKS 叢集架構 Strapi CMS 和 Vue.js 前端的容器化部署 Jenkins CI/CD 自動化部署流程 Ingress NGINX 負載均衡和 SSL 憑證管理 與 AWS RDS、S3、ECR 的整合 第三方服務整合 (Firebase FCM、台灣金流) 監控與日誌管理 架構概覽 整體架構圖 核心技術棧 基礎設施層: AWS EKS 1.32.9 (Kubernetes 託管服務) AWS EC2 (ARM64 架構 - t4g.medium) AWS RDS PostgreSQL (託管資料庫) AWS S3 (物件儲存) AWS ECR (容器映像倉儲) AWS ELB (負載均衡器) CI/CD 層: Jenkins (Mac mini 本地部署) Docker (容器建置) kubectl (Kubernetes 部署工具) 應用層: ...

June 5, 2024 · 10 分鐘 · Peter

搜尋目錄下每一個檔案的特定字/檔案名稱

在開發過程中,我們常需要快速找出「哪個檔案用到了這個變數?」或是「那個設定檔到底放在哪裡?」。 本文整理了 Linux/Mac 終端機中最常用的搜尋指令:grep、ag 和 find。 1. 搜尋檔案內容 (grep) grep 是系統內建最強大的文字搜尋工具。 常用指令 # 在目前目錄 (./) 遞歸搜尋 "R2" grep -rwn --color=auto ./ -e 'R2' 參數詳解 -r (recursive):遞歸搜尋,包含所有子目錄。 -w (word):僅匹配「完整單字」,避免匹配到部分字串(例如搜尋 is 不會匹配到 this)。 -n (line number):顯示行號。 -e (pattern):指定搜尋的關鍵字。 --color=auto:將關鍵字高亮顯示。 顯示前後文 (Context) 有時候只看一行不夠,我們需要看前後幾行程式碼: # -C (Context): 顯示前後各 1 行 grep -rwn -C 1 ./ -e 'R2' # -A (After): 顯示後 1 行 # -B (Before): 顯示前 1 行 2. 更快的選擇:The Silver Searcher (ag) grep 雖然強大,但速度較慢且指令較長。ag 是一個專為程式碼搜尋設計的工具,速度極快且預設忽略 .gitignore 的檔案。 (需額外安裝:brew install the_silver_searcher) ...

September 23, 2023 · 1 分鐘 · Peter

iOS如何把專案打包framwork後也讓原本的pod/套件包入framework ?

工作需要把整個app包成一個SDK讓另一個app引用,所以要把原本專案的套件也包進去,不然在打包framework的時候,import套件那一行會報錯. iOS如何把專案打包framwork後也讓原本的pod/套件包入framework ? 工作需要把整個app包成一個SDK讓另一個app引用,所以要把原本專案的套件也包進去,不然在打包framework的時候,import套件那一行會報錯. 假設你的專案叫”B”,打包的framework取名為BPack,那麽在Podfile的部分要加入framework自己的套件引用,原本的專案裝什麼pod,framework如果有用到該套件也要跟著裝,以SnapKit為例,作法如下: target ‘B’ do Comment the next line if you don’t want to use dynamic frameworks use_frameworks! pod ‘SnapKit’, ‘~> 5.6.0’ Pods for B end target ‘BPack’ do Comment the next line if you don’t want to use dynamic frameworks use_frameworks! pod ‘SnapKit’, ‘~> 5.6.0’ Pods for B Pods for BPack end另外,在Build Setting的部分好像也要做一下設定: 如果你包的是動態庫,那麽在framework target的Build Settings: Linking -> Mach-O Type應該要選擇:Dynamic Libray Mach-O這部分不太確定有沒有影響,不過我這樣選是可以Run App Successful. 有錯麻煩請指正,感謝! ...

September 22, 2023 · 1 分鐘 · Peter