diff --git a/Dockerfile.alpine.java11 b/Dockerfile.alpine.java11 new file mode 100644 index 00000000..71543264 --- /dev/null +++ b/Dockerfile.alpine.java11 @@ -0,0 +1,89 @@ +# Copyright (c) 2016-present Sonatype, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine + +LABEL name="Nexus Repository Manager" \ + maintainer="Sonatype " \ + vendor=Sonatype \ + version="3.69.0-02" \ + release="3.69.0" \ + url="https://sonatype.com" \ + summary="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + description="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + run="docker run -d --name NAME \ + -p 8081:8081 \ + IMAGE" \ + stop="docker stop NAME" \ + com.sonatype.license="Apache License, Version 2.0" \ + com.sonatype.name="Nexus Repository Manager base image" \ + io.k8s.description="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + io.k8s.display-name="Nexus Repository Manager" \ + io.openshift.expose-services="8081:8081" \ + io.openshift.tags="Sonatype,Nexus,Repository Manager" + +ARG NEXUS_VERSION=3.69.0-02 +ARG JAVA_VERSION=java11 +ARG NEXUS_DOWNLOAD_URL=https://download.sonatype.com/nexus/3/nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz +ARG NEXUS_DOWNLOAD_SHA256_HASH=4a22cd3f2a2bd3fef46e2f13b57abfcca9e6244c36cee8c2aac226a333524c07 + +# configure nexus runtime +ENV SONATYPE_DIR=/opt/sonatype +ENV NEXUS_HOME=${SONATYPE_DIR}/nexus \ + NEXUS_DATA=/nexus-data \ + NEXUS_CONTEXT='' \ + SONATYPE_WORK=${SONATYPE_DIR}/sonatype-work \ + DOCKER_TYPE='alpine' + +# Install Java & tar +RUN apk add openjdk11 tar procps gzip curl shadow \ + && apk cache clean \ + && groupadd --gid 200 -r nexus \ + && useradd --uid 200 -r nexus -g nexus -s /bin/false -d /opt/sonatype/nexus -c 'Nexus Repository Manager user' + +WORKDIR ${SONATYPE_DIR} + +# Download nexus & setup directories +RUN curl -L ${NEXUS_DOWNLOAD_URL} --output nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz \ + && echo "${NEXUS_DOWNLOAD_SHA256_HASH} nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz" > nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && sha256sum -c nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && tar xvf nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz \ + && rm -f nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && mv nexus-${NEXUS_VERSION} $NEXUS_HOME \ + && chown -R nexus:nexus ${SONATYPE_WORK} \ + && mv ${SONATYPE_WORK}/nexus3 ${NEXUS_DATA} \ + && ln -s ${NEXUS_DATA} ${SONATYPE_WORK}/nexus3 + +# Removing java memory settings from nexus.vmoptions since now we use INSTALL4J_ADD_VM_PARAMS +RUN sed -i '/^-Xms/d;/^-Xmx/d;/^-XX:MaxDirectMemorySize/d' $NEXUS_HOME/bin/nexus.vmoptions + +RUN echo "#!/bin/bash" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && echo "cd /opt/sonatype/nexus" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && echo "exec ./bin/nexus run" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && chmod a+x ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && sed -e '/^nexus-context/ s:$:${NEXUS_CONTEXT}:' -i ${NEXUS_HOME}/etc/nexus-default.properties + +RUN apk del gzip shadow + +VOLUME ${NEXUS_DATA} + +EXPOSE 8081 +USER nexus + +ENV INSTALL4J_ADD_VM_PARAMS="-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m -Djava.util.prefs.userRoot=${NEXUS_DATA}/javaprefs" + +CMD ["/opt/sonatype/nexus/bin/nexus", "run"] diff --git a/Dockerfile.alpine.java17 b/Dockerfile.alpine.java17 new file mode 100644 index 00000000..8a55d860 --- /dev/null +++ b/Dockerfile.alpine.java17 @@ -0,0 +1,89 @@ +# Copyright (c) 2016-present Sonatype, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine + +LABEL name="Nexus Repository Manager" \ + maintainer="Sonatype " \ + vendor=Sonatype \ + version="3.69.0-02" \ + release="3.69.0" \ + url="https://sonatype.com" \ + summary="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + description="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + run="docker run -d --name NAME \ + -p 8081:8081 \ + IMAGE" \ + stop="docker stop NAME" \ + com.sonatype.license="Apache License, Version 2.0" \ + com.sonatype.name="Nexus Repository Manager base image" \ + io.k8s.description="The Nexus Repository Manager server \ + with universal support for popular component formats." \ + io.k8s.display-name="Nexus Repository Manager" \ + io.openshift.expose-services="8081:8081" \ + io.openshift.tags="Sonatype,Nexus,Repository Manager" + +ARG NEXUS_VERSION=3.69.0-02 +ARG JAVA_VERSION=java17 +ARG NEXUS_DOWNLOAD_URL=https://download.sonatype.com/nexus/3/nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz +ARG NEXUS_DOWNLOAD_SHA256_HASH=59ed008f74dea1a7f1a36dd896ea552c1d35ff537ec8e5669addd87776ecc7e2 + +# configure nexus runtime +ENV SONATYPE_DIR=/opt/sonatype +ENV NEXUS_HOME=${SONATYPE_DIR}/nexus \ + NEXUS_DATA=/nexus-data \ + NEXUS_CONTEXT='' \ + SONATYPE_WORK=${SONATYPE_DIR}/sonatype-work \ + DOCKER_TYPE='alpine' + +# Install Java & tar +RUN apk add openjdk17 tar procps gzip curl shadow \ + && apk cache clean \ + && groupadd --gid 200 -r nexus \ + && useradd --uid 200 -r nexus -g nexus -s /bin/false -d /opt/sonatype/nexus -c 'Nexus Repository Manager user' + +WORKDIR ${SONATYPE_DIR} + +# Download nexus & setup directories +RUN curl -L ${NEXUS_DOWNLOAD_URL} --output nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz \ + && echo "${NEXUS_DOWNLOAD_SHA256_HASH} nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz" > nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && sha256sum -c nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && tar xvf nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz \ + && rm -f nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz nexus-${NEXUS_VERSION}-${JAVA_VERSION}-unix.tar.gz.sha256 \ + && mv nexus-${NEXUS_VERSION} $NEXUS_HOME \ + && chown -R nexus:nexus ${SONATYPE_WORK} \ + && mv ${SONATYPE_WORK}/nexus3 ${NEXUS_DATA} \ + && ln -s ${NEXUS_DATA} ${SONATYPE_WORK}/nexus3 + +# Removing java memory settings from nexus.vmoptions since now we use INSTALL4J_ADD_VM_PARAMS +RUN sed -i '/^-Xms/d;/^-Xmx/d;/^-XX:MaxDirectMemorySize/d' $NEXUS_HOME/bin/nexus.vmoptions + +RUN echo "#!/bin/bash" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && echo "cd /opt/sonatype/nexus" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && echo "exec ./bin/nexus run" >> ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && chmod a+x ${SONATYPE_DIR}/start-nexus-repository-manager.sh \ + && sed -e '/^nexus-context/ s:$:${NEXUS_CONTEXT}:' -i ${NEXUS_HOME}/etc/nexus-default.properties + +RUN apk del gzip shadow + +VOLUME ${NEXUS_DATA} + +EXPOSE 8081 +USER nexus + +ENV INSTALL4J_ADD_VM_PARAMS="-Xms2703m -Xmx2703m -XX:MaxDirectMemorySize=2703m -Djava.util.prefs.userRoot=${NEXUS_DATA}/javaprefs" + +CMD ["/opt/sonatype/nexus/bin/nexus", "run"] diff --git a/Jenkinsfile-Internal-Release b/Jenkinsfile-Internal-Release index 3fe6ec7e..f3daf0a2 100644 --- a/Jenkinsfile-Internal-Release +++ b/Jenkinsfile-Internal-Release @@ -21,7 +21,7 @@ properties([ ]) node('ubuntu-zion') { - def commitId, commitDate, version, imageId, branch + def commitId, commitDate, version, imageId, alpineImageId, branch def imageName = 'sonatype/nexus3', archiveName = 'docker-nexus3' @@ -32,6 +32,14 @@ node('ubuntu-zion') { def DOCKERFILE_JAVA_8 = 'Dockerfile' def DOCKERFILE_JAVA_11 = 'Dockerfile.java11' def DOCKERFILE_JAVA_17 = 'Dockerfile.java17' + def DOCKERFILE_ALPINE_JAVA_11 = 'Dockerfile.alpine.java11' + def DOCKERFILE_ALPINE_JAVA_17 = 'Dockerfile.alpine.java17' + + def dockerfileMap = [ + (OPENJDK8) : [DOCKERFILE_JAVA_8], + (OPENJDK11): [DOCKERFILE_JAVA_11, DOCKERFILE_ALPINE_JAVA_11], + (OPENJDK17): [DOCKERFILE_JAVA_17, DOCKERFILE_ALPINE_JAVA_17] + ] try { stage('Preparation') { @@ -52,25 +60,19 @@ node('ubuntu-zion') { if (params.nexus_repository_manager_version) { stage('Update Repository Manager Version') { OsTools.runSafe(this, "git checkout ${branch}") - def javaVersionsDockerfilesMap = [ - (JAVA_8): DOCKERFILE_JAVA_8, - (JAVA_11): DOCKERFILE_JAVA_11, - (JAVA_17): DOCKERFILE_JAVA_17 - ] - javaVersionsDockerfilesMap.each { javaVersion, dockerfile -> - updateRepositoryManagerVersion("${pwd()}/${dockerfile}", javaVersion) + dockerfileMap.each { javaVersion, dockerfiles -> + dockerfiles.each { dockerfile -> + updateRepositoryManagerVersion("${pwd()}/${dockerfile}", javaVersion) + } } version = getShortVersion(params.nexus_repository_manager_version) } } } - stage('Build') { - def dockerfilesMap = [ - (OPENJDK8): DOCKERFILE_JAVA_8, - (OPENJDK11): DOCKERFILE_JAVA_11, - (OPENJDK17): DOCKERFILE_JAVA_17 - ] - def dockerfilePath = dockerfilesMap.get(params.java_version) + def dockerfilePath = dockerfileMap[params.java_version][0] + def alpineDockerfilePath = params.java_version == OPENJDK8 ? null : dockerfileMap[params.java_version][1] + + stage('Build UBI Image') { def baseImage = extractBaseImage(dockerfilePath) def baseImageRefFactory = load 'scripts/BaseImageReference.groovy' def baseImageReference = baseImageRefFactory.build(this, baseImage as String) @@ -78,15 +80,26 @@ node('ubuntu-zion') { def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageReferenceStr}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") imageId = hash.split(':')[1] } + if (params.java_version != OPENJDK8) { + stage('Build Alpine Image') { + def hash = OsTools.runSafe(this, "docker build --quiet --no-cache --tag ${imageName}-alpine . -f ${alpineDockerfilePath}") + alpineImageId = hash.split(':')[1] + } + } if (params.scan_for_policy_violations) { stage('Evaluate Policies') { runEvaluation({ stage -> + def isAlpine = alpineDockerfilePath != null && alpineDockerfilePath.contains('alpine') + def iqApplicationName = isAlpine ? 'docker-nexus3-alpine' : 'docker-nexus3' + def imageToScan = isAlpine ? "${imageName}-alpine" : imageName + nexusPolicyEvaluation( - iqStage: stage, - iqApplication: 'docker-nexus3', - iqScanPatterns: [[scanPattern: "container:${imageName}"]], - failBuildOnNetworkError: true, - )}, 'release') + iqStage: stage, + iqApplication: iqApplicationName, + iqScanPatterns: [[scanPattern: "container:${imageToScan}"]], + failBuildOnNetworkError: true, + ) + }, 'release') } } if (currentBuild.result == 'FAILURE') { @@ -108,11 +121,22 @@ node('ubuntu-zion') { ] def javaVersionSuffix = javaVersionSuffixesMap.get(params.java_version) - sh "docker tag ${imageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}" - sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}" + // Push UBI images + sh "docker tag ${imageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}-ubi" + sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}-ubi" if (params.java_version == OPENJDK8) { - sh "docker tag ${imageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}" - sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}" + sh "docker tag ${imageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-ubi" + sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-ubi" + } + + // Push Alpine images + if (params.java_version != OPENJDK8) { + sh "docker tag ${alpineImageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}-alpine" + sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-${javaVersionSuffix}-alpine" + if (params.java_version == OPENJDK11) { + sh "docker tag ${alpineImageId} docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-alpine" + sh "docker push docker-all.repo.sonatype.com/sonatype-internal/nexus3:${version}-alpine" + } } } } @@ -159,7 +183,7 @@ def updateRepositoryManagerVersion(dockerFileLocation, javaVersion) { } else { // default URL - def defaultUrl = /https:\/\/download-staging.sonatype.com\/nexus\/3\/nexus-\$\{NEXUS_VERSION\}-\$\{JAVA_VERSION\}-unix\.tar\.gz/ + def defaultUrl = /https:\/\/download-staging.sonatype.com\/nexus\/3\/nexus-\$\{NEXUS_VERSION\}-unix\.tar\.gz/ dockerFile = dockerFile.replaceAll(nexusUrlRegex, "\$1${defaultUrl}") def normalizedUrl = "a".replaceAll(/./, "${defaultUrl}") @@ -171,7 +195,6 @@ def updateRepositoryManagerVersion(dockerFileLocation, javaVersion) { dockerFile = dockerFile.replaceAll(shaRegex, "\$1${sha}") writeFile(file: dockerFileLocation, text: dockerFile) - } def getSha(url) { @@ -182,7 +205,7 @@ def getSha(url) { return sha } -def extractBaseImage (dockerFileLocation) { +def extractBaseImage(dockerFileLocation) { def dockerFile = readFile(file: dockerFileLocation) def baseImageRegex = "FROM\\s+([^\\s]+)" def usedImages = dockerFile =~ baseImageRegex diff --git a/Jenkinsfile-Release b/Jenkinsfile-Release index 9a1fe1bc..5ec5c0de 100644 --- a/Jenkinsfile-Release +++ b/Jenkinsfile-Release @@ -25,7 +25,7 @@ properties([ ]) node('ubuntu-zion') { - def commitId, commitDate, version, imageId, branch, dockerFileLocations, dockerJava11FileLocations, dockerJava17FileLocations + def commitId, commitDate, version, imageId, alpineImageId, branch, dockerFileLocations, dockerJava11FileLocations, dockerJava17FileLocations def organization = 'sonatype', gitHubRepository = 'docker-nexus3', credentialsId = 'jenkins-github', @@ -54,12 +54,14 @@ node('ubuntu-zion') { dockerJava11FileLocations = [ "${pwd()}/Dockerfile.java11", - "${pwd()}/Dockerfile.rh.ubi.java11" + "${pwd()}/Dockerfile.rh.ubi.java11", + "${pwd()}/Dockerfile.alpine.java11" ] dockerJava17FileLocations = [ "${pwd()}/Dockerfile.java17", - "${pwd()}/Dockerfile.rh.ubi.java17" + "${pwd()}/Dockerfile.rh.ubi.java17", + "${pwd()}/Dockerfile.alpine.java17" ] branch = checkoutDetails.GIT_BRANCH == 'origin/main' ? 'main' : checkoutDetails.GIT_BRANCH @@ -116,6 +118,13 @@ node('ubuntu-zion') { def hash = OsTools.runSafe(this, "docker build --quiet --label base-image-ref='${baseImageReferenceStr}' --no-cache --tag ${imageName} . -f ${dockerfilePath}") imageId = hash.split(':')[1] + // Build Alpine Image if not Java 8 + if (params.java_version != OPENJDK8) { + def alpineDockerfilePath = dockerfilePath.replace("Dockerfile", "Dockerfile.alpine") + def alpineHash = OsTools.runSafe(this, "docker build --quiet --no-cache --tag ${imageName}-alpine . -f ${alpineDockerfilePath}") + alpineImageId = alpineHash.split(':')[1] + } + if (currentBuild.result == 'FAILURE') { gitHub.statusUpdate commitId, 'failure', 'build', 'Build failed' return @@ -142,14 +151,19 @@ node('ubuntu-zion') { } stage('Evaluate Policies') { - runEvaluation({ stage -> - nexusPolicyEvaluation( - iqStage: stage, - iqApplication: 'docker-nexus3', - iqScanPatterns: [[scanPattern: "container:${imageName}"]], - failBuildOnNetworkError: true, - )}, 'release') - } + runEvaluation({ stage -> + def isAlpine = alpineDockerfilePath != null && alpineDockerfilePath.contains('alpine') + def iqApplicationName = isAlpine ? 'docker-nexus3-alpine' : 'docker-nexus3' + def imageToScan = isAlpine ? "${imageName}-alpine" : imageName + + nexusPolicyEvaluation( + iqStage: stage, + iqApplication: iqApplicationName, + iqScanPatterns: [[scanPattern: "container:${imageToScan}"]], + failBuildOnNetworkError: true, + ) + }, 'release') + } if (currentBuild.result == 'FAILURE') { return @@ -198,9 +212,10 @@ node('ubuntu-zion') { ] def javaVersionSuffix = javaVersionSuffixesMap.get(params.java_version) - OsTools.runSafe(this, "docker tag ${imageId} ${organization}/${dockerHubRepository}:${version}-${javaVersionSuffix}") + // Push UBI image + OsTools.runSafe(this, "docker tag ${imageId} ${organization}/${dockerHubRepository}:${version}-${javaVersionSuffix}-ubi") if (params.java_version == OPENJDK8) { - OsTools.runSafe(this, "docker tag ${imageId} ${organization}/${dockerHubRepository}:${version}") + OsTools.runSafe(this, "docker tag ${imageId} ${organization}/${dockerHubRepository}:${version}-ubi") OsTools.runSafe(this, "docker tag ${imageId} ${organization}/${dockerHubRepository}:latest") } @@ -210,13 +225,29 @@ node('ubuntu-zion') { def dockerPushCmdsMap = [ (OPENJDK8): "docker push --all-tags ${organization}/${dockerHubRepository}", - (OPENJDK11): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_11}", - (OPENJDK17): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_17}" + (OPENJDK11): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_11}-ubi", + (OPENJDK17): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_17}-ubi" ] def dockerPushCmd = dockerPushCmdsMap.get(params.java_version) OsTools.runSafe(this, dockerPushCmd) + // Push Alpine image if not Java 8 + if (params.java_version != OPENJDK8) { + OsTools.runSafe(this, "docker tag ${alpineImageId} ${organization}/${dockerHubRepository}:${version}-${javaVersionSuffix}-alpine") + if (params.java_version == OPENJDK11) { + OsTools.runSafe(this, "docker tag ${alpineImageId} ${organization}/${dockerHubRepository}:${version}-alpine") + } + + def alpineDockerPushCmdsMap = [ + (OPENJDK11): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_11}-alpine", + (OPENJDK17): "docker push ${organization}/${dockerHubRepository}:${version}-${JAVA_17}-alpine" + ] + def alpineDockerPushCmd = alpineDockerPushCmdsMap.get(params.java_version) + + OsTools.runSafe(this, alpineDockerPushCmd) + } + response = OsTools.runSafe(this, """ curl -X POST https://hub.docker.com/v2/users/login/ \ -H 'cache-control: no-cache' -H 'content-type: application/json' \ diff --git a/Jenkinsfile-sbom-release b/Jenkinsfile-sbom-release index 9e190ff4..d988d210 100644 --- a/Jenkinsfile-sbom-release +++ b/Jenkinsfile-sbom-release @@ -7,14 +7,22 @@ @Library(['private-pipeline-library', 'jenkins-shared']) _ import groovy.json.JsonSlurper +import groovy.json.JsonBuilder IQ_URL_BASE = "https://sonatype.sonatype.app/platform" REPO_BASE_URL = "https://repo.sonatype.com/service/rest" TARGET_REPO_NAME = "sonatype-sboms" +SBOM_DEPLOYER_CREDENTIALS = "sonatype-sbom-deployer" REDHAT_SBOM_REPO_URL_BASE = "https://access.redhat.com/security/data/sbom/beta" REDHAT_CONTAINER_API_URL_BASE = "https://catalog.redhat.com/api/containers/v1" CYCLONEDX_VERSION = "1.5" SPDXMERGE_VERSION_TAG = "v0.2.0" +NEXUS3_REPORT_BY_TAG = [ + "^(\\d+\\.\\d+\\.\\d+)(-java\\d+)?-alpine\$" : "docker-nexus3-alpine", + "^(\\d+\\.\\d+\\.\\d+)(-java\\d+)?(-ubi)?\$" : "docker-nexus3" +] +DOCKER_NEXUS_IMAGE_NAME = "docker-all.repo.sonatype.com/sonatype/nexus3" +DEFAULT_NEXUS3_REPORT = "docker-nexus3" properties([ parameters([ @@ -72,12 +80,12 @@ def getComponentInfo(String componentName) { } } -def publishComponent(String buildDir, String componentName, String componentVersion, boolean cyclonedxAvailable = true) { +def publishComponentSbom(String buildDir, String componentName, String componentVersion, boolean cyclonedxAvailable = true) { def publishCommand = "curl -v -s -w 'Status: %{http_code}' -u \$NXRM_USER:\$NXRM_PASSWORD -X POST '${REPO_BASE_URL}/v1/components?repository=${TARGET_REPO_NAME}' \ -F 'raw.directory=/${componentName}/${componentVersion}/' \ -F 'raw.asset1=@${buildDir}/spdx/${componentName}-${componentVersion}-spdx.json' \ -F 'raw.asset1.filename=${componentName}-${componentVersion}-spdx.json'" - + if (cyclonedxAvailable) { publishCommand = "${publishCommand} \ -F 'raw.asset2=@${buildDir}/cyclonedx/${componentName}-${componentVersion}-cyclonedx.json' \ @@ -86,11 +94,15 @@ def publishComponent(String buildDir, String componentName, String componentVers withCredentials([ usernamePassword( - credentialsId: 'sonatype-sbom-deployer', + credentialsId: SBOM_DEPLOYER_CREDENTIALS, usernameVariable: 'NXRM_USER', passwordVariable: 'NXRM_PASSWORD') ]) { - sh(publishCommand) + def publishStatus = sh(script: publishCommand, returnStdout: true).trim() + + if( !(publishStatus ==~ "Status: 2\\d\\d") ) { + error "Could not publish SBOM of component ${componentName}:${componentVersion}" + } } } @@ -125,34 +137,38 @@ def mergeSpdxComponents(String buildDir, String finalComponentName, String final """ } +def getNexusReportName(String tag) { + for(entry in NEXUS3_REPORT_BY_TAG) { + if(tag ==~ entry.key) { + return entry.value + } + } + return DEFAULT_NEXUS3_REPORT +} + +def dockerInspectLabel(String image, String tag, String label) { + sh(script: "docker inspect ${image}:${tag} | jq -r '.[0].Config.Labels[\"${label}\"]'", returnStdout: true).trim() +} + dockerizedRunPipeline( skipVulnerabilityScan: true, pathToDockerfile: "./build-images/Dockerfile.sbom-deployer", prepare: { withSonatypeDockerRegistry() { - sh "docker pull sonatype/nexus3:${params.docker_nexus3_tag}" - env['nexusVersion'] = sh(script: "docker inspect sonatype/nexus3:${params.docker_nexus3_tag} \ - | jq -r '.[0].Config.Labels.version' ", - returnStdout: true).trim() - env['dockerImageVersion'] = sh(script: "docker inspect sonatype/nexus3:${params.docker_nexus3_tag} \ - | jq -r '.[0].Config.Labels.release' ", - returnStdout: true).trim() - env['ubiImageId'] = sh(script: "docker inspect sonatype/nexus3:${params.docker_nexus3_tag} \ - | jq -r '.[0].Config.Labels.\"base-image-ref\"' \ - | sed -En 's/^.+image=(.+)\$/\\1/p'", - returnStdout: true).trim() + sh "docker pull ${DOCKER_NEXUS_IMAGE_NAME}:${params.docker_nexus3_tag}" + + def baseImageRef = dockerInspectLabel(DOCKER_NEXUS_IMAGE_NAME, params.docker_nexus3_tag, "base-image-ref") + + env['imageTag'] = params.docker_nexus3_tag + env['nexusVersion'] = dockerInspectLabel(DOCKER_NEXUS_IMAGE_NAME, params.docker_nexus3_tag, "version") + env['dockerImageVersion'] = dockerInspectLabel(DOCKER_NEXUS_IMAGE_NAME, params.docker_nexus3_tag, "release") + env['ubiImageId'] = baseImageRef.contains("image=") ? baseImageRef.split("image=")[1] : "" } }, run: { - def buildDir = "./.sbom-build/job-${env.BUILD_NUMBER}" - def ubiImageName = sh(script: "curl -s -X 'GET' '${REDHAT_CONTAINER_API_URL_BASE}/images/id/${env.ubiImageId}' -H 'accept: application/json' \ - | jq -r '.brew.build' \ - | sed -En 's/(ubi[0-9]+-minimal)-container-([0-9]+\\.[0-9]+-[0-9]+\\.?[0-9]*)/\\1-\\2/p'", - returnStdout: true).trim() - def ubiImageVersion = sh(script: "curl -s -X 'GET' '${REDHAT_CONTAINER_API_URL_BASE}/images/id/${env.ubiImageId}' -H 'accept: application/json' \ - | jq -r '.brew.build' \ - | sed -En 's/ubi[0-9]+-minimal-container-([0-9]+\\.[0-9]+-[0-9]+\\.?[0-9]*)/\\1/p'", - returnStdout: true).trim() + def buildDir = "./.sbom-build/job-${env.BUILD_NUMBER}/v${env.imageTag}" + def jsonSlurper = new JsonSlurper() + def nexusReportName = getNexusReportName(env.imageTag) // Download SBOMs sh "mkdir -p ${buildDir}/spdx && mkdir -p ${buildDir}/cyclonedx" @@ -161,26 +177,36 @@ dockerizedRunPipeline( getComponentSbom(buildDir, "nexus-internal", env.nexusVersion) // Get nxrm-db-migrator SBOM getComponentSbom(buildDir, "nxrm-db-migrator", env.nexusVersion) - // Get docker-nexus3 SBOM - getComponentSbom(buildDir, "docker-nexus3", env.dockerImageVersion) + // Get we SBOM + getComponentSbom(buildDir, nexusReportName, env.dockerImageVersion) + // Get UBI Minimal SBOM - def ubiSbomAvailable = getUbiImageSbom(buildDir, ubiImageName, ubiImageVersion) + boolean ubiSbomAvailable = env.ubiImageId?.trim() ? true : false + def ubiImageName = ubiSbomAvailable ? sh(script: "curl -s -X 'GET' '${REDHAT_CONTAINER_API_URL_BASE}/images/id/${env.ubiImageId}' -H 'accept: application/json' \ + | jq -r '.brew.build' \ + | sed -En 's/(ubi[0-9]+-minimal)-container-([0-9]+\\.[0-9]+-[0-9]+\\.?[0-9]*)/\\1-\\2/p'", + returnStdout: true).trim() : "" + def ubiImageVersion = ubiSbomAvailable ? sh(script: "curl -s -X 'GET' '${REDHAT_CONTAINER_API_URL_BASE}/images/id/${env.ubiImageId}' -H 'accept: application/json' \ + | jq -r '.brew.build' \ + | sed -En 's/ubi[0-9]+-minimal-container-([0-9]+\\.[0-9]+-[0-9]+\\.?[0-9]*)/\\1/p'", + returnStdout: true).trim() : "" + ubiSbomAvailable = ubiSbomAvailable ? getUbiImageSbom(buildDir, ubiImageName, ubiImageVersion) : false sh "echo 'Available SPDX SBOMS' && ls ${buildDir}/spdx" sh "echo 'Available CycloneDx SBOMS' && ls ${buildDir}/cyclonedx" // Merge supported sboms - def dockerImageNamespace = sh(script: "cat ${buildDir}/spdx/docker-nexus3-${env.dockerImageVersion}-spdx.json | jq -r '.documentNamespace'", returnStdout: true).trim() - mergeSpdxComponents(buildDir, "docker-nexus3-aggregate", env.dockerImageVersion, dockerImageNamespace) + def dockerImageNamespace = sh(script: "cat ${buildDir}/spdx/${nexusReportName}-${env.dockerImageVersion}-spdx.json | jq -r '.documentNamespace'", returnStdout: true).trim() + mergeSpdxComponents(buildDir, "${nexusReportName}-aggregate", env.dockerImageVersion, dockerImageNamespace) // Publish SBOMs if (ubiSbomAvailable) { publishComponent(buildDir, "ubi-minimal", ubiImageVersion, false) } - publishComponent(buildDir, "nexus-internal", env.nexusVersion) - publishComponent(buildDir, "nxrm-db-migrator", env.nexusVersion) - publishComponent(buildDir, "docker-nexus3", env.dockerImageVersion) - publishComponent(buildDir, "docker-nexus3-aggregate", env.dockerImageVersion, false) + publishComponentSbom(buildDir, "nexus-internal", env.nexusVersion) + publishComponentSbom(buildDir, "nxrm-db-migrator", env.nexusVersion) + publishComponentSbom(buildDir, nexusReportName, env.dockerImageVersion) + publishComponentSbom(buildDir, "${nexusReportName}-aggregate", env.dockerImageVersion, false) sh "rm -rf '${buildDir}'" } diff --git a/README.md b/README.md index 27fb3bef..1b42af06 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,18 @@ In addition to the Universal Base Image, we can build images based on: * Red Hat Enterprise Linux: [Dockerfile.rh.el](https://github.com/sonatype/docker-nexus3/blob/main/Dockerfile.rh.el) * CentOS: [Dockerfile.rh.centos](https://github.com/sonatype/docker-nexus3/blob/main/Dockerfile.rh.centos) +## Alpine Image + +An Alpine-based container image can be created using [Dockerfile.alpine.java11](https://github.com/sonatype/docker-nexus3/blob/main/Dockerfile.alpine.java11) This Dockerfile is built to leverage the minimalistic and efficient nature of Alpine Linux, emphasizing fewer dependencies to achieve a cleaner SBOM (Software Bill of Materials) and a stronger security posture. + +The Alpine-based container image includes minimal dependencies and uses an ENTRYPOINT script to ensure the application runs with the necessary permissions. It is optimized for rapid deployment and efficient resource usage. + +The Alpine-based container image is available from Docker Hub and can be pulled using the following tags: + +- sonatype/nexus3:3.XX.y-alpine (runs Java 11) +- sonatype/nexus3:3.XX.y-java11-alpine +- sonatype/nexus3:3.XX.y-java17-alpine + ## Notes * Our [system requirements](https://help.sonatype.com/display/NXRM3/System+Requirements) should be taken into account when provisioning the Docker container.