docker 镜像逆向工程得到Dockerfile

可能有时候你得到了一个从Dockerfile创建的镜像文件,但是原始的Dockerfile丢失了。你想从这个镜像文件的构建历史记录中,逆向分析出原始的Dockerfile而省去寻找此文件的漫长过程。

虽然不可能在所有的情况下将一个Docker镜像完全得进行逆向工程,但如果此镜像是通过Dockerfile构建的,很有可能分析出此镜像是通过了什么命令得到的。我们以下面的Dockerfile为例,构建一个镜像,然后运行一个简单的shell脚本来演示如何分析镜像的构建历史记录,最后来看一个简洁的容器化的解决方案,来得出原始的Dockerfile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM busybox
MAINTAINER ian.miell@gmail.com
ENV myenvname myenvvalue
LABEL mylabelname mylabelvalue
WORKDIR /opt
RUN mkdir -p copied
COPY Dockerfile copied/Dockerfile
RUN mkdir -p added
ADD Dockerfile added/Dockerfile
RUN touch /tmp/afile
ADD Dockerfile /
EXPOSE 80
VOLUME /data
ONBUILD touch /tmp/built
ENTRYPOINT /bin/bash
CMD -r

首先要构建这个示例镜像,镜像命名为reverseme:

$ docker build -t reverseme .

SHELL解决方案

这个基于shell的实现主要在这里用来演示逆向工程的思路与方法,它与下面的容器化解决方案相比还不是十分完整。此方案使用了docker inspect命令来提取出镜像的metadata。

此shell脚本中使用了jq程序,一个可以查询和操作JSON数据的工具。为了运行此脚本,需要安装jq程序

1
2
3
4
5
6
7
8
docker history reverseme | \
awk '{print $1}' | \
grep -v IMAGE | grep -v missing | \
tac | \
sed "s/\(.*\)/docker inspect \1 | \
jq -r \'.[0].ContainerConfig.Cmd[2] | tostring\'/" | \
sh | \
sed 's/^#(nop) //'

上述代码第1行得到了组成指定镜像的层;第2行从docker history输出得到了各层的image ID;第3行排除标题行(带有“IMAGE”的那一行)及IMAGE的 ID为missing的那一行;第4行将镜像ID倒序输出,使其符合Dockerfile的顺序(“tac”是“cat”的倒序);第5、6行使用前面命令输出的image ID构建一个docker inspect命令,它输出Docker layer metadata。而此metadata通过管道输入到jq命令中,jq命令过滤metadata,获取当时构建此镜像时Dockerfile中使用的命令。第7行运行前面通过sed构建的整个docker inspect管道链。第8行剥离不能更改文件系统的指令——那些以“#(nop)”作为前缀的指令。
最后得到的输出结果类似于如下这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.....
CMD ["sh"]
MAINTAINER ian.miell@gmail.com
ENV myenvname=myenvvalue
LABEL mylabelname=mylabelvalue
WORKDIR /opt
mkdir -p copied
COPY file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in copied/Dockerfile
mkdir -p added
ADD file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in added/Dockerfile
touch /tmp/afile
ADD file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in /
EXPOSE 80/tcp
VOLUME [/data]
ONBUILD touch /tmp/built
ENTRYPOINT ["/bin/sh" "-c" "/bin/bash"]
CMD ["/bin/sh" "-c" "-r"]

上面的输出与初始的Dockerfile有些类似了,但还有些区别。FROM指令被替换成了上述CMD指令,丢失了使用的基础镜像BusyBox的信息。ADD和COPY命令没有使用原本的文件名而是使用的校验和(checksum),文件被拷贝到的位置保存了下来。最后,CMD和ENTRYPOINT命令变成了方括号的数组形式。
由于缺少构建上下文,使得ADD和COPY命令不能使用,上面逆向工程恢复的Dockerfile并不能不加修改就运行。你需要找出什么文件被添加到构建上下文中。对于前面那个例子来说,你可以启动镜像,进入容器的/opt/copied目录和/opt/added目录,将文件提取出来加入到你的新的构建上下文中。

容器解决方案

使用前面的方案得到你感兴趣镜像的信息,是一个有用并且具有指导意义的方法,然而有更加干净的方法来得到同样的结果——使用centurylink/dockerfile-from-image镜像,同时这种方法更容易维护。而且,此方案提供了与原始Dockerfile类似的FROM命令的信息(如果它可以提供的话):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ docker run -v /var/run/docker.sock:/var/run/docker.sock \
> dockerinpractice/dockerfile-from-image reverseme
FROM busybox:latest
MAINTAINER ian.miell@gmail.com
ENV myenvname=myenvvalue
LABEL mylabelname=mylabelvalue
WORKDIR /opt
RUN mkdir -p copied
COPY file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in copied/Dockerfile
RUN mkdir -p added
ADD file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in added/Dockerfile
RUN touch /tmp/afile
ADD file:4d91fcee48e4591e5fdc4b8963892b7d9582524f85f84b33eac5af164f928213 in /
EXPOSE 80/tcp
VOLUME [/data]
ONBUILD touch /tmp/built
ENTRYPOINT ["/bin/sh" "-c" "/bin/bash"]
CMD ["/bin/sh" "-c" "-r"]

此技术只适用于基于Dockerfile创建的镜像——如果镜像是通过手工创建然后commit的,镜像间的区别不能体现在镜像的metadata里。

然而发现有bug,可参考 https://github.com/lukapeschke/dockerfile-from-image, 只支持用 image_id ,不能用 image name
主要使用方法如下:

1
2
3
4
5
6
7
## build the image
git clone https://github.com/lukapeschke/dockerfile-from-image.git
cd dockerfile-from-image
docker build -t lukapeschke/dfa .

## To get a Dockerfile from an existing image:
docker run --rm -v '/var/run/docker.sock:/var/run/docker.sock' lukapeschke/dfa <IMAGE_ID>

出自 《Docker IN PRACTICE》一书

More Ref:
https://andyyoung01.github.io/2016/08/23/%E4%BB%8E%E9%95%9C%E5%83%8F%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E9%80%86%E5%90%91%E5%88%86%E6%9E%90%E5%87%BADockerfile/

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2019-2024 John Doe
  • Visitors: | Views:

请我喝杯咖啡吧~