How to virus scan file users upload using ClamAV
Jonathan Flower
Posted on September 20, 2022
Clamav is a popular open source virus scanning solution. Part of the server application is several gigs worth of virus definition files. Because of this and our micro services architecture, it made the most sense to setup a dedicated server to host ClamAV. This also allows me to share this one instance of ClamAV across several solutions needing virus scanning capabilities.
- Create a Linode with docker
- In the config, leave all the advanced docker options blank.
- Click to create the Linode
- After it is provisioned, open the console
- Login as the root user
- Run the following (check the latest ClamAV docker container versions):
docker pull clamav/clamav:0.105.1
docker volume create clam_db
docker run --interactive --tty --restart=always --name "clam_container_01" --publish 3310:3310 \
--publish 7357:7357 \
--mount source=clam_db,target=/var/lib/clamav \
--env 'CLAMAV_NO_FRESHCLAMD=true' \
clamav/clamav:0.105.1
This pulls the ClamAV docker container, creates a persistent docker volume, runs the ClamAV docker container with ports 7357 and 3310 exposed, sets the docker container to automatically restart when the Linode is restarted, and keeps the virus definitions in the persistent docker volume called clam_db.
After completing this I used a package to help scan for viruses in nodejs.
https://www.npmjs.com/package/clamscan
For example here is what worked for me:
- Run
npm install clamscan
- Create a file called virus_scan.js:
const NodeClam = require(‘clamscan’)
const ClamScan = new NodeClam().init({
removeInfected: true, // If true, removes infected files
debugMode: false, // Whether or not to log info/debug/error msgs to the console
scanRecursively: true, // If true, deep scan folders recursively
clamdscan: {
host: ‘111.111.111.11’, // IP of host to connect to TCP interface
port: 3310, // Port of host to use when connecting via TCP interface
timeout: 60000, // Timeout for scanning files
localFallback: false, // Use local preferred binary to scan if socket/tcp fails
multiscan: true, // Scan using all available cores! Yay!
active: true, // If true, this module will consider using the clamdscan binary
bypassTest: false, // Check to see if socket is available when applicable
},
preference: ‘clamdscan’, // If clamdscan is found and active, it will be used by default
})
// Get instance by resolving ClamScan promise object
ClamScan.then(async (clamscan) => {
try {
// You can re-use the `clamscan` object as many times as you want
const version = await clamscan.getVersion()
console.log(`ClamAV Version: ${version}`)
const { isInfected, file, viruses } = await clamscan.isInfected(‘./lib/virus.txt’)
if (isInfected) console.log(`${file} is infected with ${viruses}!`)
} catch (err) {
// Handle any errors raised by the code in the try block
throw err
}
}).catch(console.error)
Contents of virus.txt
includes a virus signature to help make sure everything is working:
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
When running node virus_scan.js
you should see
null is infected with Eicar-Signature!
From here you should be able to imagine how to scan files your users upload. I will not go into detail since that is going to be fairly unique to your use case.
Posted on September 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.