Reduce Docker Images by 20 MB by Verifying APT Packages without gnupg
When working with package sources you can pass the plain text asc key into signed-by instead of a binary gpg key.
When configuring the sources list for a package you can use the signed-by
option to pass in a GPG key. You’ll want to save the key in /etc/apt/keyrings
since that’s what APT suggests since they deprecated apt-key
.
Did you know you can pass in the plain text ASCII key directly too? Here’s a
snippet from the apt-key
man pages:
Make sure to use the “asc” extension for ASCII armored keys and the “gpg” extension for the binary OpenPGP format (also known as “GPG key public ring”).
# How Does This Work?
Let’s go over a real example. I’ll use installing Node because I recently encountered this in my example Docker web apps since they use esbuild but this isn’t limited to Node. It will work with all signed apt packages.
Node’s official documentation for installing itself on Debian says to do:
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
It suggests using the binary version of their GPG key. That’s what running
gpg --dearmor /etc/apt/keyrings/nodesource.gpg
on their key will produce.
Then it says to add that binary key to your sources list:
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" \
| sudo tee /etc/apt/sources.list.d/nodesource.list
Notice the signed-by
option is pointing to the binary .gpg
file.
Optimizations
If you curl down curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key
this is a plain text
/ ASCII PGP key.
Based on the man pages we looked at earlier all we need to do is rename this
file to be nodesource.asc
and it will work because of its file extension.
Then we can reference it with signed-by=/etc/apt/keyrings/nodesource.asc
.
Since we don’t need to generate a binary GPG key we don’t need to apt install
gnupg
. We can also avoid referencing ca-certificates
since technically
curl
installs this package.
Technically you don’t need to mkdir /etc/apt/keyrings
on modern versions of
Debian and Ubuntu since it exists by default but that’s only a quick aside.
# Trying It Out in Docker
I did my tests in the official Debian Bookworm slim Docker image. You can
run that with: docker container run -it debian:bookworm-slim
At the time of this blog post it comes with 88 packages that are installed by
default. You can check by running apt list --installed
and then piping it to
| wc -l
, you need to subtract 1 because the first line of output of apt list --installed
isn’t a package.
Now try running: apt-get update && apt-get install gnupg
, notice that it
prompts you that it will use 26.1 MB of extra disk space to install it. Even if
you use apt-get install --no-install-recommends gnupg
it’s still 19.5 MB of
files. There’s also 113 installed packages.
If you have a ~200 MB image that’s a ~10% savings or even ~5% with a ~400 MB image. Not only that but it’ll speed up your builds since there’s less being installed.
Does It Still Work?
Yes, you can spin up a new container and run apt-get update && apt-get install -y curl
and then run this to install Node:
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /etc/apt/keyrings/nodesource.asc \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.asc] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update && apt-get install -y --no-install-recommends nodejs
You can run node --version
to verify it works as well as apt-get update
to
see that it picks up the nodesource list.
To be doubly safe it really works you can manually modify the nodesource.asc
file such as replacing 1 character and then when nodejs
tries to be installed
you’ll get a GPG error that says the signature couldn’t be verified.
I’ve applied this method to my example Docker Rails app since it installs Node this way. The other example apps use the base Node image for its assets where Node is already installed.
I also use this method to install Docker itself on Debian / Ubuntu within my Ansible role. This optimization saves having to run 1 extra task to create a binary GPG key.
The demo video goes over running some of the commands we came across in this post.
# Demo Video
Timestamps
- 0:51 – Going over the process of adding a package to your sources list
- 2:30 – Checking out the man pages for apt-key
- 2:54 – Running a Debian Bookworm slim Docker container which has 88 packages
- 3:47 – There’s 113 packages after installing gnupg, it uses +26 MB of space
- 5:27 – That could be a 5-10% reduction in image size
- 5:38 – Making sure everything works, including GPG key verification
- 8:06 – I made this change to my example Rails app and Ansible role for Docker
References
Are you using this method of verifying signed keys? Let me know below.