PubliclyAccessible=true 不等於對外曝露:同一台機器,RDS 連不到、kubectl 卻通

起因:一個旗標,讓 AI 一秒喊出了錯的資安結論 那天清掉一顆「砍了 EC2(Elastic Compute Cloud,AWS 的雲端虛擬機器)卻忘了收」的閒置 Elastic IP(彈性 IP — AWS 可保留、可重複指派的固定公網位址)後,我順手請我的 AI 助手盤點帳號還開著哪些服務。掃到一台正式環境的 RDS(Relational Database Service,AWS 託管的關聯式資料庫)PostgreSQL,設定裡寫著 PubliclyAccessible: true。 AI 第一個反應就回我:「正式資料庫直接掛公網,有資安風險。」這句話聽起來很合理——但它是錯的。真相要等到實際讀了 Security Group 規則、再從本機跑一次連線測試才浮現。 這篇文章講的就是這個落差:一個旗標的字面意思,跟它實際造成的曝露,是兩回事。 也順帶示範——在「AI 已經會幫你讀設定、寫指令」的年代,為什麼這種誤判依然會發生。 先說清楚:Security Group 是什麼 Security Group(安全群組,以下簡稱 SG)是 AWS 的虛擬防火牆,掛在資源的網路介面 ENI(Elastic Network Interface,彈性網路介面)上。它有三個關鍵性質: 預設全部拒絕:沒有明確放行的流量一律擋掉。你只能「加白名單」,沒有「黑名單」這種東西。 來源可以是 IP 網段,也可以是另一個 SG:後者意思是「凡是掛了某個 SG 的資源一律放行」,不必管它的 IP 是多少——這在 VPC(Virtual Private Cloud,虛擬私有雲)內部互連時非常好用。 stateful(狀態感知 — 放行了進來的連線,回程流量自動允許):不必另外開一條出站規則。 SG 的逐步設定我在 如何使用 psql 連線 AWS RDS 那篇有完整圖解(「SG 就像 RDS 的防火牆」)。這裡只談它跟「公開存取」旗標之間,那個最容易被誤會的互動。 旗標的陷阱:PubliclyAccessible=true 只給門牌,不給鑰匙 關鍵誤解就在這裡。PubliclyAccessible 這個旗標只決定一件事:RDS 要不要拿到一個公網 IP,以及一個可從網際網路解析的 DNS(網域名稱)。它完全不決定「誰連得進來」。 ...

June 25, 2026 · 3 分鐘 · Peter

EKS 維運的三個隱形陷阱:工具 Pod 消失、事件蒸發、審計空白

起因:工具 Pod 人間蒸發 某天要查資料庫,照慣例執行 kubectl exec -it psql-client,結果 Pod 不見了。 這個 psql-client 是用來連 RDS PostgreSQL 的工具容器,平常拿它跑 SQL 查詢、檢查資料表大小。問題是——沒有人記得刪過它,也沒有任何記錄顯示是誰或什麼原因讓它消失。 這件事本身影響不大,重建一個就好。但它暴露了三個更深層的問題:為什麼 Pod 會無聲消失?為什麼查不到是誰刪的?為什麼整個叢集沒有留下任何操作軌跡? 陷阱一:裸 Pod 沒有人管它的死活 問題本質 當初建立 psql-client 的指令大概是這樣: kubectl run psql-client --image=postgres:15-alpine --restart=Never -- sleep infinity 這行指令建立的是一個裸 Pod(Bare Pod)——直接建立 Pod 物件,不隸屬於任何 Deployment、ReplicaSet 或 StatefulSet。 裸 Pod 和 Deployment 管理的 Pod,差別在於有沒有 Controller 在背後看著它。Deployment 的 ReplicaSet Controller 會持續監控 Pod 數量,少一個就補一個。裸 Pod 沒有 Controller,它的生死完全取決於節點的命運。 flowchart TD subgraph 節點故障發生 A[Node 異常關機] end A --> B[裸 Pod] A --> C[Deployment Pod] B --> D[Pod 永久消失] D --> E[無人知曉] C --> F[ReplicaSet Controller 偵測] F --> G[在其他 Node 重建 Pod] G --> H[服務自動恢復] classDef default fill:#f5f5f5,stroke:#333,color:#333 classDef error fill:#f8d7da,stroke:#dc3545,color:#721c24 classDef success fill:#d4edda,stroke:#28a745,color:#155724 classDef info fill:#d1ecf1,stroke:#17a2b8,color:#0c5460 class A info class D,E error class G,H success 不處理會怎樣 裸 Pod 在以下情境會永久消失: ...

May 16, 2026 · 3 分鐘 · Peter

AWS S3 Upload Failed: The Bucket Does Not Allow ACLs

問題發生 在將 Production 環境複製到 Staging 環境後,發現 Strapi CMS 無法上傳圖片到媒體庫,畫面只顯示 Internal Server Error。 Strapi 是一個開源的 Headless CMS(無頭內容管理系統),可以讓開發者快速建立 API,並提供管理後台來管理內容。在這個專案中,我們使用 Strapi 搭配 AWS S3 來儲存上傳的圖片和檔案。 追查過程 第一步:查看 Kubernetes Logs 由於 Strapi 部署在 EKS(Elastic Kubernetes Service,AWS 的託管 Kubernetes 服務)上,我透過 kubectl 指令查看 Pod 的日誌: kubectl logs -f deployment/strapi-stg --tail=100 kubectl 是 Kubernetes 的命令列工具,用來與 Kubernetes 叢集互動。logs 指令可以查看容器的輸出日誌。 第二步:找到錯誤訊息 在日誌中發現關鍵錯誤: error: The bucket does not allow ACLs AccessControlListNotSupported: The bucket does not allow ACLs 這個錯誤訊息指出 S3 Bucket(AWS 的物件儲存服務中的儲存桶)不允許使用 ACL。 ...

January 30, 2026 · 3 分鐘 · Peter

AWS 跨區域遷移後的技術債清理:Strapi URL 的隱藏陷阱

接手專案,先看帳單 因為老闆信用卡到期了要換新卡,我順便看了一下 AWS 帳單金額,發現比預期高。之前詢問外包商技術長(已離職),得到的回覆是:「服務都已經從新加坡遷移到台北了,除了 S3 有保留做備份,其他都刪除了。」 身為工程師,最不能接受的就是「應該是這樣」。我決定親自盤點。 名詞解釋 在繼續之前,先解釋一下會提到的 AWS 服務: 服務 說明 費用特性 S3 (Simple Storage Service) 物件儲存服務,用來存放檔案、圖片、影片 按儲存容量和請求次數計費 NAT Gateway 讓私有子網路的資源能存取網際網路 按小時計費,即使沒流量也要錢 Elastic IP 固定的公開 IP 位址 使用中免費,未關聯則收費 VPC (Virtual Private Cloud) 虛擬私有網路,隔離你的雲端資源 VPC 本身免費,但相關資源收費 Network Load Balancer 負載平衡器,分散流量到多台伺服器 按小時和處理的資料量計費 ECR (Elastic Container Registry) Docker 映像檔儲存庫 按儲存容量計費 重點是:有些資源即使沒有流量,只要存在就會收費。NAT Gateway 和未關聯的 Elastic IP 就是典型的「隱形殺手」。 盤點遺留資源 # 檢查 EKS 叢集(Kubernetes 服務) aws eks list-clusters --region ap-southeast-1 # 結果:空的 ✓ # 檢查 RDS(資料庫) aws rds describe-db-instances --region ap-southeast-1 # 結果:空的 ✓ # 檢查 NAT Gateway aws ec2 describe-nat-gateways --region ap-southeast-1 \ --filter "Name=state,Values=available" # 結果:2 個還在跑 完整盤點結果: ...

January 10, 2026 · 3 分鐘 · Peter

在 Kubernetes 上部署 OV SSL 證書:完整實戰指南

為什麼選擇 TWCA OV 證書 在生產環境中,使用自簽憑證會導致瀏覽器顯示不安全警告,影響使用者信任。雖然 Let’s Encrypt 提供免費的自動化證書,但某些企業或政府專案需要台灣在地的認證機構簽發證書以符合法規要求。 SSL 證書的三個等級 SSL 證書依據驗證強度分為三個等級: 證書類型 驗證內容 適用場景 信任標記 價格 DV (Domain Validation) 僅驗證網域所有權 個人網站、部落格 🔒 基本鎖頭 免費~低 OV (Organization Validation) 驗證組織身份 企業網站、SaaS 服務 🔒 組織名稱 中 EV (Extended Validation) 嚴格驗證企業 金融、電商、政府 🟢 綠色網址列 高 本文使用的是 TWCA OV SSL 證書,它提供: ✅ 組織身份驗證(瀏覽器可顯示公司名稱) ✅ 完整的證書鏈(受所有主流瀏覽器信任) ✅ 12 個月有效期 ✅ 台灣在地技術支援 HTTPS 與 SSL/TLS 運作原理 在深入部署之前,先理解 HTTPS 如何保護資料傳輸: sequenceDiagram participant Browser as 瀏覽器<br/>(內建 TWCA 根憑證) participant Server as Web Server Note over Browser,Server: 1. TLS 握手階段 Browser->>Server: ClientHello (支援的加密套件) Server->>Browser: ServerHello (選擇的加密套件) Server->>Browser: 傳送證書鏈<br/>(伺服器證書 + 中繼憑證) Note over Browser: 2. 證書驗證(完全離線) Browser->>Browser: 用中繼憑證驗證伺服器證書 Browser->>Browser: 用內建根憑證驗證中繼憑證 Browser->>Browser: 檢查證書有效期與網域 Note over Browser: ✓ 證書有效 Note over Browser,Server: 3. 金鑰交換 Browser->>Server: 用公鑰加密的 session key Server->>Server: 用私鑰解密 session key Note over Browser,Server: 4. 加密通訊 Browser->>Server: 加密的 HTTP 請求 Server->>Browser: 加密的 HTTP 回應 關鍵機制說明: ...

December 10, 2025 · 7 分鐘 · Peter

如何使用 psql 連線 AWS RDS PostgreSQL 並在容器與 Pod 中操作

前言 在現代雲端架構中,資料庫通常部署在受保護的私有網路環境(Private Subnet)中,以提升安全性。AWS RDS(Relational Database Service)作為主流的托管資料庫服務,提供了多種連線方式,但對於初學者來說,如何在不同環境(本機、Docker、Kubernetes)中正確連線到 RDS 往往充滿挑戰。 這篇文章將深入探討: AWS RDS 網路架構:公有子網 vs 私有子網的差異 直接連線方式:當 RDS 設為 Publicly Accessible 時 SSH 隧道(SSH Tunneling):透過 Bastion Host 連線私有 RDS 容器環境連線:在 Docker 和 Kubernetes Pod 中使用 psql psql 完整命令參考:從基礎查詢到進階操作 安全最佳實踐:如何保護資料庫連線與憑證 常見問題排查:連線失敗的系統化診斷方法 無論你是在本機開發、容器化部署、或是 Kubernetes 叢集中操作,這篇文章都能幫助你建立安全可靠的資料庫連線。 AWS RDS 網路架構概覽 在開始連線之前,我們需要理解 AWS RDS 的網路架構。RDS 實例可以部署在不同的網路環境中,每種配置都有不同的連線方式和安全考量。 公有子網 vs 私有子網 graph TB subgraph "AWS VPC" subgraph "Public Subnet (10.0.1.0/24)" IGW[Internet Gateway] Bastion[Bastion Host<br/>EC2 Instance] RDS_Public[RDS PostgreSQL<br/>Publicly Accessible: Yes<br/>10.0.1.100] end subgraph "Private Subnet (10.0.2.0/24)" RDS_Private[RDS PostgreSQL<br/>Publicly Accessible: No<br/>10.0.2.100] App[Application Servers<br/>ECS/EKS Pods] end subgraph "Security" SG_RDS[Security Group: RDS<br/>Inbound: 5432 from App] SG_Bastion[Security Group: Bastion<br/>Inbound: 22 from MyIP] end end subgraph "外部網路" Developer[開發者本機] Web[網際網路] end Web --> IGW Developer -->|SSH Port 22| Bastion Developer -->|psql Port 5432<br/>⚠️ 不安全| RDS_Public Bastion -.->|SSH Tunnel| RDS_Private App -->|psql Port 5432| RDS_Private RDS_Public -.->|套用| SG_RDS RDS_Private -.->|套用| SG_RDS Bastion -.->|套用| SG_Bastion style RDS_Public fill:#ffe1e1 style RDS_Private fill:#e1ffe1 style Bastion fill:#e1f5ff style Developer fill:#fff3e1 兩種部署方式的比較 特性 公有子網 (Publicly Accessible) 私有子網 (Private) 直接連線 ✅ 可以從網際網路直接連線 ❌ 無法直接連線 安全性 ⚠️ 較低,暴露在公網 ✅ 高,完全隔離 連線方式 psql 直連 需要 Bastion Host / VPN 適用場景 開發測試環境 生產環境(推薦) 成本 RDS 費用 RDS + Bastion Host 費用 維護複雜度 低 中等(需管理 Bastion) 最佳實踐: ...

May 26, 2025 · 22 分鐘 · 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 · 13 分鐘 · Peter

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

前言:從本地開發到雲端生產環境 本文將深入解析一個全端專案在 AWS 上的完整基礎設施架構,展示如何透過 Kubernetes (EKS) 實現高可用性、可擴展性和成本效益的生產環境。這個平台提供線上課程、預約服務、會員管理和金流整合等功能。 本文涵蓋內容: 完整的 AWS EKS 叢集架構 Strapi CMS 和 Vue.js 前端的容器化部署 Jenkins CI/CD 自動化部署流程 Ingress NGINX 負載均衡和 SSL 憑證管理 與 AWS RDS、S3、ECR 的整合 第三方服務整合 (Firebase FCM、台灣金流) 監控與日誌管理 架構概覽 整體架構圖 flowchart TB subgraph Internet["網際網路"] Users["使用者"] Admin["管理員"] MobileUsers["App 使用者<br/>iOS / Android"] end subgraph OnPremise["本地機房 Mac mini"] Jenkins["Jenkins CI/CD"] subgraph MobileBuild["行動應用建置"] Fastlane["📲 Fastlane<br/>自動化打包工具"] FastlaneMatch["🔐 Fastlane Match<br/>iOS 憑證管理"] XcodeBuild["🍎 Xcode Build<br/>iOS App 編譯"] GradleBuild["🤖 Gradle Build<br/>Android App 編譯"] end end subgraph AppStores["應用商店"] TestFlight["✈️ Apple TestFlight<br/>iOS 測試版發布"] AppStore["🍎 App Store<br/>iOS 正式上架"] PlayConsole["▶️ Google Play Console<br/>Android 上架"] end GitRepo["📂 Git Repository<br/>Fastlane Match 憑證庫"] subgraph AWS["AWS Cloud (ap-southeast-1)"] subgraph EKS["EKS Cluster: mycom-taipei-cluster"] subgraph IngressNS["ingress-nginx namespace"] LB["AWS ELB<br/>LoadBalancer"] Ingress["Ingress NGINX<br/>Controller"] end subgraph CertManager["cert-manager namespace"] CertMgr["Cert Manager"] CertInj["CA Injector"] CertWebhook["Webhook"] end subgraph DefaultNS["default namespace"] StrapiPod["Strapi Pod<br/>mycom-strapi-prod<br/>(1.5 CPU / 2Gi RAM)"] WebPod["Vue.js Pod<br/>mycom-web<br/>(500m CPU / 512Mi RAM)"] PSQLClient["PostgreSQL Client<br/>(Debug)"] end subgraph KubeSystem["kube-system namespace"] CoreDNS["CoreDNS<br/>(2 replicas)"] MetricsServer["Metrics Server<br/>(2 replicas)"] AWSNode["AWS VPC CNI"] KubeProxy["Kube Proxy"] end subgraph CloudWatch["amazon-cloudwatch namespace"] FluentBit["Fluent Bit<br/>(日誌收集)"] end end subgraph Storage["儲存服務"] RDS["RDS PostgreSQL<br/>postgres-strapi-taipei<br/>(ap-southeast-1)"] S3["S3 Bucket<br/>mycom-strapi-prod-s3-taipei"] end subgraph Registry["容器映像倉儲"] ECR["AWS ECR<br/>AWS_Account_ID.dkr.ecr"] end subgraph Monitoring["監控服務"] CW["CloudWatch<br/>Logs & Metrics"] end end subgraph ThirdParty["第三方服務"] Firebase["Firebase FCM<br/>(推播通知)"] Newebpay["Newebpay<br/>(台灣金流)"] EZPay["EZPay<br/>(電子發票)"] SendGrid["SendGrid<br/>(郵件服務)"] TWCA["TWCA<br/>(OV SSL 憑證)"] end Jenkins -->|建置映像檔| ECR Jenkins -->|部署更新| EKS Jenkins -->|觸發建置| Fastlane Fastlane --> FastlaneMatch FastlaneMatch -->|同步憑證| GitRepo Fastlane --> XcodeBuild Fastlane --> GradleBuild XcodeBuild -->|自動上傳| TestFlight TestFlight -.->|正式發布| AppStore GradleBuild -->|自動上傳| PlayConsole Users -->|HTTPS| LB Admin -->|HTTPS| LB MobileUsers -->|下載 App| TestFlight MobileUsers -->|下載 App| PlayConsole LB -->|:80,:443| Ingress Ingress -->|CMS Backend| StrapiPod Ingress -->|Web Frontend| WebPod CertMgr -.->|管理 SSL 憑證| Ingress TWCA -.->|OV SSL 憑證| CertMgr StrapiPod -->|讀寫資料| RDS StrapiPod -->|上傳媒體檔案| S3 StrapiPod -->|推播通知| Firebase StrapiPod -->|金流處理| Newebpay StrapiPod -->|電子發票| EZPay StrapiPod -->|寄送郵件| SendGrid WebPod -->|GraphQL API| StrapiPod FluentBit -->|收集日誌| CW MetricsServer -.->|資源監控| CW ECR -->|拉取映像檔| StrapiPod ECR -->|拉取映像檔| WebPod 核心技術棧 基礎設施層: ...

June 5, 2024 · 12 分鐘 · Peter