Day 5 的 route table 決定一個請求能不能到達目標主機;到達之後,這個請求是否被允許進入,由另外兩層規則決定:Security Group(SG) 掛在機器的網路介面上,Network ACL(NACL,Network Access Control List) 掛在 subnet 上。封包進入一台機器的順序是先經過 subnet 的 NACL,再經過機器的 SG,兩層都放行才會送達。
兩者都是防火牆規則,差異主要在兩點:
| Security Group | NACL | |
|---|---|---|
| 是否記住連線 | 記住(stateful),回程不再檢查 | 不記住(stateless),回程重新檢查 |
| 能不能寫拒絕 | 只能寫 allow | allow 和 deny 都能寫 |
下面先看規則的格式,再用一次 HTTPS 連線看這兩個差異在哪裡發生。
SG 的規則只有一種:允許。每一條規則先指定方向——inbound 是別人連進這台機器(例如瀏覽器連到 web server 的 443),outbound 是這台機器主動連出去(例如 web server 去套件庫下載更新),再指定協定、port,以及對方是誰。一台 web server 的 SG 通常長這樣:
Inbound
Type Protocol Port Source
HTTPS TCP 443 0.0.0.0/0 ← 全世界都能連 443
SSH TCP 22 203.0.113.10/32 ← 只有辦公室這個 IP 能連 22
Outbound
Type Protocol Port Destination
All All All 0.0.0.0/0 ← 預設:出去全開
沒寫到的就是不准。自己新建一個 SG,預設是 inbound 全擋、outbound 全開;VPC 送的那個 default SG 多一條「允許同 SG 的機器互連」。一台機器可以同時掛好幾個 SG(預設一張網卡最多 5 個),規則是聯集——任何一個 SG 有放行,就算放行。SG 其實是掛在網路介面(ENI)上,不只 EC2 用,ALB、RDS、放在 VPC 裡的 Lambda 都靠它管進出。
NACL 的規則有編號,也可以寫 deny。判斷方式是從編號小的開始往下比,第一條符合的就定案,後面不再看;最後永遠有一條編號 * 的 deny 兜底:
Inbound
Rule # Type Protocol Port Source Allow/Deny
90 All All All 198.51.100.7/32 DENY ← 先擋掉這個 IP
100 HTTPS TCP 443 0.0.0.0/0 ALLOW
* All All All 0.0.0.0/0 DENY ← 兜底,改不掉
Outbound
Rule # Type Protocol Port Destination Allow/Deny
100 Custom TCP 1024-65535 0.0.0.0/0 ALLOW ← 回應封包要走這條
* All All All 0.0.0.0/0 DENY
編號的範圍是 1~32766,習慣用 100 為單位跳著編,之後才有空間插規則。上面那條 outbound 的 1024-65535 是什麼、為什麼一定要有,下面的情境會撞到。
Day 5 的架構裡,一台 web server 放在 public subnet,路是通的,瀏覽器也能正常連上。假設把這個 subnet 換上一張自己建的 NACL,只加一條 inbound allow 443,再用瀏覽器連過去——
結果會逾時。
換回 VPC 預設的 NACL,才會恢復正常。同樣的 443、同樣的 web server,差在哪?答案不在 inbound,在 outbound。

回應封包的「目的埠」不是 443,是瀏覽器那一端的隨機埠(圖上的 51234,術語叫 ephemeral port,暫時埠,作業系統每次連線隨機挑一個,範圍大約 1024–65535)。Security Group 記得這條連線是剛才放進來的,回去不再檢查——這叫有狀態(stateful)。NACL 什麼都不記,回去的封包要重新查一次 outbound 規則,自建的 NACL 預設 outbound 全擋——這叫無狀態(stateless)。
「記得」的意思是 SG 底層有一張連線追蹤表(connection tracking):某個 IP 從某個 port 連進來、被 inbound 規則放行,這條連線的回程就自動放行,反過來機器主動連出去被 outbound 放行,對方的回應也自動進得來。所以 SG 的 outbound 幾乎不用動,預設全開就夠;NACL 沒有這張表,進出兩個方向都要各自寫齊。
修法: NACL 的 outbound 加一條 allow TCP 1024–65535——就是前面規則範例裡那一條。同理,如果這台機器自己要主動連出去(例如更新套件),NACL 的 inbound 也要放行 1024–65535,讓對方的回應進得來。
| Security Group | NACL | |
|---|---|---|
| 守哪裡 | 機器(掛在網路介面上) | subnet |
| 記不記連線 | 記(有狀態) | 不記(無狀態) |
| 能寫什麼 | 只有 allow | allow 和 deny |
| 怎麼判 | 所有規則一起看,有一條符合就過 | 照編號由小到大,第一條符合就定案 |
| 自建的預設 | 進:全擋/出:全開 | 進出全擋(VPC 送的那張預設 NACL 例外,是全開) |
| 來源能寫誰 | CIDR、另一個 SG、prefix list | 只有 CIDR |
日常九成的規則都寫在 Security Group。什麼時候才碰 NACL?要明確擋掉某個來源的時候——Security Group 天生沒有 deny 這個選項。擋法是加一條 deny,編號要比放行的 allow 小,因為 NACL 是第一條符合就定案。
Security Group 的來源可以是另一個 Security Group。資料庫的 SG 沒有寫「允許 10.0.2.0/24 連 3306」,而是寫「允許來源為 app-sg 的連 3306」:
web-sg:inbound 443 from 0.0.0.0/0
app-sg:inbound 8080 from web-sg
db-sg: inbound 3306 from app-sg
應用層機器怎麼擴縮、IP 怎麼換,只要掛著 app-sg 就連得進;同 subnet 裡不相干的機器,沒掛就進不來。不用維護 IP 清單,也不會因為網段寫太寬多放行了誰。套進 Day 5 的架構,就是三層各一個 SG,每一層只認上一層:
網際網路 ──443──▶ [ web-sg ] ──8080──▶ [ app-sg ] ──3306──▶ [ db-sg ]
web server App EC2 RDS
(public subnet) (private subnet) (private subnet)
資料庫的 SG 裡完全沒有任何 IP 或網段,只有一行「來源 app-sg、port 3306」。就算有人在同一個 private subnet 開了一台新機器,沒掛 app-sg 一樣連不進資料庫。
Bastion host 也是同一招:public subnet 放一台跳板機,bastion-sg 的 22 只給辦公室 IP;private 機器的 SG 寫 inbound 22 from bastion-sg。(實務上現在更常用 Systems Manager Session Manager,連 22 都不用開,但 bastion 這種來源鎖 SG 的設計邏輯是共通的。)
| SSH / SFTP | HTTP / HTTPS | RDP | MySQL | PostgreSQL | SQL Server | Oracle |
|---|---|---|---|---|---|---|
| 22 | 80 / 443 | 3389 | 3306 | 5432 | 1433 | 1521 |
寫規則時最容易搞混的就是開錯埠:Windows 遠端桌面不是 22,PostgreSQL 不是 3306。
規則改完就生效,不用重開機器。被擋掉的封包是直接丟掉、不會回應「拒絕」,連線端只看得到逾時——所以故障排除時 SG 和 NACL 兩邊都要查,要知道是誰擋的得開 VPC Flow Logs(Day 22)。一個 subnet 只能掛一張 NACL,沒指定就用 VPC 預設那張(全開);兩個 VPC 用 Peering 連起來後,SG 的來源可以直接寫對方的 SG ID。
| 概念 | 說明 |
|---|---|
| Stateful | SG 有連線追蹤表,放行進來的連線,回程自動放行;outbound 預設全開就夠 |
| Stateless | NACL 沒有連線追蹤表,進出兩個方向各自判,自建的預設進出全擋 |
| Ephemeral port | 回應封包的目的埠是客戶端的隨機埠(1024–65535),NACL 要另外放行 |
| NACL 的 deny | SG 只有 allow;要擋特定來源只能在 NACL 寫 deny,編號要比 allow 小 |
| 來源寫 SG | SG 的來源可以是另一個 SG,機器 IP 變動不影響規則 |
摘要:SG 和 NACL 差在有沒有連線追蹤——有,回程自動過;沒有,回程要自己寫。deny 只有 NACL 有;來源寫 SG 只有 SG 有。
下一天往外推一層:WAF 和 Shield 看的是「HTTP 請求裡寫了什麼」和「流量有多大」,那是 Security Group 完全看不到的東西。
某公司的三層式應用程式部署在一個 VPC 中,應用層的 EC2 instance 由 Auto Scaling group 管理,數量會隨流量在 4 到 40 台之間變化,IP 位址也隨之變動。資料庫是 private subnet 中的 Amazon RDS for MySQL。資安政策要求資料庫只能被應用層的 instance 連線,同一 subnet 內的其他機器(例如批次工作伺服器)也不得存取。公司希望用維運負擔最低(LEAST operational overhead)的方式實作。
解決方案架構師應該怎麼設定資料庫的 Security Group?
0.0.0.0/0 的 3306某公司在 public subnet 部署了一台 EC2 web server,Security Group 的 inbound 允許來自
0.0.0.0/0的 TCP 443,outbound 維持預設。為了加強控管,工程師為該 subnet 建立了一張自訂 NACL,並加入 inbound 規則允許0.0.0.0/0的 TCP 443。套用之後,使用者的瀏覽器連線全部逾時;把 subnet 換回 VPC 預設的 NACL 後又恢復正常。
解決方案架構師應該怎麼修改自訂 NACL,才能在保留這張 NACL 的前提下恢復服務?
0.0.0.0/0 的 TCP 1024–655350.0.0.0/0 的 TCP 4430.0.0.0/0 的 TCP 1024–655350.0.0.0/0 改為該 subnet 的 CIDR 區塊某公司的公開網站運行在 public subnet 的 EC2 instance 上,Security Group 允許來自
0.0.0.0/0的 TCP 443。資安團隊從日誌發現一個特定的來源 IP 位址持續對網站進行掃描與暴力嘗試,要求立刻阻擋這個 IP,但網站必須維持對所有其他使用者開放。
解決方案架構師應該怎麼做?
/32
/32,放在現有編號 100 的 allow 規則之後/32,放在現有編號 100 的 allow 規則之前0.0.0.0/0 改為已知合法客戶的 IP 清單某公司在 private subnet 中運行數十台 Linux EC2 instance,這些 instance 依規定不得配置 public IP,VPC 已掛有 Internet Gateway 並有一個 public subnet。維運團隊需要從公司辦公室(固定的對外 IP 位址)以 SSH 連入這些機器進行維護,資安團隊則要求任何對外開放的 SSH 入口都必須限制來源。公司要求用最安全(MOST secure)的方式提供這個存取路徑。
解決方案架構師應該建議哪一個做法?
0.0.0.0/0 的 TCP 22,並以金鑰對驗證身份;private instance 的 Security Group 允許來源為 bastion host Security Group 的 TCP 22某公司在 public subnet 部署了一台 Windows Server EC2 instance 作為資料庫管理工作站,管理員需要從辦公室(固定 IP)遠端登入這台工作站,再從工作站連線到 private subnet 中的 Amazon RDS for PostgreSQL。目前工作站的 Security Group inbound 只允許來自辦公室 IP 的 TCP 22,RDS 的 Security Group inbound 只允許來源為工作站 Security Group 的 TCP 3306。管理員回報兩段連線都失敗。
在維持最小權限(least privilege)的前提下,解決方案架構師應該做哪兩項修改?(選擇兩項)
0.0.0.0/0 的 TCP 22Security Group 的來源可以指定另一個 Security Group:只要 instance 掛著應用層的 SG,不管它的 IP 是什麼、有幾台,都能連進資料庫;不掛那個 SG 的機器(例如同 subnet 的批次伺服器)則連不進來。這同時滿足「只有應用層能連」和「不用維護清單」。
A 用 subnet CIDR 會把同 subnet 的批次伺服器也放進來,違反資安政策。C 技術上做得到,但自己寫 Lambda 維護 IP 清單是用維運換一個 SG 原生就有的功能。D 是誤解:NACL 是 subnet 級、不能以 instance 為粒度區分,而且 SG 開 0.0.0.0/0 等於資料庫對所有人開放。
NACL 是無狀態的,回應封包不會因為「連線是對方先開的」就自動放行;web server 回給瀏覽器的封包目的埠是客戶端的暫時埠(1024–65535),自訂 NACL 預設 outbound 全擋,所以連線建立到一半就逾時。加上 outbound 允許暫時埠的規則即可。
B 是誤解:Security Group 有狀態,回應本來就自動放行,而且預設 outbound 就是全開。C 方向錯了,暫時埠是回應「出去」時用的。D 會讓網站只有同 subnet 的機器能連,解的是別的問題。
Security Group 只有 allow 規則、沒有 deny,所以「擋掉特定 IP」只能靠 NACL;NACL 依規則編號由小到大評估、第一條符合就決定,所以 deny 規則的編號必須比放行 443 的 allow 規則小,該 IP 才會先被擋下。
A 做不到:SG 沒有 deny。B 順序錯了:編號 100 的 allow 先符合,該 IP 已經被放行,編號 200 的 deny 永遠輪不到。D 違反「網站必須對所有其他使用者開放」。
Bastion 放在 public subnet 才連得到,它的 SG 把 SSH 來源鎖在辦公室 IP,private instance 的 SG 只認 bastion 的 SG,兩層都是最小權限,且 private instance 始終沒有 public IP。
A 把 bastion 放在 private subnet,辦公室根本連不到它。B 違反「不得配置 public IP」,把數十台機器直接暴露在網際網路上。C 技術上可用,但 bastion 對全世界開 22,暴力嘗試會直接打到它,只靠金鑰對不如再加來源 IP 限制安全。(延伸:實務上更安全的做法是用 AWS Systems Manager Session Manager,完全不開 22,也不需要 bastion。)
Windows 的遠端桌面走 RDP、TCP 3389,不是 SSH 的 22;PostgreSQL 聽的是 5432,3306 是 MySQL。兩段連線各自開錯了埠。
C 和 D 都是誤解:Security Group 有狀態,工作站主動連出去、RDS 收到後回應,都不需要額外的 outbound 規則(何況 SG 預設 outbound 全開)。E 把 SSH 開給全世界,既不安全、也不是 Windows 遠端登入用的協定。