Home
Unchained
Product Blog

How to transition to secure container images with new migration guides

Adrian Mouat, Staff DevRel Engineer

Is your container security strategy keeping pace with your container adoption? If you're looking to bolster the integrity of your container images, Chainguard's new migration guides might be the answer.

Why migrate to Chainguard Images?

There are several reasons to migrate your container images to Chainguard: ‍

  • Minimal:

    Drastically reduced size with only essential components. ‍

  • Low-to-no CVEs:

    Significantly fewer vulnerabilities due to reduced attack surface. ‍

  • Continuous maintenance:

    Daily updates to address security concerns and vulnerabilities.

‍‍

Five tips for migrating to Chainguard Images

1. Use -dev Images when you need a shell

Chainguard Images have no shell or package manager by default. This is great for security, but sometimes you need these things, especially for build stages in mutli-stage Dockerfiles and for debugging. For these cases there are -dev image variants which do include a shell and package manager.

For example, if I try to get a shell in the cgr.dev/chainguard/nginx:latestimage:


docker run -it --entrypoint /bin/sh --user root cgr.dev/chainguard/nginx:latest
docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown.

But this is possible with the latest-devvariant:

docker run -it --entrypoint /bin/sh --user root cgr.dev/chainguard/nginx:latest-dev
/ # apk add php
fetch https://packages.wolfi.dev/os/aarch64/APKINDEX.tar.gz
(1/6) Installing xz (5.4.6-r0)
(2/6) Installing libxml2 (2.12.6-r0)
(3/6) Installing php-8.2-config (8.2.18-r0)
(4/6) Installing readline (8.2-r3)
(5/6) Installing sqlite-libs (3.45.1-r0)
(6/6) Installing php-8.2 (8.2.18-r0)
OK: 66 MiB in 38 packages
/ #

2. You can install a different shell

The -dev images and wolfi-base images use the ash shell from BusyBox by default. This is nice from a minimalist perspective, but it's not so great if you need to port a bash-and-Debian-centric entrypoint script to Chainguard Images.

In these cases you have a choice — you can persevere and update your scripts to work in ash, or you can simply install the shell that works with your scripts. There's no reason to be stuck on the ash shell if you really need bash or zsh.

For example:


docker run -it cgr.dev/chainguard/wolfi-base
423450e3fd52:/# echo {1..5}
{1..5}
423450e3fd52:/# apk add bash
fetch https://packages.wolfi.dev/os/aarch64/APKINDEX.tar.gz
(1/3) Installing ncurses-terminfo-base (6.4_p20231125-r1)
(2/3) Installing ncurses (6.4_p20231125-r1)
(3/3) Installing bash (5.2.21-r1)
OK: 20 MiB in 17 packages
423450e3fd52:/# bash
423450e3fd52:/# echo {1..5}
1 2 3 4 5
423450e3fd52:/#

3. Use apk search

Following on from the last point, you'll often need to install extra utilities to provide required dependencies for applications and scripts. These dependencies are likely to have different package names compared to other Linux distributions, so the apk search command can be very useful for finding the package you need.

For example, say we are porting a Dockerfile that uses the groupaddcommand. We could convert this to the busybox addgroupequivalent, but it's also perfectly fine to add the groupaddutility. The only issue is that there's no groupaddpackage, so we have to search for it:


docker run -it cgr.dev/chainguard/wolfi-base
ae154854dc6d:/# groupadd
/bin/sh: groupadd: not found
ae154854dc6d:/# apk add groupadd
ERROR: unable to select packages:
  groupadd (no such package):
    required by: world[groupadd]
ae154854dc6d:/# apk search groupadd
shadow-4.15.1-r0
ae154854dc6d:/# apk add shadow
(1/4) Installing libmd (1.1.0-r1)
(2/4) Installing libbsd (0.12.2-r0)
(3/4) Installing linux-pam (1.6.1-r0)
(4/4) Installing shadow (4.15.1-r0)
OK: 20 MiB in 18 packages
ae154854dc6d:/# groupadd
Usage: groupadd [options] GROUP

Options:
  -f, --force                   exit successfully if the group already exists,
                                and cancel -g if the GID is already used
  -g, --gid GID                 use GID for the new group
  -h, --help                    display this help message and exit
  -K, --key KEY=VALUE           override /etc/login.defs defaults
  -o, --non-unique              allow to create groups with duplicate
                                (non-unique) GID
  -p, --password PASSWORD       use this encrypted password for the new group
  -r, --system                  create a system account
  -R, --root CHROOT_DIR         directory to chroot into
  -P, --prefix PREFIX_DIR       directory prefix
  -U, --users USERS             list of user members of this group

Another useful trick is the "cmd:"syntax for finding packages that provide commands. For example, searching for lddreturns multiple results:


ae154854dc6d:/# apk search ldd
dpkg-dev-1.22.6-r0
nfs-utils-2.6.4-r1
posix-libc-utils-2.39-r1

But if we use the cmd:syntax we only get a single result:

ae154854dc6d:/# apk search cmd:ldd
posix-libc-utils-2.39-r1

And we can even use the syntax directly in apk add:

ae154854dc6d:/# apk add cmd:ldd
(1/4) Installing ncurses-terminfo-base (6.4_p20231125-r1)
(2/4) Installing ncurses (6.4_p20231125-r1)
(3/4) Installing bash (5.2.21-r1)
(4/4) Installing posix-libc-utils (2.39-r1)
OK: 27 MiB in 22 packages

4. Watch out for entrypoint differences

The entrypoint on Chainguard images is often different to other common images. This is due to the lack of a shell in our images, but it can be confusing.

For example, if I run Docker Hub's official Python image, it opens the Python interpreter by default:


docker run -it python
Python 3.12.3 (main, Apr 10 2024, 11:26:46) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

And the Chainguard Image works in the same way:


docker run -it cgr.dev/chainguard/python
Python 3.12.3 (main, Apr  9 2024, 16:36:34) [GCC 13.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exit()

But if I pass a Linux command to the Docker Hub image, it will be run from a shell:

docker run -it python echo "in a shell"
in a shell

This is made possible by a clever entrypoint script in the Docker Hub image. As we don't have a shell in the Chainguard Image, it instead tries to parse the command as an argument to the Python interpreter:


docker run -it cgr.dev/chainguard/python echo "in a shell"
/usr/bin/python: can't open file '//echo': [Errno 2] No such file or directory

Because we wanted to keep the entrypoint consistent between our standard and -dev images, the same results are seen when running cgr.dev/chainguard/python:latest-dev:


docker run -it cgr.dev/chainguard/python:latest-dev echo "in a shell"
/usr/bin/python: can't open file '//echo': [Errno 2] No such file or directory

5. Wolfi is not the same as Alpine Linux

Chainguard Images use the Wolfi Linux Distribution. This distribution uses the apk packaging format pioneered by Alpine, but it is a completely separate distribution to Alpine.

A particularly important difference is that Wolfi packages are all compiled against glibc, which is the most common standard C library, unlike Alpine which uses musl. Chainguard chose glibc for compatibility reasons — glibc is the standard C library used by most of the industry.

Because our toolchains and dependencies are different, it's not possible to mix Wolfi packages with Alpine packages in the same image.

Migration guides for smooth transition

To facilitate a smooth and successful migration, Chainguard has developed a series of comprehensive image migration guides. These guides cover the following platforms: ‍

Each of these reference docs provide a compatibility overview between Chainguard Images and the third-party images of their respective platform. Each includes a table highlighting what binaries and scripts are contained in Chainguard's Wolfi BusyBox and coreutils packages versus their counterparts. This will support developers in being able to quickly understand what utils are available and how they may need to transition their current applications effectively.

Additionally, we've published a general migration guide that outlines the changes one would need to make to an existing Dockerfile — whether it currently uses Debian, Red Hat UBI, or Alpine — to instead use Chainguard Images.

The goal for these guides is to help you migrate your existing container images to Chainguard with minimal disruption. If there is a migration guide you don’t see here but would find helpful, reach out to our team.

Secure your container images with Chainguard

By migrating your container images to Chainguard, you can significantly fortify the security and compliance of your container-based applications. With Chainguard's image migration guides, you can transition your existing images to secure Chainguard Images and enjoy the benefits of improved security, simplified compliance, and increased efficiency.

Get started with Chainguard Image migration

To get started with Chainguard image migration, visit our website and find the relevant guide for your platform. If you have any questions you can reach out or join one of our live Learning Labs.

Share

Ready to Lock Down Your Supply Chain?

Talk to our customer obsessed, community-driven team.

Get Started