Docker Image Reverse Engineering
Why?
The other day I was helping some colleagues to develop a container application for Cisco APIC.
To build a stateful
application you must follow some specific steps and build it according to the APIC specification.
If it sounds complex, you are right, is a bit complex.
When reviewing the components needed, I saw, it is need to use a special docker image, which has all the required packages. The downside, the packages were old, for example python2
was used.
As a good engineer I started to look for the dockerfile to have an updated image, but no dockerfile was avalaible, who knows why?. Then I tried to run the image, see what it is inside, but the image was crashing, good.
docker history
I remembered that you could see how a docker layer
was built using docker history
. So I tried and this was the result:
╰─ docker history aci_appcenter_docker_image
IMAGE CREATED CREATED BY SIZE COMMENT
62a3eb9c3646 5 years ago /bin/sh -c apk --no-cache add bash … 606MB
54ed6729adfb 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756… 81.1MB
d77e3599de56 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B
ec521dc7384d 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.… 0B
5339b29ce749 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161… 4.8MB
You can see on line 3, the important part is missing, where I can see how the image is build.
is there a better way?, yep, docker provides the option to use the --no-trunc
command which can give us more infomation.
╰─ docker history aci_appcenter_docker_image --no-trunc
IMAGE CREATED CREATED BY SIZE COMMENT
sha256:62a3eb9c364683751a2fc84227286a7d121a87efca5f043e299a6de62b2846a1 5 years ago /bin/sh -c apk --no-cache add bash python python-dev py-pip py-flask py-requests=2.9.1-r0 ca-certificates openssl-dev openssl gcc libffi-dev build-base && update-ca-certificates && pip install --use-wheel pyopenssl && python /usr/local/cobrafiles/ez_setup.py && easy_install -Z /usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg && easy_install -Z /usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg 606MB
sha256:54ed6729adfbb5e0816cdd0d16153032a093d8a203390804e9544989f53e0c72 5 years ago /bin/sh -c #(nop) COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles 81.1MB
sha256:d77e3599de5642e16d39a0c9455c7e850ee80ed700cd76cce6ba5deafb0dd616 5 years ago /bin/sh -c mkdir /usr/local/cobrafiles 0B
sha256:ec521dc7384d86f97585c6b4b65a76aef37671e456ec303505bdb3c108b1dee4 5 years ago /bin/sh -c #(nop) MAINTAINER gursheno@cisco.com 0B
sha256:5339b29ce7493a9dc881571d439e6a2cdc4ba5af3e1874c232296d4e6610b9c8 5 years ago /bin/sh -c #(nop) ADD file:d6ee3ba7a4d59b161917082cc7242c660c61bb3f3cc1549c7e2dfff2b0de7104 in / 4.8MB
We can see now the commands used but still not very user friendly.
dfimage
So I looked in the internet and found this handy image which can give us the information we want. Check it out here
To use it you can create an alias
alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage"
And run dfimage
specifying the name and tag of the image you want to reverse engineer.
╰─ dfimage -sV=1.36 aci_appcenter_docker_image:latest
Unable to find image 'alpine/dfimage:latest' locally
latest: Pulling from alpine/dfimage
df20fa9351a1: Pull complete
820dbffe2156: Pull complete
Digest: sha256:4a271e763d51b7f3cca72eac9bf508502c032665dde0e4c8d5fcf6376600f64a
Status: Downloaded newer image for alpine/dfimage:latest
Analyzing aci_appcenter_docker_image:latest
Docker Version: 1.8.2
GraphDriver: overlay2
Environment Variables
|PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Image user
|User is root
Potential secrets:
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
|Found match etc/apk/keys/alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub Possible public key \.pub$ 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
|Found match etc/udhcpd.conf DHCP server configs dhcpd[^ ]*.conf 1290a865bff43de688fdf7f813e14341692cde9719576c82ed5760bbbccbda36/layer.tar
Dockerfile:
MAINTAINER gursheno@cisco.com
RUN mkdir /usr/local/cobrafiles
COPY dir:ff8259f4217102756455051f7d44d6fd73b05936a199b32b9f5dab90f4e73670 in /usr/local/cobrafiles
usr/
usr/local/
usr/local/cobrafiles/
usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg
usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg
usr/local/cobrafiles/ez_setup.py
RUN apk --no-cache add bash python python-dev py-pip py-flask py-requests=2.9.1-r0 ca-certificates openssl-dev openssl gcc libffi-dev build-base \
&& update-ca-certificates \
&& pip install --use-wheel pyopenssl \
&& python /usr/local/cobrafiles/ez_setup.py \
&& easy_install -Z /usr/local/cobrafiles/acicobra-2.2_0.184-py2.7.egg \
&& easy_install -Z /usr/local/cobrafiles/acimodel-2.2_0.184-py2.7.egg
From the output (lines 28 to the end) you can see we get what we wanted, the packages installed to build this application and also the SDKs installed.
Also on the messages of the potential secrets (line 19) we can see an alpine image was used as a base image.
From this point I was able to create my dockerfile and build my updated image for the APIC container app I was helping.