Skip to content

Use Alpine without shell as default base image - CLM-39066#190

Draft
rpokorny wants to merge 105 commits into
mainfrom
CLM-39066_alpine_default_base
Draft

Use Alpine without shell as default base image - CLM-39066#190
rpokorny wants to merge 105 commits into
mainfrom
CLM-39066_alpine_default_base

Conversation

@rpokorny
Copy link
Copy Markdown
Contributor

After #183 and #182 got closed, this is the next attempt to move off of RedHat base images and remove the shell. This time, using an alpine builder image and NO runtime base image.

rpokorny added 30 commits March 23, 2026 15:21
The wolfi-base apk requires the target root to have etc/apk/keys and
etc/apk/repositories before installing packages into an isolated root.
The --initdb flag creates the database and directory structure that
apk needs before it can install packages into an alternate root.
The infosec dev image has neither apk nor wget. Move the download and
checksum verification into the packages stage (wolfi-base) which has
both. The builder stage now only handles config sed and javac.
wolfi-base is minimal and doesn't include wget by default.
The infosec runtime image is fully distroless with no shell. Include
busybox to provide /bin/sh needed for RUN commands during build and
for start.sh at runtime.
The infosec runtime image defaults to a non-root user. Need USER root
before mkdir/chmod/chown operations, then switch back to 65532.
The nonroot user doesn't exist in /etc/passwd in the distroless
runtime image. Use numeric IDs which work regardless.
- Add DL3006 ignore for untagged wolfi-base FROM
- Add DL3018 ignore for unpinned apk add
- Create /etc/passwd and /etc/group entries for nonroot (65532) so
  ls output shows names instead of numeric IDs
- Switch port expectations from curl to wget (busybox)
Use -O /dev/null instead of --spider for busybox wget compatibility.
Busybox symlinks aren't created by apk --initdb --root, so wget/etc
aren't available as standalone commands. Add curl as an explicit
package and restore the original curl-based port expectations.
Add --app flag to healthcheck.java that checks port 8070 and prints
the response body. Update expectations to use the healthcheck class
instead of curl, checking for DOCTYPE in the app port response.
This removes curl from the production image.
Retry connections for up to 120 seconds to handle the case where the
server hasn't fully started yet (matches old curl --connect-timeout).
Port 8070 may return non-200 (e.g. redirect) without a license.
In --app mode, any response means the port is up. Also handle
error streams for non-2xx responses to avoid exceptions.
- Runtime stage now uses sonatype-infosec/jre:openjdk-17 instead of
  the full JDK, reducing attack surface (no compiler in production)
- Rename healthcheck -> Healthcheck (public class, capitalized)
- Use 2-space indentation
- Fix --app mode to use response-code based exit status
The slim and alpine variants need to continue publishing during the
transition to infosec hardened base images (~3 releases).
Replace bundle tarball download with direct downloads of the server
jar and jvm.options file from the Maven repository. This removes the
dependency on the bundle distribution which is being sunset.
Update to 1.203.0-SNAPSHOT with real SHA values. Switch artifact
download URL from maven-private-releases to maven-private (group repo)
so that both release and snapshot artifacts can be resolved.
Replace curl-based artifact downloads with Maven dependency:copy. This
solves two problems: authentication (Maven uses credentials from
settings.xml) and SNAPSHOT resolution (Maven resolves timestamped
builds automatically).

The settings.xml is passed via BuildKit --secret mount so credentials
never appear in any image layer. All docker buildx build invocations
(Jenkinsfile, Jenkinsfile.release, build_and_push_images.sh) now pass
the secret.

Jenkinsfile.release simplified: removed bundle checksum handling since
artifacts are now downloaded by Maven (which does its own verification).
The updateIQServerVersion function only updates the version ARG and
release label.
The Jenkins agents don't have settings.xml at $HOME/.m2/; Maven settings
are managed by the Config File Provider plugin with ID 'private-settings.xml'.
Use configFileProvider to write settings.xml to the workspace, then pass
that path as the BuildKit secret.

build_and_push_images.sh now uses a MAVEN_SETTINGS env var (with fallback
to $HOME/.m2/settings.xml for local use).
Maven only needs a JRE to run dependency:copy (no compilation).
The JRE package is lighter than the full JDK.
rpokorny added 24 commits April 24, 2026 11:23
Added as submodule at ./localcheck pointing to sonatype/localcheck.
Will be compiled during Docker build instead of using Wolfi package
since we are migrating from Wolfi to Alpine.

CLM-39066
Replace Chainguard Wolfi base images with Alpine 3.
- Build stage: Alpine with Maven for artifact download, gcc for
  launcher, cargo for localcheck
- Runtime stage: FROM scratch with isolated-root apk packages
- Uses openjdk17-jre-headless from Alpine apk (amd64 + arm64)
- Strips busybox/shell from runtime (only needed for apk scripts)
- Compiles localcheck from submodule for healthcheck
- No shell, no package manager in runtime image

CLM-39066
- launcher at /usr/bin/launcher (Alpine /bin -> /usr/bin symlink)
- use Alpine from sonatype.repo.sonatype.app registry

CLM-39066
The main Dockerfile is now Alpine-based, making these redundant:
- Dockerfile.alpine
- Jenkinsfile.alpine
- Jenkinsfile.alpine.release
- alpine-expectations.groovy

CLM-39066
Remove alpine pull from prepare stage (image is now Alpine-based).

CLM-39066
- /tmp should be root-owned with 1777 permissions, not chown'd to nexus user
- Jenkinsfile.release now publishes VERSION-alpine and latest-alpine tags
  from the main Dockerfile (replaces removed Jenkinsfile.alpine.release)

CLM-39066
…ult_base

# Conflicts:
#	Dockerfile.alpine
#	Jenkinsfile.alpine
#	Jenkinsfile.alpine.release
Comment thread .gitmodules
@@ -0,0 +1,3 @@
[submodule "localcheck"]
path = localcheck
url = git@github.com:sonatype/localcheck.git
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

localcheck is a simple tool for http healthchecks written by our infosec dept. At the moment, its only published form is a Wolfi-compatible apk package on our repo server, which we can't use in alpine. So instead the approach for the moment is to check out the source code as a submodule and build it locally during our docker build.

Comment thread Dockerfile
&& chown -R ${UID}:${GID} /runtime-deps/etc/nexus-iq-server \
&& chown -R ${UID}:${GID} /runtime-deps/var/log/nexus-iq-server

# Download the server jar and JVM options file as individual Maven artifacts.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative that I considered, since we aren't using infosec's specially hardened JVM in this approach, was to download the jreleaser bundles and not have a globally-installed JRE on the docker image. However, that won't work because we don't have a jreleaser bundle for aarch64 MUSL, and apparently the reason we don't is because temurin doesn't provide a build for that configuration. So system JVM it is.

@rpokorny rpokorny force-pushed the CLM-39066_alpine_default_base branch from 787146c to de2d962 Compare April 28, 2026 18:44
- Update description and migration section for Alpine base
- Remove SHA256 checksum build args (no longer used)
- Note that VERSION and VERSION-alpine tags are the same image

CLM-39066
@rpokorny rpokorny force-pushed the CLM-39066_alpine_default_base branch from de2d962 to c7130b7 Compare April 28, 2026 18:53
The main Dockerfile now produces a scratch-based image with Alpine/musl
packages, but is not itself an Alpine image. Restore the separate Alpine
pipeline files (Dockerfile.alpine, Jenkinsfile.alpine, etc.) so that
the -alpine tags continue to be published by their own pipeline.

Remove -alpine tags from Jenkinsfile.release since that is the main
image pipeline.

CLM-39066
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant