Published on June 1, 2023
How to Reverse Docker Images into Dockerfiles
#Coding#en-US

convert an image into a file. 3D, simple, clean, minimalist style. generated by - DALL.E 3
Nowadays, Docker is an essential tool in daily development, but we might encounter a situation: We have a Docker image, but, for some reason, we've lost the original Dockerfile used to create it. In such a scenario, how do we reverse a Docker image into its Dockerfile? It's a real-world probability, and that's what we are going to discuss in this article.
Introduce the Whaler
Whaler is a Go program which is designed to reverse engineer docker images into the Dockerfile that created it. https://github.com/P3GLEG/Whaler
It supports the following actions:
- Generates a Dockerfile from an Image
- Searches added filenames for potential secret files
- Extracts files that were added by the Docker ADD/COPY Instructions
- It also displays misc. information such as ports open, the user it runs as and environment variables.
Pull the Image
1docker pull pegleg/whalerReverse the Image
Let's use the Nginx image as an example to reverse it into the Dockerfile.
1docker run -t --rm -v /var/run/docker.sock:/var/run/docker.sock:ro pegleg/whaler -sV=1.36 nginx:latestThen, you can see the output of the DockerFile:
1CMD ["bash"]
2LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
3ENV NGINX_VERSION=1.25.2
4ENV NJS_VERSION=0.8.0
5ENV PKG_RELEASE=1~bookworm
6RUN set -x \
7 && groupadd --system --gid 101 nginx \
8 && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
9 && apt-get update \
10 && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
11 && NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; export GNUPGHOME="$(mktemp -d)"; found=''; for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo "Fetching GPG key $NGINX_GPGKEY from $server"; gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" \
12 && found=yes \
13 && break; done; test -z "$found" \
14 && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" \
15 && exit 1; gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; rm -rf "$GNUPGHOME"; apt-get remove --purge --auto-remove -y gnupg1 \
16 && rm -rf /var/lib/apt/lists/* \
17 && dpkgArch="$(dpkg --print-architecture)" \
18 && nginxPackages=" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} " \
19 && case "$dpkgArch" in amd64|arm64) echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \
20 && apt-get update ;; *) echo "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \
21 && tempDir="$(mktemp -d)" \
22 && chmod 777 "$tempDir" \
23 && savedAptMark="$(apt-mark showmanual)" \
24 && apt-get update \
25 && apt-get build-dep -y $nginxPackages \
26 && ( cd "$tempDir" \
27 && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" apt-get source --compile $nginxPackages ) \
28 && apt-mark showmanual | xargs apt-mark auto > /dev/null \
29 && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
30 && ls -lAFh "$tempDir" \
31 && ( cd "$tempDir" \
32 && dpkg-scanpackages . > Packages ) \
33 && grep '^Package: ' "$tempDir/Packages" \
34 && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
35 && apt-get -o Acquire::GzipIndexes=false update ;; esac \
36 && apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl \
37 && apt-get remove --purge --auto-remove -y \
38 && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
39 && if [ -n "$tempDir" ]; then apt-get purge -y --auto-remove \
40 && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; fi \
41 && ln -sf /dev/stdout /var/log/nginx/access.log \
42 && ln -sf /dev/stderr /var/log/nginx/error.log \
43 && mkdir /docker-entrypoint.d
44COPY file:01e75c6dd0ce317d516928a17584d111cd082840c01e58be0afc851b33adb916 in /
45 docker-entrypoint.sh
46
47COPY file:caec368f5a54f70a844a13005eb2255bed778809b3672d516e719ce2f4bce123 in /docker-entrypoint.d
48 docker-entrypoint.d/
49 docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
50
51COPY file:3b1b9915b7dd898a0e32f7eb9715a35c9feab914022efff68ba990bc1ec7d169 in /docker-entrypoint.d
52 docker-entrypoint.d/
53 docker-entrypoint.d/15-local-resolvers.envsh
54
55COPY file:57846632accc89753f45cbc00cb9e6223d991e1d31297eec3395a7ca58eed6a6 in /docker-entrypoint.d
56 docker-entrypoint.d/
57 docker-entrypoint.d/20-envsubst-on-templates.sh
58
59COPY file:9e3b2b63db9f8fc702e2dc2bdd0943be0d990c028cddcf1c159f5556a8ba3030 in /docker-entrypoint.d
60 docker-entrypoint.d/
61 docker-entrypoint.d/30-tune-worker-processes.sh
62
63ENTRYPOINT ["/docker-entrypoint.sh"]
64EXPOSE 80
65STOPSIGNAL SIGQUIT
66CMD ["nginx" "-g" "daemon off;"]Let's compare it to the original version:
1CMD ["bash"]
2LABEL maintainer=NGINX Docker Maintainers <docker-maint@nginx.com>
3ENV NGINX_VERSION=1.25.2
4ENV NJS_VERSION=0.8.0
5ENV PKG_RELEASE=1~bookworm
6RUN set -x \
7 && groupadd --system --gid 101 nginx \
8 && useradd --system --gid nginx --no-create-home --home /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \
9 && apt-get update \
10 && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
11 && NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; NGINX_GPGKEY_PATH=/usr/share/keyrings/nginx-archive-keyring.gpg; export GNUPGHOME="$(mktemp -d)"; found=''; for server in hkp://keyserver.ubuntu.com:80 pgp.mit.edu ; do echo "Fetching GPG key $NGINX_GPGKEY from $server"; gpg1 --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" \
12 && found=yes \
13 && break; done; test -z "$found" \
14 && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" \
15 && exit 1; gpg1 --export "$NGINX_GPGKEY" > "$NGINX_GPGKEY_PATH" ; rm -rf "$GNUPGHOME"; apt-get remove --purge --auto-remove -y gnupg1 \
16 && rm -rf /var/lib/apt/lists/* \
17 && dpkgArch="$(dpkg --print-architecture)" \
18 && nginxPackages=" nginx=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} " \
19 && case "$dpkgArch" in amd64|arm64) echo "deb [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \
20 && apt-get update ;; *) echo "deb-src [signed-by=$NGINX_GPGKEY_PATH] https://nginx.org/packages/mainline/debian/ bookworm nginx" >> /etc/apt/sources.list.d/nginx.list \
21 && tempDir="$(mktemp -d)" \
22 && chmod 777 "$tempDir" \
23 && savedAptMark="$(apt-mark showmanual)" \
24 && apt-get update \
25 && apt-get build-dep -y $nginxPackages \
26 && ( cd "$tempDir" \
27 && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" apt-get source --compile $nginxPackages ) \
28 && apt-mark showmanual | xargs apt-mark auto > /dev/null \
29 && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
30 && ls -lAFh "$tempDir" \
31 && ( cd "$tempDir" \
32 && dpkg-scanpackages . > Packages ) \
33 && grep '^Package: ' "$tempDir/Packages" \
34 && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
35 && apt-get -o Acquire::GzipIndexes=false update ;; esac \
36 && apt-get install --no-install-recommends --no-install-suggests -y $nginxPackages gettext-base curl \
37 && apt-get remove --purge --auto-remove -y \
38 && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
39 && if [ -n "$tempDir" ]; then apt-get purge -y --auto-remove \
40 && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; fi \
41 && ln -sf /dev/stdout /var/log/nginx/access.log \
42 && ln -sf /dev/stderr /var/log/nginx/error.log \
43 && mkdir /docker-entrypoint.d
44COPY file:01e75c6dd0ce317d516928a17584d111cd082840c01e58be0afc851b33adb916 in /
45 docker-entrypoint.sh
46
47COPY file:caec368f5a54f70a844a13005eb2255bed778809b3672d516e719ce2f4bce123 in /docker-entrypoint.d
48 docker-entrypoint.d/
49 docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
50
51COPY file:3b1b9915b7dd898a0e32f7eb9715a35c9feab914022efff68ba990bc1ec7d169 in /docker-entrypoint.d
52 docker-entrypoint.d/
53 docker-entrypoint.d/15-local-resolvers.envsh
54
55COPY file:57846632accc89753f45cbc00cb9e6223d991e1d31297eec3395a7ca58eed6a6 in /docker-entrypoint.d
56 docker-entrypoint.d/
57 docker-entrypoint.d/20-envsubst-on-templates.sh
58
59COPY file:9e3b2b63db9f8fc702e2dc2bdd0943be0d990c028cddcf1c159f5556a8ba3030 in /docker-entrypoint.d
60 docker-entrypoint.d/
61 docker-entrypoint.d/30-tune-worker-processes.sh
62
63ENTRYPOINT ["/docker-entrypoint.sh"]
64EXPOSE 80
65STOPSIGNAL SIGQUIT
66CMD ["nginx" "-g" "daemon off;"]Note
We see that there are many similar steps to the original file, but due to various reasons, such as missing intermediary layers, these reversed Dockerfiles might not be an exact copy of the original Dockerfile. Therefore, you may need to rebuild the image and test it in your development environment.