Docker images can bundle arbitrary binaries and libraries into a single blob of data. Inspecting what’s actually inside an image helps you assess its suitability and identify any security hazards.

The easiest way to explore an image’s content involves starting a container, getting a shell session, and then using regular terminal commands like ls and cd to view its directory structure from within. This isn’t ideal in security-critical environments though – creating a container with an unknown image could expose you to a malicious entrypoint script.

Here are techniques you can use to inspect an image’s files without starting a container.

Creating a Container Without Starting It

docker create is a lesser-known counterpart to docker run. It creates a new container atop a given image without starting it. You could launch it later on with the docker start command.

Creating a new container isn’t dangerous as it’ll stay inert until it’s run. You can roughly liken it to defining the config settings for a VM which you don’t use. Even if it’s set to boot from a tainted operating system ISO, you’re not going to cause any damage to your environment.

The command above creates a new container called suspect-container that will be based on the suspect-image:latest image.

Exporting the Container’s Filesystem

Now you’ve got a valid but stopped container, you can export its filesystem using the docker export command. As the container’s never been started, you can be sure the export accurately represents the filesystem defined by your image’s layers.

You’ll end up with a tar archive in your working directory that contains everything inside your image. Open or extract this archive using your favorite software to browse the image’s directories and list and view files.

If you don’t need to save or open the archive, instead preferring to get the file list in your terminal, modify the tar command:

tar t lists the contents of the input archive. You’ll end up with a list of everything in your image inside suspect-container-files.txt.

Using “docker image save”

A variation on this technique is using docker image save. This command directly saves an image’s data to a tar archive.

This method produces an archive that’s focused on the image, not containers created from it. The tar will include a manifest.json file, describing the image’s layers, and a set of directories containing the content of all the individual layers.

This is helpful when you’re evaluating each layer’s role in building the image. However, creating and exporting a stopped container is a more accessible way to browse the image’s final filesystem.

Listing Layers With “docker image history”

Another way of inspecting an image’s content is to view its layer list with the docker image history command.

This exposes the Dockerfile instructions that composed the image’s layers. It won’t let you see individual files and directories in the image’s filesystem but can more be effective at highlighting suspect behavior.

Each line in the command’s output represents a new layer in the image. The “CREATED BY” column shows the Dockerfile instruction that created the layer.

Scanning the layer list helps you quickly identify suspicious actions that could indicate you’re using a malicious image. Look for unknown binaries in RUN instructions, unexpected environment variable changes, and suspicious CMD and ENTRYPOINT statements.

The latter two layers are arguably the most important to assess when inspecting an image’s history. They tell you exactly what will launch when you docker run or docker start a container. If either instruction looks suspicious or unfamiliar, consider using the techniques above to fully inspect the referenced binaries or scripts.

Accessing an image’s filesystem provides a very granular view of its contents where malicious content can easily go unnoticed, even after manual inspection. The layer list exposed by docker image history can’t help you find disguised filesystem items but is more effective at surfacing blatantly malicious operations such as furtive spyware downloads or environment variable overrides.

Third-Party Tools

Third-party open-source tools are also available to help you list the content of images. These typically offer filtering capabilities so you can quickly enumerate installed operating system packages, programming language dependencies, and ordinary files.

Image inspection is built into the Anchore container scanning engine. You can use it by running anchore-cli image content my-image:latest after you’ve installed Anchore. This provides a complete list of the target image’s filesystem contents.

Another option is Dive, a tool expressly built for visualizing image content. It uses a layer-based approach and highlights the filesystem changes made with each new layer. You browse through the filesystem using a tree-based interactive terminal view.

Conclusion

Docker images are usually opaque at the point of consumption. Popular registries don’t provide a file listing in their APIs or user interfaces. This functionality isn’t integrated into the Docker CLI either. Although many developers use images as-is, an unaudited image may be intolerable in high-risk environments.

You can inspect a suspect image by exporting it to an archive and browsing through its content. This keeps the image inert, preventing malicious content from being run, while providing a complete view of the filesystem it would create in a container.

You can further boost your security posture by combining manual content exploration with automated image scans. These accelerate the process of detecting known vulnerabilities but may not be effective at finding new malicious files deposited in concealed locations. Using multiple techniques lets you spread your coverage and catch the widest possible set of suspect files.