diff --git a/Dockerfile b/Dockerfile index 2bb817f..c5966f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,16 @@ -FROM centos:centos7 +FROM --platform=$TARGETPLATFORM rockylinux:8.9 # Define args and set a default value ARG maintainer=tier ARG imagename=shibboleth_sp ARG version=3.4.1 -ARG TIERVERSION=20230612 +ARG TIERVERSION=20240308-Rocky8-MA MAINTAINER $maintainer LABEL Vendor="Internet2" LABEL ImageType="Base" LABEL ImageName=$imagename -LABEL ImageOS=centos7 +LABEL ImageOS=rocky8 LABEL Version=$version LABEL Build docker build --rm --tag $maintainer/$imagename . @@ -23,16 +23,20 @@ RUN ln -sf /usr/share/zoneinfo/UTC /etc/localtime \ && echo "NETWORKING=yes" > /etc/sysconfig/network RUN rm -fr /var/cache/yum/* && yum clean all && yum -y install --setopt=tsflags=nodocs epel-release && yum -y update && \ - yum -y install net-tools wget curl tar unzip mlocate logrotate strace telnet man vim rsyslog cron httpd mod_ssl dos2unix cronie supervisor && \ + yum -y install net-tools wget curl tar unzip mlocate logrotate strace telnet man vim rsyslog httpd mod_ssl dos2unix cronie supervisor && \ yum clean all #install shibboleth, cleanup httpd COPY container_files/shibboleth/shibboleth.repo /etc/yum.repos.d/security:shibboleth.repo -RUN yum -y install shibboleth-$version-\*.x86_64 \ +RUN yum -y install shibboleth-$version-\* \ && yum clean all ADD ./container_files/httpd/*.conf /etc/httpd/conf.d/ ADD ./container_files/shibboleth/* /etc/shibboleth/ + +RUN openssl req -new -nodes -newkey rsa:2048 -subj "/commonName=localhost.localdomain" -batch -keyout /etc/pki/tls/private/localhost.key -out localhost.csr +RUN openssl x509 -req -days 1825 -in localhost.csr -signkey /etc/pki/tls/private/localhost.key -out /etc/pki/tls/certs/localhost.crt + RUN sed -i '/^[[:space:]]*CustomLog/s/^/#/' /etc/httpd/conf/httpd.conf # add a basic page to shibb's default protected directory diff --git a/Jenkinsfile b/Jenkinsfile index dade7c5..ba7b33e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,122 +1,186 @@ -node('docker') { - stage 'Checkout' +pipeline { + agent { node { label 'docker-multi-arch' } } + environment { + maintainer = "t" + imagename = 's' + tag = 'l' + DOCKERHUBPW=credentials('tieradmin-dockerhub-pw') - checkout scm - - stage 'Acquire util' - - sh 'mkdir -p tmp' - dir('tmp'){ - git([ url: "https://github.internet2.edu/docker/util.git", - credentialsId: "jenkins-github-access-token" ]) - sh 'ls' - sh 'rm -rf ../bin/windows/' - sh 'mv bin/* ../bin/.' - } - stage 'Setting build context' - - def maintainer = maintainer() - def previous_maintainer = previous_maintainer() - def imagename = imagename() - def tag - - // Tag images created on master branch with 'latest' - if(env.BRANCH_NAME == "master"){ - tag = "latest" - }else{ - tag = env.BRANCH_NAME } - - if(!imagename){ - echo "You must define an imagename in common.bash" - currentBuild.result = 'FAILURE' - } - if(maintainer){ - echo "Building ${imagename}:${tag} for ${maintainer}" - } - - stage 'Build' - try{ - sh 'bin/rebuild.sh &> debug' - } catch(error) { - def error_details = readFile('./debug'); - def message = "BUILD ERROR: There was a problem building the shibboleth-sp image. \n\n ${error_details}" - sh "rm -f ./debug" - handleError(message) - } - stage 'Start container' - - sh 'bin/ci-run.sh' - - stage 'Tests' - - try{ - sh 'bin/test.sh &> debug' - } catch(error) { - def error_details = readFile('./debug'); - def message = "BUILD ERROR: There was a problem testing ${imagename}:${tag}. \n\n ${error_details}" - sh "rm -f ./debug" - handleError(message) + stages { + stage('Setting build context') { + steps { + script { + maintainer = maintain() + imagename = imagename() + if(env.BRANCH_NAME == "master") { + tag = "latest" + } else { + tag = env.BRANCH_NAME + } + if(!imagename){ + echo "You must define an imagename in common.bash" + currentBuild.result = 'FAILURE' + } + sh 'mkdir -p tmp && mkdir -p bin' + dir('tmp'){ + git([ url: "https://github.internet2.edu/docker/util.git", credentialsId: "jenkins-github-access-token" ]) + sh 'rm -rf ../bin/*' + sh 'mv ./bin/* ../bin/.' + } + // Build and test scripts expect that 'tag' is present in common.bash. This is necessary for both Jenkins and standalone testing. + // We don't care if there are more 'tag' assignments there. The latest one wins. + sh "echo >> common.bash ; echo \"tag=\\\"${tag}\\\"\" >> common.bash ; echo common.bash ; cat common.bash" + } + } + } + stage('Clean') { + steps { + script { + try{ + sh 'bin/destroy.sh >> debug' + } catch(error) { + def error_details = readFile('./debug'); + def message = "BUILD ERROR: There was a problem building the Base Image. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Build') { + steps { + script { + try{ + sh 'docker login -u tieradmin -p $DOCKERHUBPW' + // fails if already exists + // sh 'docker buildx create --use --name multiarch --append' + sh 'docker buildx inspect --bootstrap' + sh 'docker buildx ls' + sh "docker buildx build --platform linux/amd64 -t ${imagename}_${tag} --load ." + sh "docker buildx build --platform linux/arm64 -t ${imagename}_${tag}:arm64 --load ." + } catch(error) { + def error_details = readFile('./debug'); + def message = "BUILD ERROR: There was a problem building ${maintainer}/${imagename}:${tag}. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Test') { + steps { + script { + try { + echo "Starting tests..." + sh 'bats tests' + // echo "Skipping tests for now" + } catch (error) { + def error_details = readFile('./debug') + def message = "BUILD ERROR: There was a problem testing ${maintainer}/${imagename}:${tag}. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Scan') { + steps { + script { + try { + echo "Starting security scan..." + // Install trivy and HTML template + sh 'curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.31.1' + sh 'curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/html.tpl > html.tpl' + + // Scan container for all vulnerability levels + echo "Scanning for all vulnerabilities..." + sh 'mkdir -p reports' + sh "trivy image --ignore-unfixed --vuln-type os,library --severity CRITICAL,HIGH --no-progress --security-checks vuln --format template --template '@html.tpl' -o reports/container-scan.html ${imagename}_${tag}" + sh "trivy image --ignore-unfixed --vuln-type os,library --severity CRITICAL,HIGH --no-progress --security-checks vuln --format template --template '@html.tpl' -o reports/container-scan-arm.html ${imagename}_${tag}:arm64" + publishHTML target : [ + allowMissing: true, + alwaysLinkToLastBuild: true, + keepAll: true, + reportDir: 'reports', + reportFiles: 'container-scan.html', + reportName: 'Security Scan', + reportTitles: 'Security Scan' + ] + publishHTML target : [ + allowMissing: true, + alwaysLinkToLastBuild: true, + keepAll: true, + reportDir: 'reports', + reportFiles: 'container-scan-arm.html', + reportName: 'Security Scan (ARM)', + reportTitles: 'Security Scan (ARM)' + ] + // Scan again and fail on CRITICAL vulns + //below can be temporarily commented to prevent build from failing + echo "Scanning for CRITICAL vulnerabilities only (fatal)..." + sh "trivy image --ignore-unfixed --vuln-type os,library --exit-code 1 --severity CRITICAL ${imagename}_${tag}" + sh "trivy image --ignore-unfixed --vuln-type os,library --exit-code 1 --severity CRITICAL ${imagename}_${tag}:arm64" + //echo "Skipping scan for CRITICAL vulnerabilities (temporary)..." + } catch(error) { + def error_details = readFile('./debug'); + def message = "BUILD ERROR: There was a problem scanning ${imagename}:${tag}. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Push') { + steps { + script { + sh 'docker login -u tieradmin -p $DOCKERHUBPW' + // fails if already exists + // sh 'docker buildx create --use --name multiarch --append' + sh 'docker buildx inspect --bootstrap' + sh 'docker buildx ls' + echo "Pushing image to dockerhub..." + sh "docker buildx build --push --platform linux/arm64,linux/amd64 -t ${maintainer}/${imagename}:${tag} ." + } + } + } + stage('Cleanup') { + steps { + script { + try{ + echo "Cleaning up artifacts from the build..." + sh 'tests/cleanup.sh' + } catch(error) { + def error_details = readFile('./debug'); + def message = "BUILD ERROR: There was a problem with cleanup of the image. \n\n ${error_details}" + sh "rm -f ./debug" + handleError(message) + } + } + } + } + stage('Notify') { + steps{ + echo "$maintainer" + slackSend color: 'good', message: "$maintainer/$imagename:$tag pushed to DockerHub" + } + } } - - stage 'Scan' - - try { - // Install trivy and HTML template - sh 'curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin v0.31.1' - sh 'curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/html.tpl > html.tpl' - - // Scan container for all vulnerability levels - sh 'mkdir -p reports' - sh "trivy image --ignore-unfixed --vuln-type os,library --severity CRITICAL,HIGH --no-progress --security-checks vuln --format template --template '@html.tpl' -o reports/container-scan.html ${maintainer}/${imagename}:latest" - publishHTML target : [ - allowMissing: true, - alwaysLinkToLastBuild: true, - keepAll: true, - reportDir: 'reports', - reportFiles: 'container-scan.html', - reportName: 'Security Scan', - reportTitles: 'Security Scan' - ] - - // Scan again and fail on CRITICAL vulns - sh "trivy image --ignore-unfixed --vuln-type os,library --exit-code 1 --severity CRITICAL ${maintainer}/${imagename}:latest" - } catch(error) { - def error_details = readFile('./debug'); - def message = "BUILD ERROR: There was a problem scanning ${imagename}:${tag}. \n\n ${error_details}" - sh "rm -f ./debug" - handleError(message) + post { + always { + echo 'Done Building.' + } + failure { + // slackSend color: 'good', message: "Build failed" + handleError("BUILD ERROR: There was a problem building ${maintainer}/${imagename}:${tag}.") + } } - - stage 'Stop container' - - sh 'bin/ci-stop.sh' - - stage 'Push' - docker.withRegistry('https://registry.hub.docker.com/', "dockerhub-$previous_maintainer") { - def baseImg = docker.build("$maintainer/$imagename", "--no-cache .") - baseImg.push("$tag") - } - - docker.withRegistry('https://registry.hub.docker.com/', "dockerhub-$previous_maintainer") { - def altImg = docker.build("$previous_maintainer/$imagename", "--no-cache .") - altImg.push("$tag") - } - - - stage 'Notify' - - slackSend color: 'good', message: "$maintainer/$imagename:$tag pushed to DockerHub" } -def maintainer() { - def matcher = readFile('common.bash') =~ 'maintainer="(.+)"' - matcher ? matcher[0][1] : 'tier' -} -def previous_maintainer() { - def matcher = readFile('common.bash') =~ 'previous_maintainer="(.+)"' +def maintain() { + def matcher = readFile('common.bash') =~ 'maintainer="(.+)"' matcher ? matcher[0][1] : 'tier' } @@ -129,5 +193,7 @@ def handleError(String message){ echo "${message}" currentBuild.setResult("FAILED") slackSend color: 'danger', message: "${message}" + //step([$class: 'Mailer', notifyEveryUnstableBuild: true, recipients: 'pcaskey@internet2.edu', sendToIndividuals: true]) sh 'exit 1' } + diff --git a/container_files/shibboleth/shibboleth.repo b/container_files/shibboleth/shibboleth.repo index 8994175..65cdd34 100644 --- a/container_files/shibboleth/shibboleth.repo +++ b/container_files/shibboleth/shibboleth.repo @@ -1,8 +1,8 @@ [shibboleth] -name=Shibboleth (CentOS_7) +name=Shibboleth (rockylinux8) # Please report any problems to https://shibboleth.atlassian.net/jira type=rpm-md -mirrorlist=https://shibboleth.net/cgi-bin/mirrorlist.cgi/CentOS_7 +mirrorlist=https://shibboleth.net/cgi-bin/mirrorlist.cgi/rockylinux8 gpgcheck=1 gpgkey=https://shibboleth.net/downloads/service-provider/RPMS/repomd.xml.key https://shibboleth.net/downloads/service-provider/RPMS/cantor.repomd.xml.key diff --git a/tests/clairscan.sh b/tests/clairscan.sh deleted file mode 100755 index a06ea78..0000000 --- a/tests/clairscan.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash - -startsecs=$(date +'%s') -starttime=$(date +%H:%M:%S) - -echo 'starting:' ${starttime} - -#ensure clair-scanner -if [ ! -s ./clair-scanner ]; then - echo 'downloading curl-scanner...' - curl -s -L -o ./clair-scanner https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64 - chmod 755 clair-scanner -else - echo 'using existing clair-scanner...' -fi - -#ensure DB container -echo 'ensuring a fresh clair-db container...' -docker ps | grep clair-db &>/dev/null -if [ $? == "0" ]; then - echo 'removing existing clair-db container...' - docker kill db &>/dev/null - docker rm db &>/dev/null - docker run -p 5432:5432 -d --name db arminc/clair-db:latest &>/dev/null -else - docker run -p 5432:5432 -d --name db arminc/clair-db:latest &>/dev/null -fi -sleep 30 - -#ensure clair-scan container -echo 'ensuring a fresh clair-scan container...' -docker ps | grep clair-local-scan &>/dev/null -if [ $? == "0" ]; then - echo 'removing existing clair-scan container...' - docker kill clair &>/dev/null - docker rm clair &>/dev/null - docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.5 &>/dev/null -else - docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.5 &>/dev/null -fi -sleep 30 - -#get ip where clair-scanner will listen -clairip=$(/sbin/ifconfig docker0 | grep 'inet ' | sed 's/^[[:space:]]*//g' | cut -f 2 -d ' ' | sed 's/^[[:space:]]*//g') -echo 'sending ip addr' ${clairip} 'to clair-scan server...' - -#run scan -echo 'running scan...' -./clair-scanner --ip ${clairip} $1 -retcode=$? - -#eval results -if [ $retcode == '0' ]; then - echo 'scan found nothing.' -else - echo 'scan found issues.' -fi - -#cleanup -echo 'removing temporary containers...' -docker kill clair &>/dev/null -docker rm clair &>/dev/null -docker kill db &>/dev/null -docker rm db &>/dev/null - -endsecs=$(date +'%s') -endtime=$(date +%H:%M:%S) -echo 'finished:' $endtime ' ('$((endsecs - startsecs)) 'seconds)' -echo "" - -#pass along return code from scan -exit $retcode - diff --git a/tests/cleanup.sh b/tests/cleanup.sh new file mode 100755 index 0000000..fabb9d3 --- /dev/null +++ b/tests/cleanup.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +. ./common.bash + +result=$(docker ps -a | grep ${imagename}_${tag}) +if [ ! -z "$result" ]; then + echo "removing existing containers" + docker rm -f $(docker ps -a | grep ${imagename}_${tag} | awk '{print $1}') +fi +docker rmi -f ${imagename}_${tag} + +result2=$(docker ps -a | grep ${imagename}_${tag}:arm64) +if [ ! -z "$result2" ]; then + echo "removing existing containers" + docker rm -f $(docker ps -a | grep ${imagename}_${tag}:arm64 | awk '{print $1}') +fi +docker rmi -f ${imagename}_${tag}:arm64 + diff --git a/tests/image.bats b/tests/image.bats index eaff6b1..0731294 100644 --- a/tests/image.bats +++ b/tests/image.bats @@ -3,26 +3,27 @@ load ../common @test "Shibd binary available" { - docker run -i $maintainer/$imagename find /usr/sbin/shibd + docker run -i ${imagename}_${tag} find /usr/sbin/shibd + docker run -i ${imagename}_${tag}:arm64 find /usr/sbin/shibd } @test "Shibboleth root available" { - docker run -i $maintainer/$imagename find /etc/shibboleth + docker run -i ${imagename}_${tag} find /etc/shibboleth + docker run -i ${imagename}_${tag}:arm64 find /etc/shibboleth } @test "Sample attribute map available" { - docker run -i $maintainer/$imagename find /etc/shibboleth/attribute-map.xml + docker run -i ${imagename}_${tag} find /etc/shibboleth/attribute-map.xml + docker run -i ${imagename}_${tag}:arm64 find /etc/shibboleth/attribute-map.xml } @test "Includes InCommon cert" { - docker run -i $maintainer/$imagename find /etc/shibboleth/inc-md-cert.pem + docker run -i ${imagename}_${tag} find /etc/shibboleth/inc-md-cert.pem + docker run -i ${imagename}_${tag}:arm64 find /etc/shibboleth/inc-md-cert.pem } @test "Includes startup script" { - docker run -i $maintainer/$imagename find /usr/local/bin/startup.sh + docker run -i ${imagename}_${tag} find /usr/local/bin/startup.sh + docker run -i ${imagename}_${tag}:arm64 find /usr/local/bin/startup.sh } -#@test "070 There are no known security vulnerabilities" { -# ./tests/clairscan.sh ${maintainer}/${imagename}:latest -#} - diff --git a/tests/running.bats b/tests/running.bats deleted file mode 100644 index 781715b..0000000 --- a/tests/running.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -load ../common - -# These tests assume the pipeline will start and stop the container. - -@test "Leaves running process" { - result="$(docker ps | grep $imagename)" - [ "$result" != '' ] -} - -@test "Exposes running HTTP service" { - result="$(docker exec -i $imagename curl http://localhost/)" - [ "$result" != '' ] -} - -@test "Exposes running SSO process" { - result="$(docker exec -i $imagename curl http://localhost/Shibboleth.sso/Status)" - [ "$result" != '' ] -} -