← 返回日报
精读 预计 2 分钟

Making HTTP requests from a container that has no curl, using bash /dev/tcp

摘要

文章介绍了在 Docker 容器中缺少 curl 或 wget 的场景下,利用 bash 内置特性通过 /dev/tcp 协议直接构建 HTTP 请求。方法是执行类似 bash -c 'exec 3<> service:8642; echo -e "GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n" >&3; cat <&3' 的命令,脚本输出完整响应(状态行、头、空白行及正文)。作者强调这是 bash 内部处理的 TCP 重定向,不是真实设备文件;需确保目标主机名可解析且在共享网络中可用;通过 Connection: close 头确保连接关闭以正确读取响应;结合 timeout 命令避免无响应情况;指出此为简易调试工具,不支持重定向、chunked 编码、压缩、TLS 等完整 HTTP 客户端功能,仅限明文 HTTP。

荐读理由

在AI工程项目容器无curl的场景下,用bash /dev/tcp + timeout 6 构建GET /health请求,能快速验证跨Docker网络连通性并打印状态行+头+体,照着例子抄配置就能直接用于健康检查流水线

原文

Making HTTP requests from a container that has no curl, using bash /dev/tcp

I needed to check that one container could reach another over an internal Docker network: a plain GET /health against a service on a shared network. The obvious move is curl http://service:8642/health. But this app image was stripped right down, with no curl or wget and nothing else around that I could use to open a socket.

As it turns out, bash can speak HTTP by itself bash can open a TCP socket, and you can write a small HTTP request to it by hand. Opening a connection to a host and port and writing the request needs nothing beyond the shell that’s already there:

bash

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

service here is just the hostname of whatever you’re talking to. It has to resolve and be reachable from wherever you run this, so it needs to be set up first: a container or service name on a Docker network you’ve configured, or any DNS name that resolves. Swap in your own host and port.

That prints the whole response: the status line, the headers, the blank line, and the body. To add a header, such as an Authorization: Bearer token, put another \r\n-terminated line before the blank line that ends the request:

bash

exec 3<>/dev/tcp/service/8642
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

What caught me out the first time is that /dev/tcp isn’t a real device file. There’s no such path on disk; ls /dev/tcp finds nothing, and cat /dev/tcp/... from another shell just errors. It’s a redirection that bash handles internally. From the Bash manual:

/dev/tcp/host/port – If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket.

The names were picked because no real Unix has a /dev/tcp or /dev/udp hierarchy, so there’s nothing to collide with. Bash does the DNS lookup and the connect(2) for you, and exec 3<> hands the socket a file descriptor (3) you read from and write to like any other.

A few things worth knowing:

  • This is not a real HTTP client. It does not parse HTTP properly, handle redirects, chunked responses, compression, retries, TLS, or any of the other things curl quietly does for you. It’s a quick connectivity and debugging trick.

  • The Connection: close header matters. Without it the server keeps the connection open after it responds, which is the HTTP/1.1 default, and cat <&3 then waits forever for bytes that never arrive. Asking the server to close means cat reaches EOF and returns. Wrapping the call in timeout 6 bash -c '...' covers you either way.

  • There’s no TLS. /dev/tcp opens a raw socket, so this only works for plaintext HTTP. For https you’d need openssl s_client, and by then you may as well have the proper tools.

  • This is a bash feature, not POSIX. dash (Debian’s /bin/sh) and zsh don’t have it, so a #!/bin/sh script can’t use it. Call bash directly.

  • It’s a compile-time option, switched on when bash is built with --enable-net-redirections. Most mainstream builds enable it, and it worked without any fuss in the Debian-based image I was in, but Debian shipped it disabled for years, so on an old or very minimal system it’s worth checking first.

For day-to-day work curl is still the right tool. But inside a deliberately small container where you can’t install anything, this gets a quick check done without adding a package.

Lobsters · 3 赞 · 1 评 讨论 → 阅读原文 →

这条对你有帮助吗?