Thủ thuật Bash: Gửi yêu cầu HTTP mà không cần cài đặt curl hay wget
Bạn có thể sử dụng tính năng tích hợp sẵn của Bash là /dev/tcp để gửi yêu cầu HTTP mà không cần cài đặt công cụ như curl hay wget. Kỹ thuật này đặc biệt hữu ích trong các container Docker tối giản hóa để kiểm tra kết nối mạng. Tuy nhiên, nó chỉ hoạt động với giao thức HTTP thuần túy và không hỗ trợ TLS/SSL.

Trong quá trình làm việc với Docker, đôi khi chúng ta gặp phải tình huống cần kiểm tra kết nối giữa các container nội bộ. Ví dụ, bạn cần gửi một yêu cầu GET đơn giản đến endpoint /health của một dịch vụ trên mạng chung. Thông thường, lệnh curl http://service:8642/health sẽ giải quyết nhanh chóng vấn đề.
Tuy nhiên, trong trường hợp image ứng dụng được cắt giảm tối đa (stripped down), không có curl, không có wget hay bất kỳ công cụ nào khác để mở socket, bạn sẽ phải làm thế nào?
May mắn thay, chính Bash có khả năng "nói" ngôn ngữ HTTP. Bạn có thể mở kết nối đến một host và port cụ thể, sau đó viết yêu cầu thủ công mà không cần bất kỳ công cụ bổ sung nào ngoài shell đang có.
Cách sử dụng /dev/tcp trong Bash
Bash cung cấp một thiết bị đặc biệt cho phép chuyển hướng mạng. Dưới đây là cách bạn có thể thực hiện một yêu cầu HTTP GET:
exec 3<>/dev/tcp/service/8642
printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3
cat <&3
Hoặc ví dụ phức tạp hơn với header Authorization:
printf 'GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' "$API_KEY" >&3
cat <&3
Trong đoạn mã trên, lệnh exec 3<>/dev/tcp/... gán một file descriptor (số 3) cho socket. Bạn có thể đọc và ghi vào file descriptor này giống như bất kỳ tệp nào khác.
Một số lưu ý quan trọng
Khi sử dụng kỹ thuật này, có một số điểm bạn cần nhớ để tránh gặp sự cố:
Header Connection: close là rất quan trọng
Nếu không có header này, máy chủ sẽ giữ kết nối mở sau khi phản hồi (đây là mặc định của HTTP/1.1). Khi đó, lệnh cat <&3 sẽ chờ vô hạn vì không bao giờ nhận được tín hiệu kết thúc (EOF). Yêu cầu máy chủ đóng kết nối sẽ giúp cat nhận được EOF và trả về quyền điều khiển cho terminal. Bạn cũng có thể bao bọc lệnh bằng timeout 6 bash -c '...' để phòng trường hợp bị treo.
Không hỗ trợ TLS
/dev/tcp mở một raw socket, do đó kỹ thuật này chỉ hoạt động với HTTP thuần túy (plaintext). Nếu bạn cần HTTPS, bạn sẽ phải dùng openssl s_client, và khi đó thì bạn nên cài đặt các công cụ chuyên dụng thay vì dùng mẹo này.
Đây là tính năng riêng của Bash
Tính năng này không phải là tiêu chuẩn POSIX. Các shell như dash (mặc định là /bin/sh trên Debian) hay zsh không hỗ trợ nó. Do đó, bạn không thể dùng nó trong các script bắt đầu bằng #!/bin/sh. Bạn cần gọi bash trực tiếp.
Tùy chọn biên dịch
Tính năng này được bật bằng tùy chọn --enable-net-redirections khi biên dịch Bash. Hầu hết các bản build chính thống đều bật tính năng này và nó hoạt động tốt trên các image dựa trên Debian. Tuy nhiên, Debian từng tắt tính năng này trong nhiều năm, vì vậy trên các hệ thống cũ hoặc rất tối giản, bạn nên kiểm tra trước.
Kết luận
Đối với công việc hàng ngày, curl vẫn là công cụ tốt nhất và nên được sử dụng. Nhưng bên trong một container được cố tình cắt giảm dung lượng, nơi bạn không thể cài đặt thêm gói phần mềm nào, thủ thuật sử dụng /dev/tcp của Bash là một giải pháp tuyệt vời để hoàn thành nhanh chóng việc kiểm tra sức khỏe (health check) mà không cần thêm bất kỳ phụ thuộc nào.



