# 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)