← 返回信息流
AI 资讯Hacker News·2 小时前

无需curl:Bash利用/dev/tcp直接发起HTTP请求

原标题:TIL: You can make HTTP requests without curl using Bash /dev/TCP

速览

Bash shell内置了/dev/tcp和/dev/udp设备,允许用户直接通过重定向进行网络通信。这一特性使得开发者无需依赖curl或wget等外部工具,即可在脚本中快速发起HTTP请求或测试端口连通性。虽然功能强大,但因其非标准且缺乏完整的HTTP协议支持,通常仅适用于简单的调试或自动化场景。

AI 深度解读

背景

在容器化开发和运维的日常工作中,调试服务间的网络连通性是一项高频任务。假设你需要验证一个容器能否通过内部 Docker 网络访问另一个容器,通常的做法是发送一个简单的 GET /health 请求来检查健康状态。

在大多数标准 Linux 环境中,我们会毫不犹豫地选择 curlwget 等成熟工具。然而,在某些极端场景下——例如为了追求极致的镜像体积而“精简”过的应用镜像(stripped-down image)中——系统可能只保留了最基础的 Shell 环境,连 curlwget 这样的网络工具都未安装,更不用说其他依赖库了。

在这种“裸奔”的 Shell 环境中,如果无法安装任何额外软件包,该如何发起 HTTP 请求?这篇文章揭示了一个鲜为人知但极具实用价值的 Bash 特性:利用 /dev/tcp 伪设备,仅凭 Shell 本身即可构建 TCP 连接并手动发送 HTTP 请求。

核心内容

1. 基础实现:无工具发起 HTTP 请求

Bash 内置了对网络重定向的支持。通过 /dev/tcp/host/port 语法,Bash 可以绕过文件系统,直接在内部建立 TCP 连接。

以下是一个完整的示例,用于向 service 主机的 8642 端口发送健康检查请求:

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

代码解析:

  • exec 3<>/dev/tcp/service/8642:打开一个双向文件描述符(fd 3),连接到指定主机和端口。这里的 service 可以是 Docker 网络中的容器名、服务名,或任何可解析的 DNS 域名。
  • printf ... >&3:通过文件描述符 3 手动构造并发送 HTTP/1.1 请求。注意必须包含 \r\n 作为行结束符,以及最后的空行以结束请求头。
  • cat <&3:从文件描述符 3 读取响应,直到连接关闭。

2. 进阶用法:携带认证信息

如果需要访问需要认证的 API(例如 LLM 服务),只需在请求头中添加相应的 Header 即可。例如,发送带有 Bearer Token 的请求:

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

3. 原理揭秘:/dev/tcp 并非真实文件

很多开发者第一次使用时会感到困惑:为什么 ls /dev/tcp 找不到任何文件?为什么在另一个 Shell 中执行 cat /dev/tcp/... 会报错?

这是因为 /dev/tcp 并不是一个真实的设备节点,而是 Bash 内部处理的一种重定向语法

根据 Bash 手册:

/dev/tcp/host/port:如果 host 是有效的hostname 或 Internet 地址,且 port 是整数端口号或服务名,Bash 将尝试打开对应的 TCP 套接字。

选择 /dev/tcp/dev/udp 作为名称,是因为没有任何真实的 Unix 系统存在这些层级结构,从而避免了命名冲突。Bash 会自动完成 DNS 查找和 connect(2) 系统调用,并将套接字分配给文件描述符(如 fd 3),使其表现得像普通文件一样可读可写。

关键要点

  • Connection: close 至关重要:HTTP/1.1 默认保持连接打开。如果不发送 Connection: close,服务器在响应后不会关闭连接,导致 cat <&3 无限期等待数据到达。强制关闭连接后,cat 会检测到 EOF 并正常返回。也可以包裹在 timeout 6 bash -c '...' 中以防止死锁。
  • 不支持 TLS/HTTPS/dev/tcp 仅打开原始 TCP 套接字,因此仅适用于明文 HTTP。如果需要 HTTPS,必须使用 openssl s_client 等工具,此时直接使用 curl 等成熟工具更为合适。
  • 非 POSIX 标准,仅限 Bash:这是 Bash 特有的功能。dash(Debian 的 /bin/sh)和 zsh 均不支持此语法。因此,脚本开头必须使用 #!/bin/bash,而不能使用 #!/bin/sh
  • 编译时选项:该功能依赖于 Bash 编译时的 --enable-net-redirections 选项。虽然大多数主流发行版默认启用此功能,但 Debian 曾长期默认禁用它。在极其精简或老旧的系统上,使用前建议确认 Bash 是否支持该特性。
  • 适用场景:尽管 curl 是日常工作的首选工具,但在故意构建的极小化容器(如 Alpine 精简版)中,当无法安装任何额外软件包时,这种方法提供了一种无需添加依赖即可快速进行网络检查的“黑科技”方案。

意义与影响

这一技巧虽然看似冷门,但在特定领域具有独特的实用价值:

  1. 极小化镜像调试:在构建用于生产环境的超轻量级容器(如基于 Alpine Linux 的镜像)时,为了减少攻击面和镜像体积,开发者往往会剔除 curlwget 等工具。当遇到网络连通性问题时,掌握此技巧可以避免为了调试而临时安装软件包,从而保持镜像的纯净和一致性。
  2. 嵌入式与受限环境:在资源极度受限的嵌入式系统或故障恢复环境中,当标准网络工具缺失时,Bash 的内置网络重定向功能提供了一种底层的、零依赖的调试手段。
  3. 理解 Shell 底层机制:这一案例生动地展示了 Unix 哲学中“一切皆文件”的理念在 Shell 层面的延伸。它提醒开发者,Shell 不仅仅是命令的解释器,更是一个具备底层系统调用能力的强大环境。

尽管对于大多数日常开发而言,curlwget 依然是更稳健、更安全的选择,但了解 /dev/tcp 的存在,体现了资深工程师对系统底层机制的掌控力,以及在极端约束条件下解决问题的灵活性。

查看原文 →mareksuppa.com