今天才知道:不用 curl,也能用 Bash 的 /dev/tcp 發出 HTTP 請求 (★ 102 分)
作者描述了一個在精簡 Docker 容器中排查連線問題的做法:他想確認同一個內部 Docker 網路裡的容器能否連到另一個服務,原本可用 curl 呼叫 http://service:8642/health,但該映像檔沒有 curl、wget 或其他可開 socket 的工具。解法是使用 Bash 的 /dev/tcp/host/port 重新導向功能,讓 Bash 開啟 TCP socket(網路連線端點),再把手寫的 HTTP GET 請求寫入檔案描述符(file descriptor)3,最後從同一個描述符讀回伺服器回應。
文章強調,/dev/tcp 不是真正存在於磁碟上的裝置檔,而是 Bash 內部處理的特殊重新導向語法;Bash 會代為做 DNS(Domain Name System,網域名稱系統)解析與 connect(2) 系統呼叫。這種方式可以印出 HTTP 狀態列、標頭、空白行與本文,也能手動加入 Authorization: Bearer 權杖等標頭。Connection: close 很重要,因為 HTTP/1.1 預設會保持連線,若伺服器不主動關閉,cat 可能會一直等待;必要時可搭配 timeout 避免卡住。
作者也提醒這不是完整的 HTTP 用戶端。它不會正確解析 HTTP、不會處理重新導向、分塊傳輸、壓縮、重試或 TLS(Transport Layer Security,傳輸層安全性協定),因此只適合臨時確認連線或除錯;若要打 HTTPS,仍需要 openssl s_client 之類工具。這也是 Bash 專屬功能,不屬於 POSIX(Portable Operating System Interface,作業系統介面標準),dash 與 zsh 不一定可用,而且必須在 Bash 建置時啟用 --enable-net-redirections;多數主流版本有開啟,但老舊或極度精簡的系統仍值得先確認。
Hacker News 討論的主要補充是語意上的修正:Bash 並不是「會說 HTTP」,而只是能開 TCP socket,真正的 HTTP 請求仍是使用者手動送出的文字。多位留言者認為這招很適合容器內健康檢查、內部服務連線測試,或像 Envoy 映像檔、受限路由器韌體這類沒有 curl/wget 的環境;但若要連外部服務,curl、wget、nc(netcat,常用的網路讀寫工具)、telnet,或 Python 標準函式庫的 socket/urllib 通常更安全可靠。也有人指出,既然原容器是 python:3.12.2-slim-bookworm,用 Python 單行指令做連線測試可能更直接,作者也接受這點。
討論也延伸到精簡與 distroless 容器的取捨。有人主張生產環境映像檔應盡量不放非必要工具,以降低 CVE(Common Vulnerabilities and Exposures,常見弱點與揭露編號)與攻擊面;也有人認為少了標準除錯工具會讓實際問題更難診斷,加上新增 curl 或 wget 往往只是 Dockerfile 的幾行設定。另有留言對 Bash 具備 /dev/tcp 這類功能表達疑慮,認為它增加 GNU 工具的攻擊面,甚至可能被拿來繞過受限 shell 傳出資料;但整體共識是,這是一個有用且有趣的應急技巧,不應被當成正式 HTTP 用戶端替代品。
👥 60 則討論、評論 💬
https://news.ycombinator.com/item?id=48558018