From 29a6f8815a50e971ac402cf3762f8e5819c0912c Mon Sep 17 00:00:00 2001 From: Niel Markwick Date: Fri, 29 Nov 2024 16:45:36 +0100 Subject: [PATCH] feat: Use ClamAV 1.4 from ClamAV managed container ClamAV version from the managed container is more up to date than the version in the Debian distribution --- cloudrun-malware-scanner/Dockerfile | 122 +++++++++++++++----------- cloudrun-malware-scanner/bootstrap.sh | 90 ++++++++++--------- 2 files changed, 120 insertions(+), 92 deletions(-) diff --git a/cloudrun-malware-scanner/Dockerfile b/cloudrun-malware-scanner/Dockerfile index 16e04e2..2529cf1 100644 --- a/cloudrun-malware-scanner/Dockerfile +++ b/cloudrun-malware-scanner/Dockerfile @@ -12,65 +12,85 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM node:22.11.0 -WORKDIR /app -COPY . /app -# Install apt, pip3 and npm requirements. -# GCloud SDK install taken from -# https://github.com/GoogleCloudPlatform/cloud-sdk-docker/blob/master/debian_slim/Dockerfile -# -# Note that section this both adds and removes clamav. -# This is so that the clamav config files and prerequisites are installed here -# but the latest package will actually get installed by bootstrap.sh -# when the container starts. -# -# This is because -# a) clamav cannot be installed only in container startup due to unable to -# create some symlinks and config files in the container runtime. -# b) clamav cannot be installed here, then updated when the container starts -# due to some wierdness relating to symlinks in the container -# runtime being the wrong size and causing dpkg to fail. -# -# see https://github.com/GoogleCloudPlatform/docker-clamav-malware-scanner/issues/32 +# Copy Node from node's container: +FROM node:22.11.0-bookworm-slim AS node + +# Base image is the ClamAV image +FROM clamav/clamav-debian:1.4.1_base + +# renovate: datasource=python packageName=cvdupdate versioning=python +ARG CVDUPDATE_VERISION=1.1.2 + +# Update all pkgs # -ENV PATH "$PATH:/opt/google-cloud-sdk/bin/" +# Install all dpkg dependencies +# Combination of the packages required by NodeJS +# (see https://github.com/nodejs/docker-node/blob/main/22/bookworm-slim/Dockerfile) +# gcloud cli +# (https://cloud.google.com/sdk/docs/install#deb) +# The cvdupdater +# (https://pypi.org/project/cvdupdate/) +# and the malware scanner service +ENV PATH="$PATH:/opt/google-cloud-sdk/bin:/usr/local/bin:/root/.local/bin" +ENV DEBIAN_FRONTEND=noninteractive + RUN set -x \ - && echo 'Dir::Log::Planner "/dev/null";' > /etc/apt/apt.conf.d/99disablePlannerLog \ - && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -qqy update \ + && apt-get update \ + && apt-get -y upgrade \ && apt-get -qqy --no-install-recommends install \ - apt-transport-https \ - ca-certificates \ - curl \ - python3-pip \ - pipx \ - lsb-release \ - openssh-client \ - gnupg \ - jq \ - gawk \ - gettext-base \ - clamav-daemon \ - clamav-freshclam \ - python3-crcmod \ - && apt-get -qqy remove \ - libclamav9 \ - clamav-base \ - clamav-daemon \ - clamav-freshclam \ - && echo -n "Adding Cloud SDK apt repository: " \ + apt-transport-https \ + ca-certificates \ + curl \ + python3-pip \ + pipx \ + lsb-release \ + openssh-client \ + gnupg \ + jq \ + gawk \ + gettext-base \ + python3-crcmod \ + python3-dev \ + wget \ + dirmngr \ + xz-utils \ + libatomic1 \ + git \ + make \ + && pipx install "cvdupdate==${CVDUPDATE_VERISION}" + +# Install node by copying from container. +COPY --from=node /usr/local /usr/local + +# Install cloud SDK +RUN set -x \ && echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \ - | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list \ - && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg \ - | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg \ + > /etc/apt/sources.list.d/google-cloud-sdk.list \ + && curl --silent https://packages.cloud.google.com/apt/doc/apt-key.gpg \ + | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg \ && apt-get -qqy update \ - && apt-get -qqy --no-install-recommends install google-cloud-sdk \ + && apt-get -qqy --no-install-recommends install google-cloud-cli \ && gcloud config set core/disable_usage_reporting true \ && gcloud config set component_manager/disable_update_check true \ - && gcloud config set metrics/environment github_docker_image \ + && gcloud config set metrics/environment docker_image_latest + +# Sanity check that required binaries are installed by checking versions +# +RUN set -x \ + && node --version \ + && npm --version \ + && clamd --version \ + && freshclam --version \ && gcloud --version \ - && find /var/log -type f -delete \ - && npm install --omit=dev + && gsutil --version \ + && cvdupdate --help + +# Copy the source code +WORKDIR /app +COPY . /app + +# Install NPM modules +RUN npm install --omit=dev CMD ["bash", "bootstrap.sh"] diff --git a/cloudrun-malware-scanner/bootstrap.sh b/cloudrun-malware-scanner/bootstrap.sh index dc59036..47b4737 100755 --- a/cloudrun-malware-scanner/bootstrap.sh +++ b/cloudrun-malware-scanner/bootstrap.sh @@ -15,7 +15,6 @@ # limitations under the License. CMD="$(basename $0)" -export PROJECT_ID="$(curl -s 'http://metadata.google.internal/computeMetadata/v1/project/project-id' -H 'Metadata-Flavor: Google')" function Log { case "$1" in @@ -38,19 +37,7 @@ function trapError { trap trapError ERR -# Install up-to-date clam and cvdupdate versions on container startup -Log INFO main "Installing latest clamAV and CVDUpdate" - -export DEBIAN_FRONTEND=noninteractive -apt-get -qqy update -apt-get -qqy install --no-install-recommends clamav-daemon clamav-freshclam -export PATH="$PATH:$HOME/.local/bin" # add pipx locations to path. -pipx install cvdupdate - -# Ensure clamav services are shut down, as we do not have the config files set up yet. -service clamav-daemon stop & -service clamav-freshclam stop & - +# Setup config CONFIG_FILE=./config.json # Check for config from environment @@ -84,24 +71,9 @@ fi # CVD mirror bucket as if it was an unauthenticated HTTP server # export PROXY_PORT=${PROXY_PORT:-8888} -PROXY_SERVER_ADDRESS=127.0.0.1:${PROXY_PORT} +PROXY_SERVER_ADDRESS=localhost:${PROXY_PORT} npm run start-proxy "${CONFIG_FILE}" & -# wait for it to startup before freshclam connects to it... -PROXY_CHECK_ATTEMPTS=${PROXY_CHECK_ATTEMPTS:-12} -PROXY_CHECK_INTERVAL=${PROXY_CHECK_INTERVAL:-5} -attempts=0 -while [[ $attempts -lt "${PROXY_CHECK_ATTEMPTS}" ]]; do - attempts=$((attempts + 1)) - Log INFO main "Waiting for proxy server to start...${attempts}" - sleep ${PROXY_CHECK_INTERVAL} - # Query a known file to verify if the proxy is up and running. - curl -s -I "http://${PROXY_SERVER_ADDRESS}/${CVD_MIRROR_BUCKET}/cvds/state.json" > /dev/null 2>&1 && break -done -if [[ $attempts -eq ${PROXY_CHECK_ATTEMPTS} ]] ; then - Log ERROR main "Proxy server did not start after $((PROXY_CHECK_ATTEMPTS * PROXY_CHECK_INTERVAL)) secs" - exit 1; -fi # This function is used to update clam and freshclam config files. # Use by specifying the config file on the command line and @@ -168,24 +140,60 @@ DatabaseMirror http://${PROXY_SERVER_ADDRESS}/${CVD_MIRROR_BUCKET}/cvds # Number of database checks per day. # Once per half hour, which is fine as we are using a local mirror. +# overridden by command line --checks=$FRESHCLAM_CHECKS Checks 48 EOF -# Get latest definitions from GCS CVD mirror bucket. -Log INFO main "Running freshclam for first run" -freshclam -# Sanity check that local cvds exist (otherwise clamav daemon will not start) -if [[ ! -e /var/lib/clamav/main.cvd ]] ; then - Log ERROR main "ClamAV database files do not exist" >&2 - exit 1 -fi + +function waitForAlive() { + if [[ "$#" -lt 4 ]] ; then + echo "waitForAlive name interval attempts alive-check-command" + return 1 + fi + + local -r CHECK_NAME="$1" + local -i -r CHECK_INTERVAL="$2" + local -i -r CHECK_ATTEMPTS="$3" + shift ; shift ; shift + local -i attempts=0 + + while [[ $attempts -lt "${CHECK_ATTEMPTS}" ]]; do + attempts=$((attempts + 1)) + Log INFO main "Waiting for $CHECK_NAME to start...${attempts}" + sleep ${CHECK_INTERVAL} + + # run alive check... + "${@}" >& /dev/null && break + done + if [[ $attempts -eq ${CHECK_ATTEMPTS} ]] ; then + Log ERROR main "$CHECK_NAME did not start after $((CHECK_ATTEMPTS * CHECK_INTERVAL)) secs" + exit 1; + fi +} + +# Allow 1 min for proxy server to start up. +waitForAlive "Proxy server" "${PROXY_CHECK_INTERVAL:-5}" "${PROXY_CHECK_ATTEMPTS:-12}" \ + curl -s -I "http://${PROXY_SERVER_ADDRESS}/${CVD_MIRROR_BUCKET}/cvds/state.json" # Restart clamav and freshclam Services Log INFO main "Starting clamav services" -service clamav-daemon force-reload & -service clamav-freshclam force-reload & +# Ensure correct services are started: +# https://github.com/Cisco-Talos/clamav-docker/blob/main/clamav/README-debian.md#controlling-the-container +# Note -- double-negative variables. +CLAMAV_NO_CLAMD=false +CLAMAV_NO_FRESHCLAMD=false +CLAMAV_NO_MILTERD=true +FRESHCLAM_CHECKS=48 +export CLAMAV_NO_CLAMD CLAMAV_NO_FRESHCLAMD CLAMAV_NO_MILTERD FRESHCLAM_CHECKS +bash -x /init & + +sleep 30 + +# Allow another 2 min for clamd service to start up. +waitForAlive "Clamd service" "${CLAMD_CHECK_INTERVAL:-10}" "${CLAMD_CHECK_ATTEMPTS:-12}" \ + clamdcheck.sh # Run node server process Log INFO main "Starting malware-scanner service" -npm run start "${CONFIG_FILE}" +exec npm run start "${CONFIG_FILE}"