多服務 compose 只讓 Nginx 寫 ports,其餘服務一律不發佈 host port——前端、後端、DB 全走 Docker 內部網路,靠服務名稱(Docker DNS)互連。沒寫 ports 的服務無法透過 localhost 存取,對外攻擊面只剩 Nginx 一個入口;注意這道隔離擋的是外部流量——Linux host 本機仍可直連容器的 bridge IP。

internal: true 的精確語意(最容易誤解的點)

internal: true 約束的是「僅在該網路」的容器無法對外連線,不是「掛上這個網路就被隔離」:

服務所在網路能否對外連線
db只在 data(internal)無法
backendapp + data可以——透過 app 網路逃逸

所以 DB 加 internal: true 是有效防線(即使被攻破也無法對外回連),但 backend 同時掛兩個網路就不受約束——通常是合理取捨(backend 多半要呼叫外部 API);真要連 backend 一起隔離,拔掉它的 app 網路、改讓 Nginx 直接加入 data 網路。

拓撲

Host ── port 80 ── Nginx
                     │ app 網路(bridge)
                     ├── Frontend(無 host port)
                     └── Backend ──┐ data 網路(bridge, internal: true)
                                   └── DB(無 host port、無法對外)

不同網路的服務無法直接通訊:nginx / frontend 只在 app,連不到 db;backend 雙網路,是唯一橋接層。

networks:
  app:
    driver: bridge
  data:
    driver: bridge
    internal: true

各服務只要在 networks: 掛對網路即可;Nginx 的 proxy_pass 直接寫服務名(http://backend:8000),Docker DNS 解析。

反向代理時必須轉發這四個 header,否則後端會收到錯誤資訊(X-Forwarded-Proto 錯會導致 redirect loop 或 secure cookie 失效):

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

驗證隔離有沒有真的生效

三個檢查點:docker compose ps 的 PORTS 欄只有 nginx 該出現 0.0.0.0:80->80;從 frontend 連 db 應失敗、從 backend 連 db 應成功(docker compose exec <svc> sh -c "nc -zv db 5432")。精簡 image 多半沒裝 nc——改用 getent hosts db 驗名稱解析即可:frontend 應解析不到,backend 應解析到。

相關

參考資料