Mạng nội bộ dễ bị tổn thương: Một lầm tưởng đắt giá
Trong nhiều năm, ‘mạng nội bộ’ thường được coi như một khu dân cư biệt lập có cổng bảo vệ. Chúng ta chi bộn tiền cho những cái cổng—tường lửa, WAF, và rate limiting—nhưng lại để cửa chính của các ngôi nhà mở toang. Các microservices nội bộ trao đổi qua HTTP thông thường hoặc TLS một chiều cơ bản. Logic ở đây là gì? Nếu hacker đã vào được bên trong thì coi như trò chơi kết thúc. Tư duy đó là một canh bạc nguy hiểm mà các kiến trúc Zero Trust cuối cùng cũng đang dẹp bỏ.
Lời cảnh tỉnh của tôi không đến từ sách giáo khoa. Nó đến vào lúc 3 giờ sáng khi một cảnh báo tự động cho thấy 45.000 lần thử SSH thất bại trên một máy chủ staging vốn được coi là ‘ẩn’. Các hệ thống phòng thủ ranh giới không bao giờ là một sự đảm bảo tuyệt đối. Nếu một dịch vụ sụp đổ, kẻ tấn công có thể di chuyển ngang qua toàn bộ hệ thống của bạn như một bóng ma. Mutual TLS (mTLS) giải quyết vấn đề ‘bên trong mềm yếu’ này. Nó buộc mọi dịch vụ phải trình thẻ định danh mã hóa trước khi trao đổi dù chỉ một byte dữ liệu.
Sau khi chạy mTLS trên 24 microservices trong suốt sáu tháng qua, tôi đã thấy được những thắng lợi về bảo mật cũng như những ‘vết sẹo’ trong vận hành. Dưới đây là cách chúng tôi xây dựng một lớp xác thực kiên cố bằng Nginx và OpenSSL.
Cái bắt tay hai chiều: Không còn sự tin tưởng mù quáng
TLS tiêu chuẩn giống như việc bạn trình căn cước cho bảo vệ, nhưng bảo vệ không bao giờ trình thẻ của họ cho bạn. Trình duyệt của bạn kiểm tra máy chủ, nhưng máy chủ không hề biết bạn là ai. Trong một mạng lưới microservices, đây là một điểm mù khổng lồ. Dịch vụ A có thể yêu cầu dữ liệu từ Dịch vụ B, nhưng Dịch vụ B về cơ bản chỉ đang tin vào lời nói của Dịch vụ A.
Cách mTLS thắt chặt an ninh
Mutual TLS yêu cầu một cái bắt tay hai chiều. Máy chủ xác thực ứng dụng khách (client) và client xác thực máy chủ. Nếu chữ ký không khớp với Cơ quan chứng thực (CA) nội bộ của bạn, kết nối sẽ bị ngắt ngay lập tức. Điều này biến mạng của bạn thành một hệ thống định danh được thực thi bằng mật mã.
Bạn cần ba trụ cột để hệ thống này hoạt động:
- Một CA nội bộ: ‘Gốc tin cậy’ (root of trust) nội bộ của bạn, dùng để ký mọi chứng chỉ.
- Chứng chỉ máy chủ (Server Certificates): Bằng chứng định danh cho dịch vụ nhận yêu cầu.
- Chứng chỉ ứng dụng khách (Client Certificates): Bằng chứng định danh cho dịch vụ thực hiện cuộc gọi.
Triển khai: mTLS với OpenSSL và Nginx
Chúng tôi sử dụng Nginx như một sidecar proxy. Điều này cho phép mã nguồn Python và Go của chúng tôi luôn sạch sẽ; ứng dụng thậm chí không biết rằng quá trình mã hóa đang diễn ra. Nginx xử lý các phép toán nặng nề, trong khi ứng dụng chỉ thấy lưu lượng truy cập cục bộ.
Bước 1: Xây dựng Gốc tin cậy
Hãy bỏ qua các CA công khai như Let’s Encrypt cho lưu lượng nội bộ. Chúng không xử lý tốt các IP nội bộ và tốn kém hơn để quản lý ở quy mô lớn. Thay vào đó, hãy tự tạo gốc (root) của riêng bạn.
# Tạo Private Key cho CA
openssl genrsa -out internal-ca.key 4096
# Tạo Root Certificate (Hiệu lực trong 10 năm)
openssl req -x509 -new -nodes -key internal-ca.key -sha256 -days 3650 -out internal-ca.crt \
-subj "/CN=Internal-CA/O=DevOps"
File internal-ca.crt này là ‘điểm neo’ của bạn. Mọi dịch vụ đều cần một bản sao để xác minh các thực thể ngang hàng của nó.
Bước 2: Cấp chứng chỉ máy chủ (Order API)
Đối với ‘Order API’, chúng tôi tạo một khóa và một yêu cầu ký, sau đó để CA của chúng tôi ký nó.
# Tạo Key và CSR
openssl genrsa -out order-api.key 2048
openssl req -new -key order-api.key -out order-api.csr -subj "/CN=order-api.internal"
# Ký bằng CA nội bộ của chúng tôi
openssl x509 -req -in order-api.csr -CA internal-ca.crt -CAkey internal-ca.key \
-CAcreateserial -out order-api.crt -days 365 -sha256
Bước 3: Cấp chứng chỉ ứng dụng khách (Frontend App)
Ứng dụng ‘Frontend’ cần thông tin xác thực riêng để chứng minh nó được phép giao tiếp với Order API.
# Tạo Client Key và CSR
openssl genrsa -out frontend.key 2048
openssl req -new -key frontend.key -out frontend.csr -subj "/CN=frontend-app"
# Ký xác nhận client hợp lệ
openssl x509 -req -in frontend.csr -CA internal-ca.crt -CAkey internal-ca.key \
-CAcreateserial -out frontend.crt -days 365 -sha256
Bước 4: Lớp thực thi Nginx
Đây là nơi các quy tắc được thiết lập. Trên máy chủ Order API, chúng tôi yêu cầu Nginx đòi hỏi chứng chỉ từ bất kỳ ai cố gắng kết nối.
server {
listen 443 ssl;
server_name order-api.internal;
ssl_certificate /etc/nginx/certs/order-api.crt;
ssl_certificate_key /etc/nginx/certs/order-api.key;
# CA dùng để xác thực các CLIENT
ssl_client_certificate /etc/nginx/certs/internal-ca.crt;
# Bắt buộc xác thực hai chiều
ssl_verify_client on;
location / {
proxy_set_header X-Client-ID $ssl_client_s_dn;
proxy_pass http://localhost:8080;
}
}
Bằng cách thiết lập ssl_verify_client on;, Nginx sẽ chặn mọi yêu cầu không có chứng chỉ hợp lệ được ký bởi CA của bạn. Người gọi sẽ nhận được lỗi 400 Bad Request (SSL Certificate Required) trước khi ứng dụng của bạn kịp thấy yêu cầu đó.
Bước 5: Kiểm tra với cURL
Một yêu cầu thông thường bây giờ sẽ thất bại. Bạn phải cung cấp các file định danh.
# Lệnh này sẽ lỗi do thiếu SSL
curl https://order-api.internal
# Lệnh này sẽ thành công
curl --cacert internal-ca.crt \
--cert frontend.crt \
--key frontend.key \
https://order-api.internal
Những sự thật phũ phàng từ thực tế
mTLS mang lại khả năng bảo mật tuyệt vời, nhưng nó không phải là kiểu ‘thiết lập xong rồi quên’. Dưới đây là những gì chúng tôi học được sau sáu tháng triển khai thực tế.
1. Tự động hóa không phải là tùy chọn
Chúng tôi bắt đầu với chứng chỉ có thời hạn 1 năm. Đến tháng thứ ba, việc xoay vòng chứng chỉ thủ công trên 24 dịch vụ đã trở thành một cơn ác mộng. Cuối cùng, chúng tôi đã tích hợp HashiCorp Vault để tự động hóa việc cấp phát. Nếu bạn có hơn năm dịch vụ, đừng làm việc này thủ công. Bạn *chắc chắn* sẽ quên ngày hết hạn và gây ra sự cố ngừng hoạt động vào lúc 2 giờ sáng.
2. Cái giá của khả năng quan sát
tcpdump tiêu chuẩn trở nên vô dụng khi mọi thứ đã được mã hóa. Bạn không còn nhìn thấy nội dung dữ liệu (payload) nữa. Chúng tôi đã phải cải thiện việc ghi nhật ký của Nginx, cụ thể là ghi lại biến $ssl_client_verify. Điều này giúp chúng tôi nhanh chóng biết được kết nối thất bại là do chứng chỉ hết hạn hay do lỗi cấu hình.
3. Độ trễ và CPU
Chúng tôi nhận thấy độ trễ bắt tay trung bình từ 12ms đến 18ms cho mỗi kết nối mới. Đối với các dịch vụ có lưu lượng cao thực hiện 5.000 yêu cầu mỗi giây, điều này làm tăng mức sử dụng CPU lên 15%. Việc tinh chỉnh ssl_session_cache và duy trì các kết nối (HTTP Keep-Alive) là rất quan trọng để giữ cho hiệu suất luôn nhanh nhạy.
Lời kết
Chuyển sang mTLS là một trong những bước đi bảo mật hiệu quả nhất mà bạn có thể thực hiện. Nó làm cho các API key bị đánh cắp trở nên vô dụng; kẻ tấn công cần cả chứng chỉ vật lý và khóa riêng mới có thể gây hại. Nó chuyển gánh nặng bảo mật từ các nhà phát triển sang cơ sở hạ tầng của bạn. Chỉ cần đảm bảo rằng bạn có một kế hoạch quản lý vòng đời chứng chỉ trước khi kích hoạt nó.

