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

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

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

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

编译它:

gcc uname.c -o uname

当我在我的 ubuntu 上运行它时它工作得很好。

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

cat > Dockerfile <<EOF
FROM alpine:latest
RUN rm /bin/uname
COPY uname /bin/
ENTRYPOINT sh;
WORKDIR /home
EOF  

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

当我运行图像时:

docker run -it --rm myimage  

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

/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

答案1

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

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

$ readelf -l /bin/uname
<...>
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

但Alpine下没有这样的解释器。相反, Alpine/lib/ld-musl-x86_64.so.1的所有二进制文件都是为了请求这个解释器而构建的:

# readelf -l /bin/busybox
<...>
      [Requesting program interpreter: /lib/ld-musl-x86_64.so.1]

所以,当你构建你的uname在 Ubuntu 中构建然后在 Alpine 中运行它时,内核无法找到解释器,因此它返回“未找到”。

有趣的是,您可以手动调用解释器,如果问题是这样的话,这可能会起作用仅有的在解释器中:

# /lib/ld-musl-x86_64.so.1 /bin/uname 
This is a fake uname

但这当然只是为了排除故障。

那么该怎么办?

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

gcc uname.c -static -o uname

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

相关内容