如何在 Linux 容器内替换 /bin 中的二进制文件

E23*_*235 1 container docker

我想做一些奇怪的容器,我的任务之一是替换uname二进制文件。
我编码它:

#include <stdio.h>
int main() {
   printf("This is a fake uname\n");
   return 0;
}  
Run Code Online (Sandbox Code Playgroud)

编译它:

gcc uname.c -o uname
Run Code Online (Sandbox Code Playgroud)

当我在我的 ubuntu 上运行它时它工作正常。

我创建了一个Dockerfile并将其复制到图像中:

cat > Dockerfile <<EOF
FROM alpine:latest
RUN rm /bin/uname
COPY uname /bin/
ENTRYPOINT sh;
WORKDIR /home
EOF  
Run Code Online (Sandbox Code Playgroud)

并建造它 docker build -t myimage -f Dockerfile .

当我运行图像时:

docker run -it --rm myimage  
Run Code Online (Sandbox Code Playgroud)

该文件存在,但是当我尝试运行它时,它写入它不存在:

/home # uname
sh: uname: not found
/home # ls -lia uname
ls: uname: No such file or directory
/home # ls -lia /bin/uname
     35 -rwxr-xr-x    1 root     root          8600 Jan 29 13:27 /bin/uname
Run Code Online (Sandbox Code Playgroud)

Dan*_*ver 5

问题是您针对glibc(因为您使用的是 Ubuntu)构建二进制文件,然后在 Alpine 上运行它(musl改为使用)。

当您构建二进制文件时,一些元数据被硬编码到其中,特别是解释器(动态链接器)位置。在二进制执行期间,内核使用此解释器加载二进制文件及其所有运行时依赖项。当您针对 构建时glibc,二进制文件会请求glibc的解释器(类似于/lib64/ld-linux-x86-64.so.2):

$ readelf -l /bin/uname
<...>
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
Run Code Online (Sandbox Code Playgroud)

但是Alpine下没有这样的解释器。/lib/ld-musl-x86_64.so.1相反,Alpine 已经构建了它的所有二进制文件,以便它们准确地请求这个解释器:

# readelf -l /bin/busybox
<...>
      [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]
Run Code Online (Sandbox Code Playgroud)

因此,当您uname在 Ubuntu 中构建并在 Alpine 中运行它时,内核无法找到解释器,因此它返回“未找到”。

有趣的一点是,您可以手动调用解释器,如果问题出现在解释器中,这可能会起作用:

# /lib/ld-musl-x86_64.so.1 /bin/uname 
This is a fake uname
Run Code Online (Sandbox Code Playgroud)

但这当然仅用于故障排除。

那么该怎么办?

您的选择是静态链接您的二进制文件(因此不需要解释器和所有共享库,以便内核可以自行加载和运行二进制文件):

gcc uname.c -static -o uname
Run Code Online (Sandbox Code Playgroud)

或在 Alpine 下构建它(也许尝试 Docker 的多阶段构建?我会推荐这种方法,因为在这种情况下,您明确声明了您的意图并表明您在 Alpine 中构建了自定义二进制文件以替换原始的 Alpine 二进制文件)。