From 10ae041edb36a46780fac241b7bcb3478a842e23 Mon Sep 17 00:00:00 2001 From: jmromeroes Date: Sat, 18 Jul 2026 13:54:45 -0500 Subject: [PATCH] chore: remove util-linux family + gzip from runtime image Eliminates CVEs by removing packages that have no runtime consumers, per Ross's feedback rejecting the gzip waiver and suggesting util-linux via rpm -e --nodeps with a /sbin/nologin symlink stand-in. Runtime verification: - Zero binaries in the image link libblkid/libmount/libsmartcols/libuuid/ libfdisk after removal (verified via readelf -d across all ELF files) - No runtime reference to gzip/gunzip in start.sh or the IQ Server bundle - ssh, ssh-keygen, git-over-https, and git-over-ssh all continue to work despite openssh's declared RPM file-dep on /sbin/nologin (a symlink to /bin/false substitutes with identical exit behavior) - Server boots and admin auth API responds correctly Ordering: microdnf remove runs first for the packages it can cleanly resolve, then rpm -e --nodeps removes the util-linux family (breaking openssh's file-dep), then a final rpm -e removes rpm/rpm-libs plus libarchive/libxml2/sqlite-libs/xz-libs/bzip2-libs which rpm itself depends on until it exits. --- Dockerfile | 23 +++++++++++++++++++---- Dockerfile.rh | 23 +++++++++++++++++++---- Dockerfile.slim | 23 +++++++++++++++++++---- 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 516b13e..41305af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -152,6 +152,9 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # uses BouncyCastle FIPS via JSSE for all cryptographic operations, not libgcrypt. # Present only as a transitive install-time dep of packages that are themselves removed # later in this block (systemd-libs pulls it in; systemd-libs is in the rpm -e list above). +# - cracklib, cracklib-dicts, gzip: transitively pulled in by pam. pam is removed via the +# rpm -e --nodeps step above, which leaves cracklib and gzip as orphans. Nothing at runtime +# invokes gzip (verified: no reference in start.sh or the IQ Server bundle). # # rpm -e --nodeps required only for packages with RPM-level deps that aren't actual runtime links: # - gawk: krb5-libs has a scriptlet-only dep on it @@ -164,6 +167,15 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # Modern git over HTTPS uses git-remote-https -> git-remote-http, which does NOT link # libexpat (verified via ldd in the baseline image). No code path in IQ Server uses # dumb-HTTP git push, and the JRE parses XML with Xerces, not libexpat. +# - util-linux, util-linux-core, libblkid, libmount, libsmartcols, libuuid, libfdisk: no +# runtime binary in the image links libblkid/libmount/libsmartcols/libuuid/libfdisk (0 NEEDED +# entries across all ELFs, verified via readelf). openssh declares an RPM file-dep on +# /sbin/nologin (owned by util-linux); --nodeps breaks that declared dep and we substitute +# a symlink to /bin/false (which coreutils-single provides with identical exit behavior) +# so any /etc/passwd shell entries referencing nologin still resolve. +# - sqlite-libs, xz-libs, bzip2-libs, libarchive, libxml2, rpm, rpm-libs: kept alive until the +# last step because rpm binary itself dynamically links against them (or transitively through +# librpm/librpmio -> libarchive -> libxml2); removed together in the final rpm -e call. # hadolint ignore=DL3059 RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ pam libpwquality expat \ @@ -171,11 +183,14 @@ RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ crypto-policies-scripts python3 python3-libs python3-pip-wheel python3-setuptools-wheel \ microdnf libdnf librepo librhsm libsolv libmodulemd \ gobject-introspection libpeas json-glib glib2 \ - gpgme gnupg2 libarchive libusbx \ - gnutls libxml2 sqlite-libs \ - shadow-utils libsemanage bzip2-libs xz-libs openldap \ + gpgme gnupg2 libusbx \ + gnutls \ + shadow-utils libsemanage openldap \ libgcrypt \ - rpm rpm-libs + cracklib cracklib-dicts gzip \ +&& rpm -e --nodeps util-linux util-linux-core libblkid libmount libsmartcols libuuid libfdisk \ +&& ln -sf /bin/false /sbin/nologin \ +&& rpm -e --nodeps rpm rpm-libs libarchive libxml2 sqlite-libs xz-libs bzip2-libs # This is where we will store persistent data VOLUME ${SONATYPE_WORK} diff --git a/Dockerfile.rh b/Dockerfile.rh index fca66ad..d5ac274 100644 --- a/Dockerfile.rh +++ b/Dockerfile.rh @@ -155,6 +155,9 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # uses BouncyCastle FIPS via JSSE for all cryptographic operations, not libgcrypt. # Present only as a transitive install-time dep of packages that are themselves removed # later in this block (systemd-libs pulls it in; systemd-libs is in the rpm -e list above). +# - cracklib, cracklib-dicts, gzip: transitively pulled in by pam. pam is removed via the +# rpm -e --nodeps step above, which leaves cracklib and gzip as orphans. Nothing at runtime +# invokes gzip (verified: no reference in start.sh or the IQ Server bundle). # # rpm -e --nodeps required only for packages with RPM-level deps that aren't actual runtime links: # - gawk: krb5-libs has a scriptlet-only dep on it @@ -167,6 +170,15 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # Modern git over HTTPS uses git-remote-https -> git-remote-http, which does NOT link # libexpat (verified via ldd in the baseline image). No code path in IQ Server uses # dumb-HTTP git push, and the JRE parses XML with Xerces, not libexpat. +# - util-linux, util-linux-core, libblkid, libmount, libsmartcols, libuuid, libfdisk: no +# runtime binary in the image links libblkid/libmount/libsmartcols/libuuid/libfdisk (0 NEEDED +# entries across all ELFs, verified via readelf). openssh declares an RPM file-dep on +# /sbin/nologin (owned by util-linux); --nodeps breaks that declared dep and we substitute +# a symlink to /bin/false (which coreutils-single provides with identical exit behavior) +# so any /etc/passwd shell entries referencing nologin still resolve. +# - sqlite-libs, xz-libs, bzip2-libs, libarchive, libxml2, rpm, rpm-libs: kept alive until the +# last step because rpm binary itself dynamically links against them (or transitively through +# librpm/librpmio -> libarchive -> libxml2); removed together in the final rpm -e call. # hadolint ignore=DL3059 RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ pam libpwquality expat \ @@ -174,11 +186,14 @@ RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ crypto-policies-scripts python3 python3-libs python3-pip-wheel python3-setuptools-wheel \ microdnf libdnf librepo librhsm libsolv libmodulemd \ gobject-introspection libpeas json-glib glib2 \ - gpgme gnupg2 libarchive libusbx \ - gnutls libxml2 sqlite-libs \ - shadow-utils libsemanage bzip2-libs xz-libs openldap \ + gpgme gnupg2 libusbx \ + gnutls \ + shadow-utils libsemanage openldap \ libgcrypt \ - rpm rpm-libs + cracklib cracklib-dicts gzip \ +&& rpm -e --nodeps util-linux util-linux-core libblkid libmount libsmartcols libuuid libfdisk \ +&& ln -sf /bin/false /sbin/nologin \ +&& rpm -e --nodeps rpm rpm-libs libarchive libxml2 sqlite-libs xz-libs bzip2-libs # This is where we will store persistent data VOLUME ${SONATYPE_WORK} diff --git a/Dockerfile.slim b/Dockerfile.slim index 516b13e..41305af 100644 --- a/Dockerfile.slim +++ b/Dockerfile.slim @@ -152,6 +152,9 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # uses BouncyCastle FIPS via JSSE for all cryptographic operations, not libgcrypt. # Present only as a transitive install-time dep of packages that are themselves removed # later in this block (systemd-libs pulls it in; systemd-libs is in the rpm -e list above). +# - cracklib, cracklib-dicts, gzip: transitively pulled in by pam. pam is removed via the +# rpm -e --nodeps step above, which leaves cracklib and gzip as orphans. Nothing at runtime +# invokes gzip (verified: no reference in start.sh or the IQ Server bundle). # # rpm -e --nodeps required only for packages with RPM-level deps that aren't actual runtime links: # - gawk: krb5-libs has a scriptlet-only dep on it @@ -164,6 +167,15 @@ RUN update-crypto-policies --set DEFAULT:SHA1 # Modern git over HTTPS uses git-remote-https -> git-remote-http, which does NOT link # libexpat (verified via ldd in the baseline image). No code path in IQ Server uses # dumb-HTTP git push, and the JRE parses XML with Xerces, not libexpat. +# - util-linux, util-linux-core, libblkid, libmount, libsmartcols, libuuid, libfdisk: no +# runtime binary in the image links libblkid/libmount/libsmartcols/libuuid/libfdisk (0 NEEDED +# entries across all ELFs, verified via readelf). openssh declares an RPM file-dep on +# /sbin/nologin (owned by util-linux); --nodeps breaks that declared dep and we substitute +# a symlink to /bin/false (which coreutils-single provides with identical exit behavior) +# so any /etc/passwd shell entries referencing nologin still resolve. +# - sqlite-libs, xz-libs, bzip2-libs, libarchive, libxml2, rpm, rpm-libs: kept alive until the +# last step because rpm binary itself dynamically links against them (or transitively through +# librpm/librpmio -> libarchive -> libxml2); removed together in the final rpm -e call. # hadolint ignore=DL3059 RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ pam libpwquality expat \ @@ -171,11 +183,14 @@ RUN rpm -e --nodeps gawk libfido2 systemd-libs p11-kit p11-kit-trust libtasn1 \ crypto-policies-scripts python3 python3-libs python3-pip-wheel python3-setuptools-wheel \ microdnf libdnf librepo librhsm libsolv libmodulemd \ gobject-introspection libpeas json-glib glib2 \ - gpgme gnupg2 libarchive libusbx \ - gnutls libxml2 sqlite-libs \ - shadow-utils libsemanage bzip2-libs xz-libs openldap \ + gpgme gnupg2 libusbx \ + gnutls \ + shadow-utils libsemanage openldap \ libgcrypt \ - rpm rpm-libs + cracklib cracklib-dicts gzip \ +&& rpm -e --nodeps util-linux util-linux-core libblkid libmount libsmartcols libuuid libfdisk \ +&& ln -sf /bin/false /sbin/nologin \ +&& rpm -e --nodeps rpm rpm-libs libarchive libxml2 sqlite-libs xz-libs bzip2-libs # This is where we will store persistent data VOLUME ${SONATYPE_WORK}