Skip to content

Feature request: allow clamdscan --stream instead of hardcoded --fdpass for scan_clamd_remote #494

Description

@level420

Summary

When scan_clamd_remote=1, LMD invokes clamdscan with a hardcoded --fdpass. This only works when clamd shares the local filesystem and a Unix-domain socket with LMD. For a genuinely remote clamd — a separate host, or (very common today) a clamd running in a Docker container isolated from the host filesystem — --fdpass cannot work. That contradicts the documented purpose of scan_clamd_remote ("connecting to a remote clamd service … files being scanned are effectively piped to remote daemon"). Please make the clamd hand-off mode selectable so users can choose --stream (INSTREAM).

Current behavior

With scan_clamscan=1 + scan_clamd_remote=1, LMD assembles (seen in logs/clamscan_log; built in internals/functions):

clamdscan --fdpass -c <remote_clamd_config> --infected --no-summary -f <filelist>

--fdpass is fixed; there is no option to use --stream.

Why --fdpass breaks for a remote / containerized clamd

--fdpass passes an open file descriptor to clamd over the local Unix socket (SCM_RIGHTS). Two requirements a remote/isolated clamd cannot meet:

  1. Transport — fd-passing only works over an AF_UNIX socket. If remote_clamd_config points at a TCP clamd (TCPSocket/TCPAddr), clamdscan --fdpass returns 2 (an fd cannot be passed over TCP). TCP is the only practical transport to clamd on another host.
  2. Path visibility — even over a shared Unix socket (e.g. a container's clamd socket bind-mounted to the host), clamd still stats the original file path and fails with File path check failure: Permission denied, because the scanned file does not exist inside clamd's mount namespace.

So today scan_clamd_remote only works if clamd can also open the file directly by its host path — which defeats the "remote daemon" premise and forces users to bind-mount the scanned directories into the clamd container.

Why --stream is the correct mode here

clamdscan --stream uses clamd's INSTREAM: the client reads the file and streams its content; clamd never needs the path or an fd. This is exactly how other clamd clients reach a remote/containerized clamd (e.g. clamav-milter uses INSTREAM). It works over TCP and to an isolated clamd, with no shared filesystem and no bind-mounts. The conf wording ("piped to remote daemon") already describes streaming — only the implementation doesn't do it.

Proposed change

A selectable mode in conf.maldet, e.g.:

# How scan_clamd_remote hands files to clamd:
#   fdpass = pass a file descriptor (local clamd, shared filesystem)   [back-compat default]
#   stream = INSTREAM the content    (remote / containerized clamd over TCP or isolated socket)
remote_clamd_mode="stream"

A reasonable default would be --stream whenever scan_clamd_remote=1 (a remote daemon inherently cannot fdpass), but even a simple toggle that preserves today's default fixes it. The change is essentially a one-line conditional where the clamdscan flags are built in internals/functions.

Current workaround (fragile)

Two options today, both unpleasant:

  1. Patch the hardcoded --fdpass in internals/functions — overwritten on every maldet -d / version auto-update (autoupdate_version).
  2. Shim the clamdscan binary with a wrapper. LMD auto-detects clamdscan and checks /usr/local/sbin before /bin, so a wrapper there is picked up without touching the real binary, and it survives both LMD and ClamAV package updates:
#!/bin/bash
# /usr/local/sbin/clamdscan — picked up by LMD before /bin/clamdscan.
# Rewrites LMD's hardcoded --fdpass to --stream, but only for the remote-clamd scan
# (identified by the remote_clamd_config path appearing in the args).
real=/usr/bin/clamdscan
remoteconf=/usr/local/maldetect/etc/clamd.remote.conf

use_stream=0
for a in "$@"; do [ "$a" = "$remoteconf" ] && use_stream=1; done

args=()
for a in "$@"; do
  if [ "$use_stream" = 1 ] && [ "$a" = "--fdpass" ]; then
    args+=(--stream)
  else
    args+=("$a")
  fi
done
exec "$real" "${args[@]}"

This proves the fix is trivial — a first-class remote_clamd_mode option would remove the need for the shim entirely.

Environment

  • Linux Malware Detect v1.6.6
  • clamd: ClamAV 1.5 (official clamav/clamav:1.5-debian13-slim Docker container), reached over TCP 127.0.0.1:<port>
  • local clamdscan client (the one invoked with --fdpass): centos stock clamav package
  • scan_clamscan=1, scan_clamd_remote=1, remote_clamd_config pointing at the container clamd

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions