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:
- 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.
- 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:
- Patch the hardcoded
--fdpass in internals/functions — overwritten on every maldet -d / version auto-update (autoupdate_version).
- 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
Summary
When
scan_clamd_remote=1, LMD invokesclamdscanwith 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 —--fdpasscannot work. That contradicts the documented purpose ofscan_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 inlogs/clamscan_log; built ininternals/functions):--fdpassis fixed; there is no option to use--stream.Why
--fdpassbreaks for a remote / containerized clamd--fdpasspasses an open file descriptor to clamd over the local Unix socket (SCM_RIGHTS). Two requirements a remote/isolated clamd cannot meet:AF_UNIXsocket. Ifremote_clamd_configpoints at a TCP clamd (TCPSocket/TCPAddr),clamdscan --fdpassreturns2(an fd cannot be passed over TCP). TCP is the only practical transport to clamd on another host.File path check failure: Permission denied, because the scanned file does not exist inside clamd's mount namespace.So today
scan_clamd_remoteonly 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
--streamis the correct mode hereclamdscan --streamuses clamd'sINSTREAM: 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-milteruses 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.:A reasonable default would be
--streamwheneverscan_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 theclamdscanflags are built ininternals/functions.Current workaround (fragile)
Two options today, both unpleasant:
--fdpassininternals/functions— overwritten on everymaldet -d/ version auto-update (autoupdate_version).clamdscanbinary with a wrapper. LMD auto-detectsclamdscanand checks/usr/local/sbinbefore/bin, so a wrapper there is picked up without touching the real binary, and it survives both LMD and ClamAV package updates:This proves the fix is trivial — a first-class
remote_clamd_modeoption would remove the need for the shim entirely.Environment
clamav/clamav:1.5-debian13-slimDocker container), reached over TCP127.0.0.1:<port>clamdscanclient (the one invoked with--fdpass): centos stockclamavpackagescan_clamscan=1,scan_clamd_remote=1,remote_clamd_configpointing at the container clamd