今日知识:无需使用 curl,你可以利用 Bash 的 /dev/TCP 发送 HTTP 请求。
TIL: You can make HTTP requests without curl using Bash /dev/TCP

原始链接: https://mareksuppa.com/til/bash-dev-tcp-http-without-curl/

在使用缺乏 `curl` 或 `wget` 等工具的精简版 Docker 容器时,你可以利用 Bash 内置的 `/dev/tcp` 重定向功能来执行 HTTP 请求。 Bash 允许通过文件描述符打开原始 TCP 套接字并进行交互。你可以通过向主机和端口打开套接字、使用 `printf` 发送 HTTP 请求头,并使用 `cat` 读取响应来执行请求。 **示例:** ```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 ``` **关键注意事项:** * **连接:** 务必包含 `Connection: close`,以确保服务器关闭连接,从而使 `cat` 能够退出。 * **局限性:** 此方法仅支持明文 HTTP,无法处理 TLS(HTTPS)。 * **环境:** 这是 Bash 的特定功能,不符合 POSIX 标准。请确保容器中的 Bash 版本在编译时启用了 `--enable-net-redirections` 选项。 虽然 `curl` 仍然是标准工具,但这种技术是在精简容器环境中排查网络连接问题时一种可靠的“无需安装”替代方案。

最近的一场 Hacker News 讨论介绍了一个巧妙的 Bash 技巧:利用 `/dev/tcp` 设备在没有 `curl` 或 `wget` 等工具的情况下执行 HTTP 请求。该方法允许用户通过手动打开文件描述符并使用 `printf` 发送原始 HTTP 标头,从而直接与网络服务进行交互。 这种技术在受限的容器环境中特别有用,因为这些环境可能缺失标准的网络工具,或者安全策略禁止使用自定义镜像。 不过,评论者建议谨慎使用:虽然这种“Shell 魔法”是进行快速健康检查或连通性测试的有效变通方案,但它无法替代生产环境中的 `curl`。由于 `curl` 能够处理代理、重定向和各种编码等复杂的实际需求,它仍然是连接远程服务的首选工具。总的来说,社区认为这是一种方便且“酷”的故障排查技能,而非标准网络工具的替代品。
相关文章

原文

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. Opening a connection to a host and port and writing the request by hand 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:

  • 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.

联系我们 contact @ memedata.com