diff --git a/clamav/1.4/alpine/Dockerfile b/clamav/1.4/alpine/Dockerfile new file mode 100644 index 0000000..e83910c --- /dev/null +++ b/clamav/1.4/alpine/Dockerfile @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2020 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +# hadolint ignore=DL3007 latest is the latest stable for alpine +FROM index.docker.io/library/alpine:latest AS builder + +WORKDIR /src + +COPY . /src/ + +# hadolint ignore=DL3008 We want the latest stable versions +RUN apk update && apk upgrade \ + && \ + apk add --no-cache \ + bsd-compat-headers \ + cmake \ + file \ + g++ \ + libtool \ + linux-headers \ + make \ + musl-fts-dev \ + # Clamav dependencies provided by alpine + bzip2-dev \ + check-dev \ + curl-dev \ + json-c-dev \ + libmilter-dev \ + libxml2-dev \ + ncurses-dev \ + ncurses-dev \ + openssl-dev \ + pcre2-dev \ + zlib-dev \ + # For the tests + python3 \ + py3-pytest \ + # For Rust/Cargo + cargo \ + rust \ + && \ + mkdir -p "./build" && cd "./build" && \ + cmake .. \ + -D CMAKE_BUILD_TYPE="Release" \ + -D CMAKE_INSTALL_PREFIX="/usr" \ + -D CMAKE_INSTALL_LIBDIR="/usr/lib" \ + -D APP_CONFIG_DIRECTORY="/etc/clamav" \ + -D DATABASE_DIRECTORY="/var/lib/clamav" \ + -D ENABLE_CLAMONACC=OFF \ + -D ENABLE_EXAMPLES=OFF \ + -D ENABLE_MILTER=ON \ + -D ENABLE_MAN_PAGES=OFF \ + -D ENABLE_STATIC_LIB=OFF \ + -D ENABLE_JSON_SHARED=ON \ + && \ + make DESTDIR="/clamav" -j$(($(nproc) - 1)) install && \ + rm -r \ + "/clamav/usr/lib/pkgconfig/" \ + && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/clamd.pid|" \ + -e "s|.*\(LocalSocket\) .*|\1 /tmp/clamd.sock|" \ + -e "s|.*\(TCPSocket\) .*|\1 3310|" \ + -e "s|.*\(TCPAddr\) .*|#\1 0.0.0.0|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/clamd.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + "/clamav/etc/clamav/clamd.conf.sample" > "/clamav/etc/clamav/clamd.conf" && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/freshclam.pid|" \ + -e "s|.*\(DatabaseOwner\) .*|\1 clamav|" \ + -e "s|^\#\(UpdateLogFile\) .*|\1 /var/log/clamav/freshclam.log|" \ + -e "s|^\#\(NotifyClamd\).*|\1 /etc/clamav/clamd.conf|" \ + -e "s|^\#\(ScriptedUpdates\).*|\1 yes|" \ + "/clamav/etc/clamav/freshclam.conf.sample" > "/clamav/etc/clamav/freshclam.conf" && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/clamav-milter.pid|" \ + -e "s|.*\(MilterSocket\) .*|\1 inet:7357|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/milter.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + -e "s|.*\(\ClamdSocket\) .*|\1 unix:/tmp/clamd.sock|" \ + "/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || \ + exit 1 \ + && \ + ctest -V + +FROM index.docker.io/library/alpine:latest + +LABEL maintainer="ClamAV bugs " + +EXPOSE 3310 +EXPOSE 7357 + +ENV TZ Etc/UTC + +RUN apk add --no-cache \ + fts \ + libstdc++ \ + tini \ + tzdata \ + # Clamav dependencies provided by alpine + json-c \ + libbz2 \ + libcurl \ + libmilter \ + libxml2 \ + ncurses-libs \ + pcre2 \ + zlib \ + && \ + addgroup -S "clamav" && \ + adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -u 100 -S "clamav" && \ + install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \ + chown -R clamav:clamav /var/lib/clamav + +COPY --from=builder "/clamav" "/" +COPY "./scripts/clamdcheck.sh" "/usr/local/bin/" +COPY "./scripts/docker-entrypoint.sh" "/init" +COPY "./scripts/docker-entrypoint-unprivileged.sh" "/init-unprivileged" + +HEALTHCHECK --start-period=6m CMD clamdcheck.sh + +ENTRYPOINT [ "/init" ] diff --git a/clamav/1.4/alpine/Jenkinsfile b/clamav/1.4/alpine/Jenkinsfile new file mode 100644 index 0000000..4d9c545 --- /dev/null +++ b/clamav/1.4/alpine/Jenkinsfile @@ -0,0 +1,176 @@ +properties([ + parameters([ + string(name: 'DOCKER_REGISTRY', defaultValue: 'registry.hub.docker.com', description: 'The Docker registry to use'), + string(name: 'REGISTRY_CREDS', defaultValue: 'dockerhub', description: 'The Jenkins credentials ID for the given registry'), + string(name: 'WEBEX_SPACE_ID', defaultValue: 'b204c1a0-6862-11e8-9dbc-93ef3cfef186', description: 'ID of Webex space to push pass/fail notifications'), + string(name: 'NAMESPACE', defaultValue: 'clamav', description: 'The docker namespace to use'), + string(name: 'IMAGE_NAME', defaultValue: 'clamav', description: 'The docker image name to use'), + string(name: 'REPOSITORY', defaultValue: 'https://github.com/Cisco-Talos/clamav.git', description: 'The repository from which to build'), + string(name: 'BRANCH', defaultValue: 'rel/1.4', description: 'The repository branch for this build'), + string(name: 'FULL_VERSION', defaultValue: '1.4.0', description: 'Full version in X.Y.Z format'), + string(name: 'FEATURE_VERSION', defaultValue: '1.4', description: 'Feature version in X.Y format'), + booleanParam(name: 'IS_LATEST', defaultValue: true, description: 'If "true", will also publish to :latest, and :stable tags.'), + ]), + disableConcurrentBuilds(), + buildDiscarder(logRotator( + artifactDaysToKeepStr: '10', + artifactNumToKeepStr: '10', + daysToKeepStr: '30', + numToKeepStr: '20')) +]) + +node('docker') { + cleanWs() + + try { + // Checkout the ClamAV source code + checkout([ + $class: 'GitSCM', branches: [[name: "${params.BRANCH}"]], + doGenerateSubmoduleConfigurations: false, + extensions: [ + [$class: 'RelativeTargetDirectory', relativeTargetDir: '.'], + [$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true] + ], + submoduleCfg: [], userRemoteConfigs: [[url: "${params.REPOSITORY}"]] + ]) + + // Remove the Dockerfile and scripts from the clamav repo, if any. + sh """ + rm -rf ./Dockerfile ./dockerfiles + """ + + // Checkout the current repo + dir(path: 'clamav-docker') { + checkout scm + } + + // Use the Dockerfile and scripts from this repo. + sh """ + cp -r clamav-docker/clamav/${params.FEATURE_VERSION}/alpine/Dockerfile clamav-docker/clamav/${params.FEATURE_VERSION}/alpine/scripts . + """ + + stage('Build Image') { + withVault([vaultSecrets: [[ path: "clamavbuild-kv/${params.REGISTRY_CREDS}", engineVersion: 1, secretValues: + [[envVar: 'DOCKER_USER', vaultKey: 'username'],[envVar: 'DOCKER_PASSWD', vaultKey: 'password']]]]]) { + // Make sure we have the latest base image. + sh """ + docker pull alpine:latest + """ + + // Login to docker hub + sh """ + echo "\${_passwd:-\${DOCKER_PASSWD}}" | \ + docker login --password-stdin --username "${DOCKER_USER}" "${params.DOCKER_REGISTRY}" + """ + + // + // Build the following images: + // - X.Y.Z-R, X.Y.Z-R_base + // - X.Y.Z, X.Y.Z_base + // - X.Y, X.Y_base + // + // And maybe also: + // - latest, latest_base + // - stable, stable_base + // + + // Build X.Y.Z-R_base image. + sh """ + docker build --no-cache --tag "${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base" . + """ + + // Publish X.Y.Z-R_base tag + sh """ + docker image tag ${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base + """ + + // Publish X.Y.Z_base tag + sh """ + docker image tag ${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}_base + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}_base + """ + + // Publish X.Y_base tag + sh """ + docker image tag ${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION}_base + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION}_base + """ + + if (params.IS_LATEST) { + // Create & Publish 'stable_base' and 'latest_base' tags. + sh """ + docker image tag ${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable_base + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable_base + + docker image tag ${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest_base + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest_base + """ + } + + // The update_db_image.sh script will query for tags during the update process. + // So give the registry a little time to add the X.Y.Z-R_base image. + sh """ + sleep 20 + """ + + // Pull the X.Y.Z-R_base image, update the DB, and push it out as X.Y.Z-R (without the _base suffix) + sh """ + DOCKER_REGISTRY="${params.DOCKER_REGISTRY}" \ + CLAMAV_DOCKER_IMAGE="${params.IMAGE_NAME}" \ + ./scripts/update_db_image.sh -t ${params.FULL_VERSION}-${BUILD_NUMBER}_base -n ${params.NAMESPACE} + """ + + // Login to docker hub again, because the update_db_image.sh script removed our creds in its cleanup stage + sh """ + echo "\${_passwd:-\${DOCKER_PASSWD}}" | \ + docker login --password-stdin --username "${DOCKER_USER}" "${params.DOCKER_REGISTRY}" + """ + + // Publish X.Y.Z tag (without the _base suffix) + sh """ + docker image tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION} + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION} + """ + + // Publish X.Y tag (without the _base suffix) + sh """ + docker image tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION} + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION} + """ + + if (params.IS_LATEST) { + // Create & Publish 'stable' and 'latest' tags. + sh """ + docker image tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable + + docker image tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest + docker image push ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest + """ + } + + // log-out (again) + sh """ + docker logout "${params.DOCKER_REGISTRY}" + """ + } + } + + } catch(err) { + // log-out, if required + sh """ + docker logout "${params.DOCKER_REGISTRY}" || true + """ + + currentBuild.result = "FAILED" + sparkSend( + message: "Docker build of ${params.FULL_VERSION} from ${params.REPOSITORY} branch ${params.BRANCH} for ${params.NAMESPACE}/${params.IMAGE_NAME} [FAILED](${BUILD_URL})", + spaceList: [[spaceName: "ClamAV Jenkins", spaceId: "${params.WEBEX_SPACE_ID}"]], credentialsId: 'clambuilder', messageType: 'markdown') + throw err + } + + sparkSend( + message: "Docker build of ${params.FULL_VERSION} from ${params.REPOSITORY} branch ${params.BRANCH} for ${params.NAMESPACE}/${params.IMAGE_NAME} [PASSED](${BUILD_URL})", + spaceList: [[spaceName: "ClamAV Jenkins", spaceId: "${params.WEBEX_SPACE_ID}"]], credentialsId: 'clambuilder', messageType: 'markdown') +} diff --git a/clamav/1.4/alpine/scripts/clamdcheck.sh b/clamav/1.4/alpine/scripts/clamdcheck.sh new file mode 100755 index 0000000..e7e53a6 --- /dev/null +++ b/clamav/1.4/alpine/scripts/clamdcheck.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -eu + +if [ "${CLAMAV_NO_CLAMD:-}" != "false" ]; then + if [ "$(echo "PING" | nc localhost 3310)" != "PONG" ]; then + echo "ERROR: Unable to contact server" + exit 1 + fi + + echo "Clamd is up" +fi + +exit 0 diff --git a/clamav/1.4/alpine/scripts/docker-entrypoint-unprivileged.sh b/clamav/1.4/alpine/scripts/docker-entrypoint-unprivileged.sh new file mode 100755 index 0000000..9ef75e6 --- /dev/null +++ b/clamav/1.4/alpine/scripts/docker-entrypoint-unprivileged.sh @@ -0,0 +1,72 @@ +#!/sbin/tini /bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# +# A beginning user should be able to docker run image bash (or sh) without +# needing to learn about --entrypoint +# https://github.com/docker-library/official-images#consistency + +set -eu + +# run command if it is not starting with a "-" and is an executable in PATH +if [ "${#}" -gt 0 ] && \ + [ "${1#-}" = "${1}" ] && \ + command -v "${1}" > "/dev/null" 2>&1; then + # Ensure healthcheck always passes + CLAMAV_NO_CLAMD="true" exec "${@}" +else + if [ "${#}" -ge 1 ] && \ + [ "${1#-}" != "${1}" ]; then + # If an argument starts with "-" pass it to clamd specifically + exec clamd "${@}" + fi + # else default to running clamav's servers + + # Ensure we have some virus data, otherwise clamd refuses to start + if [ ! -f "/var/lib/clamav/main.cvd" ]; then + echo "Updating initial database" + freshclam --foreground --stdout + fi + + if [ "${CLAMAV_NO_FRESHCLAMD:-false}" != "true" ]; then + echo "Starting Freshclamd" + freshclam \ + --checks="${FRESHCLAM_CHECKS:-1}" \ + --daemon \ + --foreground \ + --stdout \ + --user="clamav" \ + & + fi + + if [ "${CLAMAV_NO_CLAMD:-false}" != "true" ]; then + echo "Starting ClamAV" + if [ -S "/tmp/clamd.sock" ]; then + unlink "/tmp/clamd.sock" + fi + clamd --foreground & + while [ ! -S "/tmp/clamd.sock" ]; do + if [ "${_timeout:=0}" -gt "${CLAMD_STARTUP_TIMEOUT:=1800}" ]; then + echo + echo "Failed to start clamd" + exit 1 + fi + printf "\r%s" "Socket for clamd not found yet, retrying (${_timeout}/${CLAMD_STARTUP_TIMEOUT}) ..." + sleep 1 + _timeout="$((_timeout + 1))" + done + echo "socket found, clamd started." + fi + + if [ "${CLAMAV_NO_MILTERD:-true}" != "true" ]; then + echo "Starting clamav milterd" + clamav-milter & + fi + + # Wait forever (or until canceled) + exec tail -f "/dev/null" +fi + +exit 0 diff --git a/clamav/1.4/alpine/scripts/docker-entrypoint.sh b/clamav/1.4/alpine/scripts/docker-entrypoint.sh new file mode 100755 index 0000000..9de8bf1 --- /dev/null +++ b/clamav/1.4/alpine/scripts/docker-entrypoint.sh @@ -0,0 +1,86 @@ +#!/sbin/tini /bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# +# A beginning user should be able to docker run image bash (or sh) without +# needing to learn about --entrypoint +# https://github.com/docker-library/official-images#consistency + +set -eu + +if [ ! -d "/run/clamav" ]; then + install -d -g "clamav" -m 775 -o "clamav" "/run/clamav" +fi + +# Assign ownership to the database directory, just in case it is a mounted volume +chown -R clamav:clamav /var/lib/clamav + +# run command if it is not starting with a "-" and is an executable in PATH +if [ "${#}" -gt 0 ] && \ + [ "${1#-}" = "${1}" ] && \ + command -v "${1}" > "/dev/null" 2>&1; then + # Ensure healthcheck always passes + CLAMAV_NO_CLAMD="true" exec "${@}" +else + if [ "${#}" -ge 1 ] && \ + [ "${1#-}" != "${1}" ]; then + # If an argument starts with "-" pass it to clamd specifically + exec clamd "${@}" + fi + # else default to running clamav's servers + + # Help tiny-init a little + mkdir -p "/run/lock" + ln -f -s "/run/lock" "/var/lock" + + # Ensure we have some virus data, otherwise clamd refuses to start + if [ ! -f "/var/lib/clamav/main.cvd" ]; then + echo "Updating initial database" + freshclam --foreground --stdout + fi + + if [ "${CLAMAV_NO_FRESHCLAMD:-false}" != "true" ]; then + echo "Starting Freshclamd" + freshclam \ + --checks="${FRESHCLAM_CHECKS:-1}" \ + --daemon \ + --foreground \ + --stdout \ + --user="clamav" \ + & + fi + + if [ "${CLAMAV_NO_CLAMD:-false}" != "true" ]; then + echo "Starting ClamAV" + if [ -S "/run/clamav/clamd.sock" ]; then + unlink "/run/clamav/clamd.sock" + fi + if [ -S "/tmp/clamd.sock" ]; then + unlink "/tmp/clamd.sock" + fi + clamd --foreground & + while [ ! -S "/run/clamav/clamd.sock" ] && [ ! -S "/tmp/clamd.sock" ]; do + if [ "${_timeout:=0}" -gt "${CLAMD_STARTUP_TIMEOUT:=1800}" ]; then + echo + echo "Failed to start clamd" + exit 1 + fi + printf "\r%s" "Socket for clamd not found yet, retrying (${_timeout}/${CLAMD_STARTUP_TIMEOUT}) ..." + sleep 1 + _timeout="$((_timeout + 1))" + done + echo "socket found, clamd started." + fi + + if [ "${CLAMAV_NO_MILTERD:-true}" != "true" ]; then + echo "Starting clamav milterd" + clamav-milter & + fi + + # Wait forever (or until canceled) + exec tail -f "/dev/null" +fi + +exit 0 diff --git a/clamav/1.4/alpine/scripts/update_db_image.sh b/clamav/1.4/alpine/scripts/update_db_image.sh new file mode 100755 index 0000000..f64a27d --- /dev/null +++ b/clamav/1.4/alpine/scripts/update_db_image.sh @@ -0,0 +1,172 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +set -eu + +DEF_CLAMAV_DOCKER_NAMESPACE="clamav" +DEF_CLAMAV_DOCKER_IMAGE="clamav" +DEF_DOCKER_REGISTRY="registry.hub.docker.com" + + +usage() +{ + echo "Usage: ${0} [OPTIONS]" + echo "Update docker images with latest clamav database." + echo " -h Print this usage" + echo " -n Namespace to use to use (default: '${DEF_CLAMAV_DOCKER_NAMESPACE}') [CLAMAV_DOCKER_NAMESPACE]" + echo " -i Image to use to use (default: '${DEF_CLAMAV_DOCKER_IMAGE}') [CLAMAV_DOCKER_IMAGE]" + echo " -p Password for docker registry (file or string) [DOCKER_PASSWD]" + echo " -r Registry to use to push docker images to (default: '${DEF_DOCKER_REGISTRY}') [DOCKER_REGISTRY]" + echo " -t Tag(s) WITH _base suffix to update (default: all tags)" + echo " -u Username for docker registry [DOCKER_USER]" + echo + echo "Options that can also be passed in environment variables listed between [BRACKETS]." +} + +init() +{ + if [ -z "${clamav_docker_user:-}" ] || + [ -z "${clamav_docker_passwd:-}" ]; then + echo "No username or password set, skipping login" + return + fi + + docker --version + + if [ -f "${clamav_docker_passwd}" ]; then + _passwd="$(cat "${clamav_docker_passwd}")" + fi + echo "${_passwd:-${clamav_docker_passwd}}" | \ + docker login \ + --password-stdin \ + --username "${clamav_docker_user}" \ + "${docker_registry}" +} + +cleanup() +{ + if [ -z "${clamav_docker_user:-}" ]; then + echo "No username set, skipping logout" + return + fi + + docker logout "${docker_registry:-}" +} + +docker_tags_get() +{ + _tags="$(wget -q -O - "https://${docker_registry}/v2/namespaces/${clamav_docker_namespace}/repositories/${clamav_docker_image}/tags" | + sed -e 's|[][]||g' -e 's|"||g' -e 's| ||g' | \ + tr '}' '\n' | \ + sed -n -e 's|.*name:\(.*\)$|\1|p')" + + echo "Tags:" + echo "${_tags}" + + for _tag in ${_tags}; do + # Only get the tags that have the _base suffix + if [ "${_tag%%_base}" != "${_tag}" ]; then + clamav_docker_tags="${_tag} ${clamav_docker_tags:-}" + fi + done + + echo "Tags:" + echo "${clamav_docker_tags}" +} + +clamav_db_update() +{ + if [ -z "${clamav_docker_tags:-}" ]; then + echo "No tags to update with, cannot continue." + exit 1 + fi + + for _tag in ${clamav_docker_tags}; do + { + # Starting with the image tag with the _base suffix + echo "FROM ${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag}" + # Update the database + echo "RUN freshclam --foreground --stdout && rm /var/lib/clamav/freshclam.dat || rm /var/lib/clamav/mirrors.dat || true" + } | \ + # Pull and Build the updated image with the tag without the _base suffix. + docker image build --pull --rm --tag "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}" - + # Push the updated image with the tag without the _base suffix. + docker image push "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}" + done +} + +main() +{ + _start_time="$(date "+%s")" + + while getopts ":hi:n:p:r:t:u:" _options; do + case "${_options}" in + h) + usage + exit 0 + ;; + i) + clamav_docker_image="${OPTARG}" + ;; + n) + clamav_docker_namespace="${OPTARG}" + ;; + p) + clamav_docker_passwd="${OPTARG}" + ;; + r) + docker_registry="${OPTARG}" + ;; + t) + clamav_docker_tag="${OPTARG}" + ;; + u) + clamav_docker_user="${OPTARG}" + ;; + :) + >&2 echo "Option -${OPTARG} requires an argument." + exit 1 + ;; + ?) + >&2 echo "Invalid option: -${OPTARG}" + exit 1 + ;; + esac + done + shift "$((OPTIND - 1))" + + clamav_docker_namespace="${clamav_docker_namespace:-${CLAMAV_DOCKER_NAMESPACE:-${DEF_CLAMAV_DOCKER_NAMESPACE}}}" + clamav_docker_image="${clamav_docker_image:-${CLAMAV_DOCKER_IMAGE:-${DEF_CLAMAV_DOCKER_IMAGE}}}" + clamav_docker_passwd="${clamav_docker_passwd:-${DOCKER_PASSWD:-}}" + clamav_docker_tag="${clamav_docker_tag:-}" + clamav_docker_user="${clamav_docker_user:-${DOCKER_USER:-}}" + docker_registry="${docker_registry:-${DOCKER_REGISTRY:-${DEF_DOCKER_REGISTRY}}}" + + init + + if [ -n "${clamav_docker_tag}" ]; then + clamav_docker_tags="${clamav_docker_tag}" + else + docker_tags_get + fi + + clamav_db_update + + echo "===============================================================================" + echo "Build report for $(date -u)" + echo + echo "Updated database for image tags ..." + echo "${clamav_docker_tags:-}" + echo + echo "... successfully in $(($(date "+%s") - _start_time)) seconds" + echo "===============================================================================" + + cleanup +} + +main "${@}" + +exit 0 diff --git a/clamav/1.4/debian/Dockerfile b/clamav/1.4/debian/Dockerfile new file mode 100644 index 0000000..dc57e73 --- /dev/null +++ b/clamav/1.4/debian/Dockerfile @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2020 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +FROM index.docker.io/library/debian:11-slim AS builder + +WORKDIR /src + +COPY . /src/ + +ENV DEBIAN_FRONTEND noninteractive +ENV CARGO_HOME /src/build + +RUN apt update && apt install -y \ + cmake \ + bison \ + flex \ + gcc \ + git \ + make \ + man-db \ + net-tools \ + pkg-config \ + python3 \ + python3-pip \ + python3-pytest \ + check \ + libbz2-dev \ + libcurl4-openssl-dev \ + libjson-c-dev \ + libmilter-dev \ + libncurses-dev \ + libpcre2-dev \ + libssl-dev \ + libxml2-dev \ + zlib1g-dev \ + curl \ + && \ + rm -rf /var/cache/apt/archives \ + && \ + # Using rustup to install Rust rather than rust:1.62.1-bullseye, because there is no rust:1.62.1-bullseye image for ppc64le at this time. + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ + && \ + . $CARGO_HOME/env \ + && \ + rustup update \ + && \ + mkdir -p "./build" && cd "./build" \ + && \ + cmake .. \ + -DCARGO_HOME=$CARGO_HOME \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_INSTALL_PREFIX="/usr" \ + -DCMAKE_INSTALL_LIBDIR="/usr/lib" \ + -DAPP_CONFIG_DIRECTORY="/etc/clamav" \ + -DDATABASE_DIRECTORY="/var/lib/clamav" \ + -DENABLE_CLAMONACC=OFF \ + -DENABLE_EXAMPLES=OFF \ + -DENABLE_JSON_SHARED=ON \ + -DENABLE_MAN_PAGES=OFF \ + -DENABLE_MILTER=ON \ + -DENABLE_STATIC_LIB=OFF \ + && \ + make DESTDIR="/clamav" -j$(($(nproc) - 1)) install \ + && \ + rm -r \ + "/clamav/usr/include" \ + "/clamav/usr/lib/pkgconfig/" \ + && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/clamd.pid|" \ + -e "s|.*\(LocalSocket\) .*|\1 /tmp/clamd.sock|" \ + -e "s|.*\(TCPSocket\) .*|\1 3310|" \ + -e "s|.*\(TCPAddr\) .*|#\1 0.0.0.0|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/clamd.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + "/clamav/etc/clamav/clamd.conf.sample" > "/clamav/etc/clamav/clamd.conf" && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/freshclam.pid|" \ + -e "s|.*\(DatabaseOwner\) .*|\1 clamav|" \ + -e "s|^\#\(UpdateLogFile\) .*|\1 /var/log/clamav/freshclam.log|" \ + -e "s|^\#\(NotifyClamd\).*|\1 /etc/clamav/clamd.conf|" \ + -e "s|^\#\(ScriptedUpdates\).*|\1 yes|" \ + "/clamav/etc/clamav/freshclam.conf.sample" > "/clamav/etc/clamav/freshclam.conf" && \ + sed -e "s|^\(Example\)|\# \1|" \ + -e "s|.*\(PidFile\) .*|\1 /tmp/clamav-milter.pid|" \ + -e "s|.*\(MilterSocket\) .*|\1 inet:7357|" \ + -e "s|.*\(User\) .*|\1 clamav|" \ + -e "s|^\#\(LogFile\) .*|\1 /var/log/clamav/milter.log|" \ + -e "s|^\#\(LogTime\).*|\1 yes|" \ + -e "s|.*\(\ClamdSocket\) .*|\1 unix:/tmp/clamd.sock|" \ + "/clamav/etc/clamav/clamav-milter.conf.sample" > "/clamav/etc/clamav/clamav-milter.conf" || \ + exit 1 \ + && \ + ctest -V + +FROM index.docker.io/library/debian:11-slim + +LABEL maintainer="ClamAV bugs " + +EXPOSE 3310 +EXPOSE 7357 + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ Etc/UTC + +RUN apt-get update && apt-get install -y \ + libbz2-1.0 \ + libcurl4 \ + libssl1.1 \ + libjson-c5 \ + libmilter1.0.1 \ + libncurses6 \ + libpcre2-8-0 \ + libxml2 \ + zlib1g \ + tzdata \ + netcat \ + && \ + rm -rf /var/cache/apt/archives && \ + groupadd -g 1000 "clamav" && \ + useradd -m -g clamav -s /bin/false --home-dir /var/lib/clamav -u 1000 -c "Clam Antivirus" clamav && \ + install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \ + chown -R clamav:clamav /var/lib/clamav + +COPY --from=builder "/clamav" "/" + +COPY "./scripts/clamdcheck.sh" "/usr/local/bin/" +COPY "./scripts/docker-entrypoint.sh" "/init" +COPY "./scripts/docker-entrypoint-unprivileged.sh" "/init-unprivileged" + +HEALTHCHECK --start-period=6m CMD clamdcheck.sh + +ENTRYPOINT [ "/init" ] diff --git a/clamav/1.4/debian/Jenkinsfile b/clamav/1.4/debian/Jenkinsfile new file mode 100644 index 0000000..f1c9719 --- /dev/null +++ b/clamav/1.4/debian/Jenkinsfile @@ -0,0 +1,174 @@ +properties([ + parameters([ + string(name: 'DOCKER_REGISTRY', defaultValue: 'registry.hub.docker.com', description: 'The Docker registry to use'), + string(name: 'REGISTRY_CREDS', defaultValue: 'dockerhub', description: 'The Jenkins credentials ID for the given registry'), + string(name: 'WEBEX_SPACE_ID', defaultValue: 'b204c1a0-6862-11e8-9dbc-93ef3cfef186', description: 'ID of Webex space to push pass/fail notifications'), + string(name: 'NAMESPACE', defaultValue: 'clamav', description: 'The docker namespace to use'), + string(name: 'IMAGE_NAME', defaultValue: 'clamav-debian', description: 'The docker image name to use'), + string(name: 'REPOSITORY', defaultValue: 'https://github.com/Cisco-Talos/clamav.git', description: 'The repository from which to build'), + string(name: 'BRANCH', defaultValue: 'rel/1.4', description: 'The repository branch for this build'), + string(name: 'FULL_VERSION', defaultValue: '1.4.0', description: 'Full version in X.Y.Z format'), + string(name: 'FEATURE_VERSION', defaultValue: '1.4', description: 'Feature version in X.Y format'), + booleanParam(name: 'IS_LATEST', defaultValue: true, description: 'If "true", will also publish to :latest, and :stable tags.'), + ]), + disableConcurrentBuilds(), + buildDiscarder(logRotator( + artifactDaysToKeepStr: '10', + artifactNumToKeepStr: '10', + daysToKeepStr: '30', + numToKeepStr: '20')) +]) + +node('macos-newer') { + cleanWs() + + try { + // Checkout the ClamAV source code + checkout([ + $class: 'GitSCM', branches: [[name: "${params.BRANCH}"]], + doGenerateSubmoduleConfigurations: false, + extensions: [ + [$class: 'RelativeTargetDirectory', relativeTargetDir: '.'], + [$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true] + ], + submoduleCfg: [], userRemoteConfigs: [[url: "${params.REPOSITORY}"]] + ]) + + // Remove the Dockerfile and scripts from the clamav repo, if any. + sh """ + rm -rf ./Dockerfile ./dockerfiles + """ + + // Checkout the current repo + dir(path: 'clamav-docker') { + checkout scm + } + + // Use the Dockerfile and scripts from this repo. + sh """ + cp -r clamav-docker/clamav/${params.FEATURE_VERSION}/debian/Dockerfile clamav-docker/clamav/${params.FEATURE_VERSION}/debian/scripts . + """ + + stage('Build Image') { + withVault([vaultSecrets: [[ path: "clamavbuild-kv/clamav_jenkins_svc_mac_minis", engineVersion: 1, secretValues: + [[envVar: 'USER', vaultKey: 'username'],[envVar: 'PASSWORD', vaultKey: 'password']]]]]) { + // Enable keychain to access keystore, else docker will store credentials in plaintext + sh ''' + security -v unlock-keychain -p "$PASSWORD" ~/Library/Keychains/login.keychain-db + security set-keychain-settings -t 36000 -l ~/Library/Keychains/login.keychain-db + ''' + } + withVault([vaultSecrets: [[ path: "clamavbuild-kv/${params.REGISTRY_CREDS}", engineVersion: 1, secretValues: + [[envVar: 'DOCKER_USER', vaultKey: 'username'],[envVar: 'DOCKER_PASSWD', vaultKey: 'password']]]]]) { + // Set docker buildx context + sh """ + docker buildx use "clamav-test-mul-arch" + """ + + // Make sure we have the latest base image. + sh """ + docker pull debian:11-slim + """ + + // Login to docker hub + sh """ + echo "\${_passwd:-\${DOCKER_PASSWD}}" | \ + docker login --password-stdin --username "${DOCKER_USER}" "${params.DOCKER_REGISTRY}" + """ + + // + // Build the following images: + // - X.Y.Z-R, X.Y.Z-R_base + // - X.Y.Z, X.Y.Z_base + // - X.Y, X.Y_base + // + // And maybe also: + // - latest, latest_base + // - stable, stable_base + // + + if (params.IS_LATEST) { + // Create & Publish 'stable_base' and 'latest_base' tags. + sh """ + docker buildx build --no-cache --platform linux/amd64,linux/arm64,linux/ppc64le \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION}_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest_base \ + --push . + """ + } else { + sh """ + docker buildx build --no-cache --platform linux/amd64,linux/arm64,linux/ppc64le \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER}_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}_base \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION}_base \ + --push . + """ + } + + // The update_db_image.sh script will query for tags during the update process. + // So give the registry a little time to add the X.Y.Z-R_base image. + sh """ + sleep 20 + """ + + // Pull the X.Y.Z-R_base image, update the DB, and push it out as X.Y.Z-R (without the _base suffix) + sh """ + DOCKER_REGISTRY="${params.DOCKER_REGISTRY}" \ + CLAMAV_DOCKER_IMAGE="${params.IMAGE_NAME}" \ + ./scripts/update_db_image.sh -t ${params.FULL_VERSION}-${BUILD_NUMBER}_base -n ${params.NAMESPACE} + """ + + // Login to docker hub again, because the update_db_image.sh script removed our creds in its cleanup stage + sh """ + echo "\${_passwd:-\${DOCKER_PASSWD}}" | \ + docker login --password-stdin --username "${DOCKER_USER}" "${params.DOCKER_REGISTRY}" + """ + + // Publish X.Y.Z tag (without the _base suffix) + + if (params.IS_LATEST) { + // Create & Publish 'stable' and 'latest' tags. + sh """ + docker buildx imagetools create ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION} \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION} \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:stable \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:latest + """ + } else { + sh """ + docker buildx imagetools create ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION}-${BUILD_NUMBER} \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FULL_VERSION} \ + --tag ${params.DOCKER_REGISTRY}/${params.NAMESPACE}/${params.IMAGE_NAME}:${params.FEATURE_VERSION} \ + + """ + + } + + // log-out (again) + sh """ + docker logout "${params.DOCKER_REGISTRY}" + """ + } + } + + } catch(err) { + // log-out, if required + sh """ + docker logout "${params.DOCKER_REGISTRY}" || true + """ + + currentBuild.result = "FAILED" + sparkSend( + message: "Docker build of ${params.FULL_VERSION} from ${params.REPOSITORY} branch ${params.BRANCH} for ${params.NAMESPACE}/${params.IMAGE_NAME} [FAILED](${BUILD_URL})", + spaceList: [[spaceName: "ClamAV Jenkins", spaceId: "${params.WEBEX_SPACE_ID}"]], credentialsId: 'clambuilder', messageType: 'markdown') + throw err + } + + sparkSend( + message: "Docker build of ${params.FULL_VERSION} from ${params.REPOSITORY} branch ${params.BRANCH} for ${params.NAMESPACE}/${params.IMAGE_NAME} [PASSED](${BUILD_URL})", + spaceList: [[spaceName: "ClamAV Jenkins", spaceId: "${params.WEBEX_SPACE_ID}"]], credentialsId: 'clambuilder', messageType: 'markdown') +} diff --git a/clamav/1.4/debian/scripts/clamdcheck.sh b/clamav/1.4/debian/scripts/clamdcheck.sh new file mode 100755 index 0000000..e7e53a6 --- /dev/null +++ b/clamav/1.4/debian/scripts/clamdcheck.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -eu + +if [ "${CLAMAV_NO_CLAMD:-}" != "false" ]; then + if [ "$(echo "PING" | nc localhost 3310)" != "PONG" ]; then + echo "ERROR: Unable to contact server" + exit 1 + fi + + echo "Clamd is up" +fi + +exit 0 diff --git a/clamav/1.4/debian/scripts/docker-entrypoint-unprivileged.sh b/clamav/1.4/debian/scripts/docker-entrypoint-unprivileged.sh new file mode 100755 index 0000000..8a55e44 --- /dev/null +++ b/clamav/1.4/debian/scripts/docker-entrypoint-unprivileged.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# +# A beginning user should be able to docker run image bash (or sh) without +# needing to learn about --entrypoint +# https://github.com/docker-library/official-images#consistency + +set -eu + +# run command if it is not starting with a "-" and is an executable in PATH +if [ "${#}" -gt 0 ] && \ + [ "${1#-}" = "${1}" ] && \ + command -v "${1}" > "/dev/null" 2>&1; then + # Ensure healthcheck always passes + CLAMAV_NO_CLAMD="true" exec "${@}" +else + if [ "${#}" -ge 1 ] && \ + [ "${1#-}" != "${1}" ]; then + # If an argument starts with "-" pass it to clamd specifically + exec clamd "${@}" + fi + # else default to running clamav's servers + + # Ensure we have some virus data, otherwise clamd refuses to start + if [ ! -f "/var/lib/clamav/main.cvd" ]; then + echo "Updating initial database" + freshclam --foreground --stdout + fi + + if [ "${CLAMAV_NO_FRESHCLAMD:-false}" != "true" ]; then + echo "Starting Freshclamd" + freshclam \ + --checks="${FRESHCLAM_CHECKS:-1}" \ + --daemon \ + --foreground \ + --stdout \ + --user="clamav" \ + & + fi + + if [ "${CLAMAV_NO_CLAMD:-false}" != "true" ]; then + echo "Starting ClamAV" + if [ -S "/tmp/clamd.sock" ]; then + unlink "/tmp/clamd.sock" + fi + clamd --foreground & + while [ ! -S "/tmp/clamd.sock" ]; do + if [ "${_timeout:=0}" -gt "${CLAMD_STARTUP_TIMEOUT:=1800}" ]; then + echo + echo "Failed to start clamd" + exit 1 + fi + printf "\r%s" "Socket for clamd not found yet, retrying (${_timeout}/${CLAMD_STARTUP_TIMEOUT}) ..." + sleep 1 + _timeout="$((_timeout + 1))" + done + echo "socket found, clamd started." + fi + + if [ "${CLAMAV_NO_MILTERD:-true}" != "true" ]; then + echo "Starting clamav milterd" + clamav-milter & + fi + + # Wait forever (or until canceled) + exec tail -f "/dev/null" +fi + +exit 0 diff --git a/clamav/1.4/debian/scripts/docker-entrypoint.sh b/clamav/1.4/debian/scripts/docker-entrypoint.sh new file mode 100755 index 0000000..97c26d3 --- /dev/null +++ b/clamav/1.4/debian/scripts/docker-entrypoint.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. +# +# A beginning user should be able to docker run image bash (or sh) without +# needing to learn about --entrypoint +# https://github.com/docker-library/official-images#consistency + +set -eu + +if [ ! -d "/run/clamav" ]; then + install -d -g "clamav" -m 775 -o "clamav" "/run/clamav" +fi + +# Assign ownership to the database directory, just in case it is a mounted volume +chown -R clamav:clamav /var/lib/clamav + +# run command if it is not starting with a "-" and is an executable in PATH +if [ "${#}" -gt 0 ] && \ + [ "${1#-}" = "${1}" ] && \ + command -v "${1}" > "/dev/null" 2>&1; then + # Ensure healthcheck always passes + CLAMAV_NO_CLAMD="true" exec "${@}" +else + if [ "${#}" -ge 1 ] && \ + [ "${1#-}" != "${1}" ]; then + # If an argument starts with "-" pass it to clamd specifically + exec clamd "${@}" + fi + # else default to running clamav's servers + + # Help tiny-init a little + mkdir -p "/run/lock" + ln -f -s "/run/lock" "/var/lock" + + # Ensure we have some virus data, otherwise clamd refuses to start + if [ ! -f "/var/lib/clamav/main.cvd" ]; then + echo "Updating initial database" + freshclam --foreground --stdout + fi + + if [ "${CLAMAV_NO_FRESHCLAMD:-false}" != "true" ]; then + echo "Starting Freshclamd" + freshclam \ + --checks="${FRESHCLAM_CHECKS:-1}" \ + --daemon \ + --foreground \ + --stdout \ + --user="clamav" \ + & + fi + + if [ "${CLAMAV_NO_CLAMD:-false}" != "true" ]; then + echo "Starting ClamAV" + if [ -S "/run/clamav/clamd.sock" ]; then + unlink "/run/clamav/clamd.sock" + fi + if [ -S "/tmp/clamd.sock" ]; then + unlink "/tmp/clamd.sock" + fi + clamd --foreground & + while [ ! -S "/run/clamav/clamd.sock" ] && [ ! -S "/tmp/clamd.sock" ]; do + if [ "${_timeout:=0}" -gt "${CLAMD_STARTUP_TIMEOUT:=1800}" ]; then + echo + echo "Failed to start clamd" + exit 1 + fi + printf "\r%s" "Socket for clamd not found yet, retrying (${_timeout}/${CLAMD_STARTUP_TIMEOUT}) ..." + sleep 1 + _timeout="$((_timeout + 1))" + done + echo "socket found, clamd started." + fi + + if [ "${CLAMAV_NO_MILTERD:-true}" != "true" ]; then + echo "Starting clamav milterd" + clamav-milter & + fi + + # Wait forever (or until canceled) + exec tail -f "/dev/null" +fi + +exit 0 diff --git a/clamav/1.4/debian/scripts/update_db_image.sh b/clamav/1.4/debian/scripts/update_db_image.sh new file mode 100755 index 0000000..d03029d --- /dev/null +++ b/clamav/1.4/debian/scripts/update_db_image.sh @@ -0,0 +1,193 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright (C) 2021 Olliver Schinagl +# Copyright (C) 2021-2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + +set -eu + +DEF_CLAMAV_DOCKER_NAMESPACE="clamav" +DEF_CLAMAV_DOCKER_IMAGE="clamav" +DEF_DOCKER_REGISTRY="registry.hub.docker.com" +DOCKER_BUILDKIT_IMAGE="multiarch/qemu-user-static" + +usage() { + echo "Usage: ${0} [OPTIONS]" + echo "Update docker images with latest clamav database." + echo " -h Print this usage" + echo " -n Namespace to use to use (default: '${DEF_CLAMAV_DOCKER_NAMESPACE}') [CLAMAV_DOCKER_NAMESPACE]" + echo " -i Image to use to use (default: '${DEF_CLAMAV_DOCKER_IMAGE}') [CLAMAV_DOCKER_IMAGE]" + echo " -p Password for docker registry (file or string) [DOCKER_PASSWD]" + echo " -r Registry to use to push docker images to (default: '${DEF_DOCKER_REGISTRY}') [DOCKER_REGISTRY]" + echo " -t Tag(s) WITH _base suffix to update (default: all tags)" + echo " -u Username for docker registry [DOCKER_USER]" + echo + echo "Options that can also be passed in environment variables listed between [BRACKETS]." +} + +init() { + if [ -z "${clamav_docker_user:-}" ] || + [ -z "${clamav_docker_passwd:-}" ]; then + echo "No username or password set, skipping login" + return + fi + + docker --version + + if [ -f "${clamav_docker_passwd}" ]; then + _passwd="$(cat "${clamav_docker_passwd}")" + fi + echo "${_passwd:-${clamav_docker_passwd}}" | + docker login \ + --password-stdin \ + --username "${clamav_docker_user}" \ + "${docker_registry}" +} + +cleanup() { + if [ -z "${clamav_docker_user:-}" ]; then + echo "No username set, skipping logout" + return + fi + + docker logout "${docker_registry:-}" +} + +docker_tags_get() { + _tags="$(wget -q -O - "https://${docker_registry}/v2/namespaces/${clamav_docker_namespace}/repositories/${clamav_docker_image}/tags" | + sed -e 's|[][]||g' -e 's|"||g' -e 's| ||g' | + tr '}' '\n' | + sed -n -e 's|.*name:\(.*\)$|\1|p')" + + echo "Tags:" + echo "${_tags}" + + for _tag in ${_tags}; do + # Only get the tags that have the _base suffix + if [ "${_tag%%_base}" != "${_tag}" ]; then + clamav_docker_tags="${_tag} ${clamav_docker_tags:-}" + fi + done + + echo "Tags:" + echo "${clamav_docker_tags}" +} + +config_docker_buildx() { + docker buildx use "clamav-test-mul-arch" +} + +clamav_db_update() { + if [ -z "${clamav_docker_tags:-}" ]; then + echo "No tags to update with, cannot continue." + exit 1 + fi + + for _tag in ${clamav_docker_tags}; do + { + # Starting with the image tag with the _base suffix + echo "FROM ${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag}" + # Update the database + echo "RUN freshclam --foreground --stdout && rm /var/lib/clamav/freshclam.dat || rm /var/lib/clamav/mirrors.dat || true" + } | \ + docker buildx build --platform linux/amd64 --pull --rm --push \ + --tag "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-amd64" - + + { + # Starting with the image tag with the _base suffix + echo "FROM ${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag}" + # Update the database + echo "RUN freshclam --foreground --stdout && rm /var/lib/clamav/freshclam.dat || rm /var/lib/clamav/mirrors.dat || true" + } | \ + docker buildx build --platform linux/arm64 --pull --rm --push \ + --tag "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-arm64" - + + { + # Starting with the image tag with the _base suffix + echo "FROM ${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag}" + # Update the database + echo "RUN freshclam --foreground --stdout && rm /var/lib/clamav/freshclam.dat || rm /var/lib/clamav/mirrors.dat || true" + } | \ + docker buildx build --platform linux/ppc64le --pull --rm --push \ + --tag "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-ppc64le" - + + done + + docker buildx imagetools create -t "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}" \ + "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-amd64" \ + "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-arm64" \ + "${docker_registry}/${clamav_docker_namespace}/${clamav_docker_image}:${_tag%%_base}-ppc64le" +} + +main() { + _start_time="$(date "+%s")" + + while getopts ":hi:n:p:r:t:u:" _options; do + case "${_options}" in + h) + usage + exit 0 + ;; + i) + clamav_docker_image="${OPTARG}" + ;; + n) + clamav_docker_namespace="${OPTARG}" + ;; + p) + clamav_docker_passwd="${OPTARG}" + ;; + r) + docker_registry="${OPTARG}" + ;; + t) + clamav_docker_tag="${OPTARG}" + ;; + u) + clamav_docker_user="${OPTARG}" + ;; + :) + echo >&2 "Option -${OPTARG} requires an argument." + exit 1 + ;; + ?) + echo >&2 "Invalid option: -${OPTARG}" + exit 1 + ;; + esac + done + shift "$((OPTIND - 1))" + + clamav_docker_namespace="${clamav_docker_namespace:-${CLAMAV_DOCKER_NAMESPACE:-${DEF_CLAMAV_DOCKER_NAMESPACE}}}" + clamav_docker_image="${clamav_docker_image:-${CLAMAV_DOCKER_IMAGE:-${DEF_CLAMAV_DOCKER_IMAGE}}}" + clamav_docker_passwd="${clamav_docker_passwd:-${DOCKER_PASSWD:-}}" + clamav_docker_tag="${clamav_docker_tag:-}" + clamav_docker_user="${clamav_docker_user:-${DOCKER_USER:-}}" + docker_registry="${docker_registry:-${DOCKER_REGISTRY:-${DEF_DOCKER_REGISTRY}}}" + + init + config_docker_buildx + + if [ -n "${clamav_docker_tag}" ]; then + clamav_docker_tags="${clamav_docker_tag}" + else + docker_tags_get + fi + + clamav_db_update + + echo "===============================================================================" + echo "Build report for $(date -u)" + echo + echo "Updated database for image tags ..." + echo "${clamav_docker_tags:-}" + echo + echo "... successfully in $(($(date "+%s") - _start_time)) seconds" + echo "===============================================================================" + + cleanup +} + +main "${@}" + +exit 0