Files
sys-analysis-design/sa-output/e-commerce/02-architecture/adr/ADR-015_service-jwt-xac-thuc-service-to-service.md
Canhchimlac 343ad8bbbc save
2026-09-15 16:02:30 +07:00

186 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADR-015 — Xác thực service-to-service bằng Service JWT ngắn hạn
*Tên file: `adr/ADR-015_service-jwt-xac-thuc-service-to-service.md`*
| | |
|---|---|
| **Status** | `Proposed` |
| **Date** | 2026-09-15 |
| **Người quyết** | Security (chưa có người — `OQ-007`) + Tech Lead (bảo mật — `decision-radar.md §5`; Security có quyền phủ quyết cuối cùng) — chưa ký thật |
| **Người đề xuất** | SA (qua skill `sa-2-architecture`, hoạt động `adr`) |
| **Điểm radar** | **5/10** *(chấm theo `decision-radar.md §2`, kế thừa nguyên ước lượng đã ghi ở `SEC_e-commerce_v1.0.md §2.4`: Chi phí đảo ngược=1 (1–3 tuần — đổi cơ chế sau khi đã code tốn sửa cả hai đầu gọi) · Bán kính ảnh hưởng=2 (mọi lời gọi cross-network nội bộ TB3/TB4 + tiền lệ cho các `IF` tương lai) · Chạm `QAS` Must=1 (gián tiếp `QAS-002`/`QAS-010` — thêm bước xin token có thể ăn vào ngân sách latency đã eo hẹp, cần đo lại) · Ràng buộc dài hạn=1 (chuẩn nội bộ cho service-to-service, trong một release) · Tranh cãi=0 → **5 ⇒ `ADR` bắt buộc** (5–7), dưới ngưỡng 8 nên không bắt buộc POC, nhưng cần đo lại latency overhead trước khi `Accepted`)* |
| **Supersedes** | — |
| **Superseded by** | — |
| **Liên quan** | `ASR-002` · `ASR-007` · `QAS-002` · `QAS-010` · `THR-09` · `THR-13` · `SEC_e-commerce_v1.0.md §1 (TB3/TB4)`, `§2.4` · `SAD_e-commerce_v1.0.md §7` (ràng buộc không thêm hạ tầng mới) · `ADR-002` · `ADR-007` · `ADR-012` · `OQ-050` · `DEC-28` |
> ⚠️ **ADR bất biến sau khi `Accepted`.** Muốn đổi quyết định thì viết ADR mới có
> `Supersedes: ADR-015` và đổi trạng thái bản cũ thành `Superseded by`. Không sửa nội dung ADR
> đã `Accepted`, không xoá ADR đã `Rejected`.
---
## 0. Ghi chú Preflight & ngoại lệ gate
Viết cùng lượt với `ADR-014`/`ADR-016`/`ADR-017` dưới ngoại lệ gate ghi ở `DEC-31` (tiếp nối
`DEC-01…30`) — xem `ADR-014 §0` cho bối cảnh đầy đủ. Riêng `ADR-015`: hiện thực hoá hướng **PA-3**
đã chọn TẠM ở `SEC_e-commerce_v1.0.md §2.4` (duyệt từng phần, `DEC-28`).
🔴 **Lưu ý quan trọng nhất của ADR này:** đây là quyết định **bảo mật** — theo `decision-radar.md
§5`, loại quyết định này do **Security chốt**, không phải Tech Lead hay SA. Security **hiện chưa
có người được chỉ định** (`OQ-007`, mở từ GĐ1). ADR này **không thể chuyển `Accepted`** cho tới
khi có đại diện Security thật ký — bất kể Tech Lead có đồng ý nội dung hay không, đúng nguyên tắc
"Security có quyền phủ quyết AG2" (`sa-2-architecture/SKILL.md`, hoạt động 6). Người dùng (vai
điều phối dự án chạy thử) đã được cảnh báo lại và **khẳng định muốn tiếp tục** dưới ngoại lệ.
---
## 1. Bối cảnh
`SEC_e-commerce_v1.0.md §1`/`§2.4` phát hiện một khoảng trống: hai ranh giới cross-network nội bộ
— TB3 (Nhóm Giao dịch ↔ Nhóm Hỗ trợ, `IF-021`/`IF-022`) và TB4 (Cart & Order → Payment, `IF-006`,
**ranh giới nhạy cảm nhất vì Payment giữ tiền thật**, `ASR-002`) — hiện **chỉ dựa vào network
reachability** (security group VPC), không có cơ chế xác thực danh tính bên gọi. `THR-09` và
`THR-13` (Spoofing, mức 🔴) ghi rõ: một service khác trong cùng VPC (kể cả bị compromise) có thể
giả danh gọi thẳng các endpoint này nếu security group cấu hình sai.
`SAD_e-commerce_v1.0.md §7` (v1.2) đã đặt một ràng buộc rõ ràng: **không tự thêm hạ tầng mới**
(VD internal ALB/App Mesh) vào sơ đồ ở lượt thiết kế này — mọi hạ tầng mới là một quyết định kiến
trúc riêng, ngoài phạm vi. Điều này loại trực tiếp phương án service mesh/mTLS đầy đủ trừ khi có
một `ADR` riêng chấp nhận thêm hạ tầng.
**Ràng buộc đang chi phối:**
| Nguồn | Nội dung |
|---|---|
| `ASR-002` | Payment Service cô lập PCI-DSS SAQ A — TB4 là ranh giới nhạy cảm nhất |
| `ASR-007` | Chỗ ra quyết định ownership/authz — mở rộng tinh thần sang cả xác thực giữa các service nội bộ |
| `SAD §7` (v1.2) | Không tự thêm hạ tầng mới ngoài phạm vi thiết kế hiện tại |
| `CON-05` | Đội vận hành chưa xác nhận có kinh nghiệm vận hành hạ tầng phân tán phức tạp (service mesh) |
**Cái đã biết chắc / cái còn là giả định:**
| Điều | 🟢 Đã kiểm chứng / 🔴 Giả định | Bằng chứng |
|---|---|---|
| Hạ tầng ký/verify JWT của Identity & Access (`CMP-02`) đã tồn tại cho xác thực người dùng cuối | 🟢 Đã kiểm chứng | `SEC §2` |
| Thêm 1 bước xin/verify service token không ăn quá nhiều vào ngân sách latency `QAS-002`/`QAS-010` | 🔴 Giả định mới — cần đo lại | Chưa đo — GĐ3 |
| AWS Cloud Map + ECS Service Connect (đã chốt ở `INF §3`, mức `DEC`, không `ADR`) tương thích với việc đính kèm header service JWT ở tầng ứng dụng | 🟢 Đã kiểm chứng (đây là 2 lớp độc lập — network discovery vs application-layer auth) | `INF §3` |
## 2. Phương án đã cân nhắc
### PA-1 — Chỉ dựa network (security group)
| | |
|---|---|
| **Mô tả** | Giữ nguyên hiện trạng — không thêm cơ chế xác thực nào giữa các service nội bộ |
| **Ưu** | Không tốn công triển khai |
| **Nhược** | Không đáp ứng "defense in depth" cho dữ liệu PII/tiền đi qua TB3/TB4; một security group cấu hình sai = không còn ranh giới nào; không giảm thiểu `THR-09`/`THR-13` |
| **Chi phí đảo ngược** | Thấp về kỹ thuật, nhưng để lại `THR-09`/`THR-13` ở mức 🔴 không giảm thiểu — rủi ro bảo mật tồn đọng |
### PA-2 — mTLS qua service mesh (AWS App Mesh/Private CA)
| | |
|---|---|
| **Mô tả** | Mỗi ECS task có chứng chỉ riêng, xác thực lẫn nhau ở tầng TLS |
| **Ưu** | Mạnh nhất, chuẩn ngành cho zero-trust nội bộ |
| **Nhược** | Thêm hạ tầng mới (App Mesh) — **mâu thuẫn trực tiếp** với ràng buộc đã ghi ở `SAD §7` v1.2 ("không tự thêm hạ tầng mới ngoài phạm vi"); đội chưa xác nhận có kinh nghiệm vận hành mesh (`CON-05`) |
| **Chi phí đảo ngược** | Cao — triển khai rồi tháo dỡ mesh tốn nhiều tuần, ảnh hưởng mọi service |
### PA-3 — Service JWT ngắn hạn ký bởi Identity & Access *(chọn)*
| | |
|---|---|
| **Mô tả** | Mỗi service tự xin "service token" (JWT riêng, `sub=service-name`, TTL ngắn ~5 phút, đề xuất) từ Identity & Access qua IAM role (không cần secret tĩnh), đính kèm header khi gọi `IF-006`/`IF-021`/`IF-022`; bên nhận verify chữ ký giống verify JWT người dùng (tái dùng hạ tầng ký/verify đã có) |
| **Ưu** | Không cần hạ tầng mới ngoài quy ước header + logic verify (tái dùng key JWT của `CMP-02`); tương thích với ràng buộc "không thêm hạ tầng mới" của `SAD §7`; nâng được một lớp phòng thủ so với hiện trạng thuần network |
| **Nhược** | Cần Identity & Access cấp phát token cho service (thêm luồng nội bộ mới, nhỏ); vẫn phụ thuộc network layer làm lớp phòng thủ đầu (không phải mTLS zero-trust đầy đủ); thêm 1 round-trip xin token có thể ăn vào ngân sách latency (cần đo lại) |
| **Chi phí đảo ngược** | Trung bình (1–3 tuần) — đổi cơ chế sau khi đã code cả hai đầu gọi (bên gửi xin token, bên nhận verify) tốn sửa cả hai phía |
### Bảng so sánh
| Tiêu chí | PA-1 | PA-2 | PA-3 |
|---|---|---|---|
| Giảm thiểu `THR-09`/`THR-13` (Spoofing) | ❌ Không | ✅ Mạnh nhất | ✅ Có (một lớp phòng thủ bổ sung) |
| Tương thích ràng buộc `SAD §7` (không thêm hạ tầng mới) | ✅ | ❌ Vi phạm trực tiếp | ✅ |
| Tương thích năng lực đội hiện tại (`CON-05`) | ✅ | 🔴 Chưa xác nhận có kinh nghiệm | ✅ Tái dùng hạ tầng JWT sẵn có |
| Chi phí đảo ngược | Thấp (nhưng để lại rủi ro) | Cao | Trung bình |
## 3. Quyết định
> **Chọn PA-3 — Service JWT ngắn hạn ký bởi Identity & Access, giữ nguyên security group làm lớp
> phòng thủ đầu.**
**Vì sao:** Đây là phương án duy nhất vừa nâng được một lớp phòng thủ thật sự (giảm thiểu
`THR-09`/`THR-13`) vừa **không vi phạm** ràng buộc đã ghi ở `SAD §7` v1.2 ("không tự thêm hạ tầng
mới ngoài phạm vi") và không đòi hỏi năng lực vận hành mesh mà đội chưa xác nhận có (`CON-05`).
**Phạm vi áp dụng:** Mọi lời gọi cross-network nội bộ giữa các nhóm triển khai — hiện tại là TB3
(`IF-021`/`IF-022`) và TB4 (`IF-006`); áp dụng cho mọi `IF` cross-network tương lai theo cùng
chuẩn (thiết lập tiền lệ).
### Điều kiện chuyển `Proposed → Accepted`
| Điều kiện | Ai xác nhận | Trạng thái hiện tại |
|---|---|---|
| **Có đại diện Security thật ký** — đây là quyết định bảo mật, Security có quyền phủ quyết | Security | 🔴 **Chặn cứng — chưa có người** (`OQ-007`, mở từ GĐ1) |
| Tech Lead xác nhận hướng PA-3 (không đổi sang PA-2 mTLS) | Tech Lead | Mở (`OQ-050` chưa đóng) |
| Đo lại latency overhead của bước xin/verify token, xác nhận không ăn quá nhiều vào ngân sách `QAS-002`/`QAS-010` | Tech Lead + QA | Chưa chạy — GĐ3 |
## 4. Phương án bị loại và lý do
| Phương án | Loại vì | Gắn với | Điều kiện nào thì xét lại |
|---|---|---|---|
| PA-1 — Chỉ dựa network | Không giảm thiểu `THR-09`/`THR-13` (Spoofing mức 🔴) — để lại rủi ro bảo mật đã biết mà không có biện pháp bổ sung | `ASR-002`, `THR-13` | Không xét lại — đây là hiện trạng đã bị đánh giá không đủ |
| PA-2 — mTLS/App Mesh | Vi phạm trực tiếp ràng buộc `SAD §7` v1.2 ("không tự thêm hạ tầng mới ngoài phạm vi"); đội chưa xác nhận có kinh nghiệm vận hành mesh (`CON-05`) | `SAD §7`, `CON-05` | Xét lại nếu tổ chức cần zero-trust đầy đủ hơn (VD mở rộng nhiều team/service hơn đáng kể) **và** có `ADR` riêng chấp nhận thêm hạ tầng mesh, **và** đội xác nhận đủ năng lực vận hành |
## 5. Hệ quả
**Hệ quả tích cực**
- Nâng một lớp phòng thủ thật sự cho TB3/TB4 (giảm thiểu `THR-09`/`THR-13`), thay vì chỉ dựa
network reachability
- Không cần hạ tầng mới — tái dùng hạ tầng ký/verify JWT sẵn có của Identity & Access
- Thiết lập một chuẩn chung cho mọi `IF` cross-network tương lai
**Hệ quả tiêu cực phải sống chung**
- Identity & Access cần thêm luồng cấp phát service token (endpoint/logic mới, dù nhỏ)
- Thêm 1 round-trip mạng (xin token) có thể ăn vào ngân sách latency vốn đã eo hẹp
(`QAS-002`/`QAS-010`) — cần đo lại trước khi `Accepted`
- Vẫn phụ thuộc network layer làm lớp phòng thủ đầu — không phải zero-trust đầy đủ như mTLS
**Cái quyết định này khoá lại**
| Muốn đổi về sau thì | Tốn |
|---|---|
| Đổi sang mTLS/App Mesh sau này | Cần viết `ADR` mới chấp nhận thêm hạ tầng, triển khai mesh, đào tạo đội vận hành — nhiều tuần |
**Việc phát sinh**
| Việc | Chủ | Hạn | Ghi ở đâu |
|---|---|---|---|
| Identity & Access thiết kế endpoint/logic cấp service token | Dev (Identity & Access) | Trước khi Dev thi công TB3/TB4 | Thiết kế chi tiết thuộc GĐ3 |
| Đo lại latency overhead trên đường găng checkout | SA/QA (GĐ3) | Trước go-live | `FAIL`/`FIT` GĐ3 |
| Viết `FIT-31` thật (service-authn test) | QA/Dev (GĐ3) | GĐ3 | `03-enablement/FIT_e-commerce.md` |
## 6. Cách kiểm chứng quyết định này được tuân thủ
| Cách kiểm | Công cụ | Chạy ở đâu | `FIT` |
|---|---|---|---|
| Gọi `IF-006`/`IF-021`/`IF-022` không có service JWT hợp lệ (thiếu, hết hạn, hoặc sai chữ ký) bị từ chối | Integration test | CI + staging | `FIT-31` *(ứng viên, đã đề xuất ở `SEC §11`)* |
| Đo p95 latency đường găng checkout với bước xin/verify token, xác nhận vẫn ≤ ngân sách `QAS-002` | Load test (k6) | Staging | Ứng viên bổ sung — cùng nhóm `FIT-23/24/25` của `ADR-013` |
## 7. Điều kiện xét lại
| Dấu hiệu | Ngưỡng | Ai theo dõi |
|---|---|---|
| Latency overhead đo được ăn quá nhiều vào margin `QAS-002` (margin còn lại theo `ADR-013` ~1.280–1.880ms) | Overhead > 100ms p95 (đề xuất, chưa xác nhận) | Tech Lead |
| Tổ chức mở rộng nhiều team/service hơn đáng kể, cần zero-trust đầy đủ | Số service cross-network > gấp đôi hiện tại (2 ranh giới TB3/TB4) | Tech Lead + Security |
## 8. Tham chiếu
- POC: Không bắt buộc (radar 5/10, dưới ngưỡng 8) — nhưng khuyến nghị đo lại latency trước khi
`Accepted`
- Bài đo: `FIT-31` (ứng viên GĐ3)
- Tài liệu ngoài: —
- Thảo luận: `SEC_e-commerce_v1.0.md §2.4` (phát hiện + đề xuất), `THR-09`/`THR-13`,
`OQ-050`/`DEC-28` (chọn hướng TẠM)