1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-20 05:11:30 +00:00

[Clamd] use python bootstrapper to start CLAMD container

This commit is contained in:
FreddleSpl0it
2025-05-21 14:02:49 +02:00
parent 5a39ae45cb
commit 669f75182d
9 changed files with 122 additions and 60 deletions

View File

@@ -21,6 +21,8 @@ def main():
from modules.BootstrapDovecot import Bootstrap from modules.BootstrapDovecot import Bootstrap
elif container_name == "rspamd-mailcow": elif container_name == "rspamd-mailcow":
from modules.BootstrapRspamd import Bootstrap from modules.BootstrapRspamd import Bootstrap
elif container_name == "clamd-mailcow":
from modules.BootstrapClamd import Bootstrap
else: else:
print(f"No bootstrap handler for container: {container_name}", file=sys.stderr) print(f"No bootstrap handler for container: {container_name}", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@@ -30,16 +30,14 @@ class BootstrapBase:
self.mysql_conn = None self.mysql_conn = None
self.redis_conn = None self.redis_conn = None
def render_config(self, template_name, output_path): def render_config(self, template_name, output_path, clean_blank_lines=False):
""" """
Renders a Jinja2 template and writes it to the specified output path. Renders a Jinja2 template and writes it to the specified output path.
The method uses the class's `self.env` Jinja2 environment and `self.env_vars`
for rendering template variables.
Args: Args:
template_name (str): Name of the template file. template_name (str): Name of the template file.
output_path (str or Path): Path to write the rendered output file. output_path (str or Path): Path to write the rendered output file.
clean_blank_lines (bool): If True, removes empty/whitespace-only lines from rendered output.
""" """
output_path = Path(output_path) output_path = Path(output_path)
@@ -48,6 +46,12 @@ class BootstrapBase:
template = self.env.get_template(template_name) template = self.env.get_template(template_name)
rendered = template.render(self.env_vars) rendered = template.render(self.env_vars)
if clean_blank_lines:
rendered = "\n".join(line for line in rendered.splitlines() if line.strip())
# converts output to Unix-style line endings
rendered = rendered.replace('\r\n', '\n').replace('\r', '\n')
with open(output_path, "w") as f: with open(output_path, "w") as f:
f.write(rendered) f.write(rendered)

View File

@@ -0,0 +1,58 @@
from jinja2 import Environment, FileSystemLoader
from modules.BootstrapBase import BootstrapBase
from pathlib import Path
import os
import sys
import time
import platform
class Bootstrap(BootstrapBase):
def bootstrap(self):
# Skip Clamd if set
if self.isYes(os.getenv("SKIP_CLAMD", "")):
print("SKIP_CLAMD is set, skipping ClamAV startup...")
time.sleep(365 * 24 * 60 * 60)
sys.exit(1)
# Connect to MySQL
self.connect_mysql()
print("Cleaning up tmp files...")
tmp_files = Path("/var/lib/clamav").glob("clamav-*.tmp")
for tmp_file in tmp_files:
try:
self.remove(tmp_file)
print(f"Removed: {tmp_file}")
except Exception as e:
print(f"Failed to remove {tmp_file}: {e}")
self.create_dir("/run/clamav")
self.create_dir("/var/lib/clamav")
# Setup Jinja2 Environment and load vars
self.env = Environment(
loader=FileSystemLoader('./etc/clamav/config_templates'),
keep_trailing_newline=True,
lstrip_blocks=True,
trim_blocks=True
)
extra_vars = {
}
self.env_vars = self.prepare_template_vars('/overwrites.json', extra_vars)
print("Set Timezone")
self.set_timezone()
print("Render config")
self.render_config("whitelist.ign2.j2", "/var/lib/clamav/whitelist.ign2", clean_blank_lines=True)
# Fix permissions
self.set_owner("/var/lib/clamav", "clamav", "clamav", recursive=True)
self.set_owner("/run/clamav", "clamav", "clamav", recursive=True)
self.set_permissions("/var/lib/clamav", 0o755)
for item in Path("/var/lib/clamav").glob("*"):
self.set_permissions(item, 0o644)
self.set_permissions("/run/clamav", 0o750)
# Copying to /etc/clamav to expose file as-is to administrator
self.copy_file("/var/lib/clamav/whitelist.ign2", "/etc/clamav/whitelist.ign2")

View File

@@ -88,23 +88,34 @@ RUN apk upgrade --no-cache \
pcre2 \ pcre2 \
zlib \ zlib \
libgcc \ libgcc \
py3-pip \
&& addgroup -S "clamav" && \ && addgroup -S "clamav" && \
adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -S "clamav" && \ adduser -D -G "clamav" -h "/var/lib/clamav" -s "/bin/false" -S "clamav" && \
install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \ install -d -m 755 -g "clamav" -o "clamav" "/var/log/clamav" && \
chown -R clamav:clamav /var/lib/clamav chown -R clamav:clamav /var/lib/clamav
RUN pip install --break-system-packages \
mysql-connector-python \
jinja2 \
redis \
dnspython
COPY --from=builder "/clamav" "/" COPY --from=builder "/clamav" "/"
# init
COPY clamd.sh /clamd.sh
RUN chmod +x /sbin/tini
# healthcheck COPY data/Dockerfiles/bootstrap /bootstrap
COPY healthcheck.sh /healthcheck.sh COPY data/Dockerfiles/clamd/docker-entrypoint.sh /docker-entrypoint.sh
COPY clamdcheck.sh /usr/local/bin COPY data/Dockerfiles/clamd/clamd.sh /clamd.sh
RUN chmod +x /healthcheck.sh COPY data/Dockerfiles/clamd/healthcheck.sh /healthcheck.sh
RUN chmod +x /usr/local/bin/clamdcheck.sh COPY data/Dockerfiles/clamd/clamdcheck.sh /usr/local/bin
HEALTHCHECK --start-period=6m CMD "/healthcheck.sh" HEALTHCHECK --start-period=6m CMD "/healthcheck.sh"
ENTRYPOINT [] RUN chmod +x /docker-entrypoint.sh \
/clamd.sh \
/healthcheck.sh \
/usr/local/bin/clamdcheck.sh \
/sbin/tini
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/sbin/tini", "-g", "--", "/clamd.sh"] CMD ["/sbin/tini", "-g", "--", "/clamd.sh"]

View File

@@ -1,48 +1,5 @@
#!/bin/bash #!/bin/bash
if [[ "${SKIP_CLAMD}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
echo "SKIP_CLAMD=y, skipping ClamAV..."
sleep 365d
exit 0
fi
# Cleaning up garbage
echo "Cleaning up tmp files..."
rm -rf /var/lib/clamav/clamav-*.tmp
# Prepare whitelist
mkdir -p /run/clamav /var/lib/clamav
if [[ -s /etc/clamav/whitelist.ign2 ]]; then
echo "Copying non-empty whitelist.ign2 to /var/lib/clamav/whitelist.ign2"
cp /etc/clamav/whitelist.ign2 /var/lib/clamav/whitelist.ign2
fi
if [[ ! -f /var/lib/clamav/whitelist.ign2 ]]; then
echo "Creating /var/lib/clamav/whitelist.ign2"
cat <<EOF > /var/lib/clamav/whitelist.ign2
# Please restart ClamAV after changing signatures
Example-Signature.Ignore-1
PUA.Win.Trojan.EmbeddedPDF-1
PUA.Pdf.Trojan.EmbeddedJavaScript-1
PUA.Pdf.Trojan.OpenActionObjectwithJavascript-1
EOF
fi
chown clamav:clamav -R /var/lib/clamav /run/clamav
chmod 755 /var/lib/clamav
chmod 644 -R /var/lib/clamav/*
chmod 750 /run/clamav
stat /var/lib/clamav/whitelist.ign2
dos2unix /var/lib/clamav/whitelist.ign2
sed -i '/^\s*$/d' /var/lib/clamav/whitelist.ign2
# Copying to /etc/clamav to expose file as-is to administrator
cp -p /var/lib/clamav/whitelist.ign2 /etc/clamav/whitelist.ign2
BACKGROUND_TASKS=() BACKGROUND_TASKS=()
echo "Running freshclam..." echo "Running freshclam..."

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# Run hooks
for file in /hooks/*; do
if [ -x "${file}" ]; then
echo "Running hook ${file}"
"${file}"
fi
done
python3 -u /bootstrap/main.py
BOOTSTRAP_EXIT_CODE=$?
if [ $BOOTSTRAP_EXIT_CODE -ne 0 ]; then
echo "Bootstrap failed with exit code $BOOTSTRAP_EXIT_CODE. Not starting Clamd."
exit $BOOTSTRAP_EXIT_CODE
fi
echo "Bootstrap succeeded. Starting Clamd..."
exec "$@"

View File

@@ -0,0 +1,5 @@
# Please restart ClamAV after changing signatures
Example-Signature.Ignore-1
PUA.Win.Trojan.EmbeddedPDF-1
PUA.Pdf.Trojan.EmbeddedJavaScript-1
PUA.Pdf.Trojan.OpenActionObjectwithJavascript-1

View File

View File

@@ -65,7 +65,7 @@ services:
- redis - redis
clamd-mailcow: clamd-mailcow:
image: ghcr.io/mailcow/clamd:1.70 image: ghcr.io/mailcow/clamd:nightly-19052025
restart: always restart: always
depends_on: depends_on:
unbound-mailcow: unbound-mailcow:
@@ -73,10 +73,15 @@ services:
dns: dns:
- ${IPV4_NETWORK:-172.22.1}.254 - ${IPV4_NETWORK:-172.22.1}.254
environment: environment:
- CONTAINER_NAME=clamd-mailcow
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- TZ=${TZ} - TZ=${TZ}
- SKIP_CLAMD=${SKIP_CLAMD:-n} - SKIP_CLAMD=${SKIP_CLAMD:-n}
volumes: volumes:
- ./data/conf/clamav/:/etc/clamav/:Z - ./data/conf/clamav/:/etc/clamav/:Z
- mysql-socket-vol-1:/var/run/mysqld/
- clamd-db-vol-1:/var/lib/clamav - clamd-db-vol-1:/var/lib/clamav
networks: networks:
mailcow-network: mailcow-network: