# Thêm xác thực và log khi không được sửa code của khách

> Ứng dụng cũ của khách không ai dám sửa vẫn có thể có thêm bảo mật và log, miễn là bạn biết đặt proxy ở đâu và không giao cho nó việc của ứng dụng.

Bản gốc: https://fdetimes.net/vi/bach-khoa/api-gateway-sidecar-ambassador/

Thử hình dung sáng thứ Hai, bạn đến văn phòng khách hàng. Hệ thống quản lý đơn hàng của họ là một ứng dụng Java viết từ nhiều năm trước, người viết đã nghỉ việc, không có test và không ai dám deploy lại.

Yêu cầu bạn nhận được là tuần sau phải có xác thực cho API, có log để bộ phận an ninh đọc, và chuyển một phần traffic sang dịch vụ báo cáo mới.

Phản xạ đầu tiên của nhiều kỹ sư là mở code ra sửa. Với một FDE, đó thường là lựa chọn tệ nhất, vì code không thuộc về bạn, rủi ro hồi quy nằm hết ở phía khách, và khó có ai ký duyệt một bản build mới trong một tuần.

Kỹ năng cần có ở đây là thêm năng lực cho hệ thống từ bên ngoài, bằng một proxy đặt đúng vị trí.

Tài liệu kiến trúc của Microsoft Azure gom việc này thành mấy pattern có tên rõ ràng: gateway, sidecar, ambassador, và một biến thể chuyên về bảo mật là gatekeeper. Nắm được chúng, lần tới gặp yêu cầu "không được sửa code nhưng phải có thêm tính năng", bạn sẽ biết ngay nên đặt proxy ở đâu.

## Proxy đặt ở đâu thì gánh được việc gì?

Hãy hình dung đường đi của một request. Nó xuất phát từ client, đi qua mạng, tới dịch vụ. Ba pattern trên khác nhau ở chỗ proxy đứng tại điểm nào trên đường đi đó.

**Gateway** đứng trước một nhóm dịch vụ. Theo mô tả của pattern Gateway Routing, gateway định tuyến ở Layer 7 nên client chỉ cần biết một endpoint duy nhất. Bạn có thể thêm, tách hay sắp xếp lại dịch vụ phía sau mà không phải sửa client. Pattern Gateway Offloading đi thêm một bước: gateway nhận luôn các việc xuyên suốt như quản lý chứng chỉ, xác thực, TLS termination, giám sát, chuyển đổi giao thức và throttling thay cho backend.

**Sidecar** là một tiến trình hoặc container chạy cạnh ứng dụng. Mỗi instance có một sidecar riêng và hai bên chung vòng đời. Vì chạy riêng nên sidecar không phụ thuộc ngôn ngữ của ứng dụng. Service mesh như Istio dùng sidecar proxy để làm mTLS, retry và telemetry mà không cần sửa code ứng dụng.

**Ambassador** cũng là proxy ngoài tiến trình, nhưng đặt cùng host với phía *gọi đi*. Nó lo monitoring, logging, routing, TLS và resiliency cho các cuộc gọi ra ngoài. Microsoft gợi ý dùng pattern này để mở rộng năng lực mạng cho ứng dụng legacy hoặc ứng dụng khó sửa.

| | Gateway | Sidecar | Ambassador |
|---|---|---|---|
| Vị trí | Trước một nhóm dịch vụ | Cạnh từng instance dịch vụ | Cạnh client, trên cùng host |
| Hướng traffic chính | Đi vào | Vào và ra của một instance | Đi ra |
| Phù hợp nhất khi | Cần một cửa vào chung có xác thực và log | Cần mTLS, telemetry giữa các dịch vụ | App cũ gọi API bên ngoài mà không có TLS, retry, log |
| Cái giá | Có thể thành điểm lỗi đơn và nút thắt | Thêm độ trễ và tài nguyên cho mỗi instance | Thêm độ trễ, retry dễ gây hậu quả |

## Ví dụ: dựng gateway trước ứng dụng đơn hàng

Quay lại khách hàng giả định ở trên. Giả sử ứng dụng đơn hàng nghe ở cổng 8080, dịch vụ báo cáo mới ở cổng 9000, và nhóm IT của khách đã có sẵn một dịch vụ kiểm tra token. Một cấu hình nginx tối giản làm gateway có thể như sau:

```nginx
log_format json escape=json
'{"time":"$time_iso8601","method":"$request_method",'
'"uri":"$request_uri","status":$status,'
'"rt":$request_time,"user":"$auth_user"}';

server {
listen 443 ssl;
ssl_certificate     /etc/nginx/certs/api.crt;
ssl_certificate_key /etc/nginx/certs/api.key;
access_log /var/log/nginx/api.json json;

location = /_auth {
internal;
proxy_pass http://auth-svc/verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}

location /api/orders/ {
auth_request /_auth;
auth_request_set $auth_user $upstream_http_x_user;
proxy_pass http://legacy-orders:8080;
}

location /api/reports/ {
auth_request /_auth;
auth_request_set $auth_user $upstream_http_x_user;
proxy_pass http://reports-v2:9000;
}
}
```

Hãy đọc file này theo ba yêu cầu của khách. Xác thực nằm ở `auth_request`: mọi request phải qua `/_auth` trước, và ứng dụng Java không biết gì về chuyện đó. Log nằm ở `log_format json`: mỗi request ghi ra một dòng JSON có thời gian, mã trạng thái, độ trễ và người gọi, để đội an ninh đọc thẳng vào hệ thống của họ.

Định tuyến nằm ở hai khối `location`. Client chỉ biết một tên miền, còn `/api/reports/` lặng lẽ đi sang dịch vụ mới. Nếu bản mới có lỗi, rollback chỉ là sửa một dòng `proxy_pass` rồi reload gateway. Đúng như pattern Gateway Offloading mô tả, kể cả khi dịch vụ phía sau chưa được instrument đúng cách, gateway vẫn cho bạn một mức giám sát và log nền.

## Việc nhất định phải làm sau khi cấu hình chạy

Cấu hình trên vẫn chưa an toàn nếu ứng dụng đơn hàng còn mở cổng 8080 ra mạng nội bộ. Ai biết địa chỉ đó sẽ gọi thẳng vào, bỏ qua xác thực và không để lại dòng log nào.

Hướng dẫn của Microsoft nói rõ backend chỉ nên nhận request đi qua đúng đường gateway. Trong thực tế, bạn có thể làm việc này bằng firewall, security group hoặc network policy, chỉ cho phép IP của gateway gọi vào.

Pattern Gatekeeper còn đi xa hơn. Lớp trung gian lo kiểm tra và làm sạch request chạy với đặc quyền hạn chế, còn phần ứng dụng giữ quyền truy cập storage và dịch vụ. Áp vào ví dụ: gateway không nên giữ mật khẩu database hay khóa của backend, để nếu gateway bị chiếm thì thiệt hại được giới hạn.

Thứ tự triển khai tại chỗ khách nên như sau. Đầu tiên chạy gateway song song và chỉ ghi log, chưa bật xác thực, để xem traffic thật trông thế nào. Sau đó bật xác thực cho từng route, tiếp theo khóa đường đi thẳng vào backend, và cuối cùng mới chuyển dần traffic sang dịch vụ mới.

## Sidecar và ambassador: khi gateway không với tới

Gateway chỉ quản được traffic đi qua cửa chính. Khi các dịch vụ trong cluster gọi nhau, hoặc khi ứng dụng cũ gọi ra API của đối tác, bạn cần proxy ở gần hơn.

Giả sử khách chạy trên Kubernetes và muốn mã hóa traffic giữa các dịch vụ. Một sidecar proxy trong mỗi pod, chẳng hạn qua Istio, làm được mTLS và thu telemetry mà ứng dụng không cần biết.

Tài liệu của Istio mô tả service mesh mang lại bảo mật zero-trust, khả năng quan sát và quản lý traffic nâng cao, với hai chế độ là ambient hoặc sidecar truyền thống.

Còn nếu ứng dụng đơn hàng gọi API thanh toán qua HTTP trần, không có timeout và không log, hãy đặt một ambassador trên cùng host. Ứng dụng gọi `localhost`, ambassador lo TLS, ghi log từng cuộc gọi đi và áp timeout. Code cũ vẫn giữ nguyên.

## Bốn cái bẫy thường gặp

Bẫy đầu tiên là để backend mở như đã nói ở trên. Bẫy thứ hai nguy hiểm hơn vì nó đến từ thiện chí: khách nhờ "tiện thì kiểm tra hạn mức tín dụng ở gateway luôn". Hướng dẫn của Microsoft nói thẳng là không bao giờ được đẩy business logic lên gateway.

**Điểm mấu chốt:** Proxy lo chuyện đường ống; logic nghiệp vụ phải ở lại trong ứng dụng.

Cám dỗ tương tự xuất hiện với Gateway Aggregation, tức gateway gọi nhiều backend rồi gộp kết quả để client đỡ phải gọi nhiều lần. Nếu logic gộp bắt đầu phức tạp, hãy tách nó thành một dịch vụ aggregation riêng đặt sau gateway.

Bẫy thứ ba là bật retry trong ambassador cho mọi thứ. Microsoft lưu ý retry trong ambassador có thể không an toàn nếu các thao tác không idempotent.

Thử hình dung lệnh tạo thanh toán bị timeout ở phía trả lời nhưng thực ra đã thành công: proxy gửi lại, và khách hàng của khách bị trừ tiền hai lần.

Chỉ retry những thao tác như GET, hoặc các lệnh ghi có idempotency key.

Bẫy thứ tư là quên rằng mọi request giờ đều đi qua gateway. Gateway có thể thành điểm lỗi đơn và nút thắt cổ chai, nên phải load test trước khi đưa vào production và thiết kế theo yêu cầu sẵn sàng của khách, chẳng hạn chạy nhiều bản.

Sidecar cũng có cái giá riêng: thêm độ trễ, phí tài nguyên với ứng dụng nhỏ, và có thể không cần nếu mesh của khách đã có data plane không dùng sidecar.

## Bạn chọn được proxy nào cho ba tình huống này?

Hãy tự trả lời trước khi đọc đáp án. Tình huống A: một ứng dụng báo cáo cũ gọi API của nhà cung cấp qua HTTP trần, khách muốn có TLS và log cho từng cuộc gọi đi. Tình huống B: đối tác bên ngoài cần một endpoint duy nhất, có xác thực, để gọi vào ba dịch vụ nội bộ.

Tình huống C: các dịch vụ trên Kubernetes gọi nhau, đội an ninh yêu cầu mã hóa và thu telemetry cho traffic giữa chúng mà không sửa code.

Đáp án: A là ambassador, vì vấn đề nằm ở traffic đi ra từ phía client. B là gateway, vì cần một cửa vào chung lo xác thực và định tuyến.

C là sidecar proxy trong service mesh, hoặc chế độ ambient nếu mesh của khách hỗ trợ và đáp ứng yêu cầu. Nếu bạn trả lời đúng cả ba, bạn đã nắm được câu hỏi cốt lõi: request đi theo hướng nào, và proxy đứng ở đâu trên đường đi đó.

## Thể hiện kỹ năng này trong hồ sơ thế nào?

Khi đọc JD vị trí FDE hoặc solutions engineer, hãy để ý các cụm như "integrate with legacy systems", "API gateway", "service mesh", "observability".

Trong CV, đừng chỉ ghi "biết nginx, Istio". Hãy viết theo kết quả, chẳng hạn: thêm xác thực và log có cấu trúc cho hệ thống legacy thông qua gateway, không sửa dòng code nào, khóa đường truy cập trực tiếp vào backend. Câu đó cho người phỏng vấn thấy bạn hiểu cả kỹ thuật lẫn ràng buộc của khách.

Lần tới khi ai đó bảo "hệ thống này không ai dám sửa", bạn đừng coi đó là ngõ cụt. Thường đó là lúc nên hỏi xem request đi qua những đâu, và nên đặt proxy ở điểm nào.

**Thử ngay tuần này:**

- Dùng docker compose dựng một ứng dụng HTTP đơn giản, đặt nginx phía trước làm gateway có auth_request và log JSON, rồi chặn cổng backend để chỉ gateway gọi vào được.
- Lấy một hệ thống bạn đang làm và liệt kê mọi endpoint ghi dữ liệu, đánh dấu cái nào idempotent, cái nào không, trước khi bật retry ở bất kỳ proxy nào.
- Viết lại một gạch đầu dòng trong CV theo dạng: đã thêm xác thực và log cho hệ thống legacy qua gateway hoặc sidecar, không sửa code ứng dụng, kèm kết quả đo được.

## Nguồn

- [Gateway Routing pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-routing)

- [Gateway Offloading Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-offloading)

- [Gateway Aggregation Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/gateway-aggregation)

- [Sidecar Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/sidecar)

- [Ambassador Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/ambassador)

- [Gatekeeper Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/gatekeeper)

- [The Istio service mesh](https://istio.io/latest/about/service-mesh/)
