Part 1 — Virus Detection Service using ClamAV and Java
In most of the web or desktop applications, we work with files or streams. Nowadays virus attacks have increased significantly it’s also required to scan files before we perform any operations on them. We had the same requirement and I searched on the internet what all open-source choices are available and what other companies are using.
ClamAV® is an open-source antivirus engine for detecting trojans, viruses, malware & other malicious threats. It has enough documentation and you can also find an active community on StackOverflow. It is also compatible with most operating systems.
https://www.clamav.net/documents/clam-antivirus-user-manual
It comes with 2 processes one is the main process which we can also call as virus detection service ClamAV and another is freshclam. Freshclam is responsible for updating ClamAV local database to support the scanning of more types of files. In this blog, we will run all services inside docker and also learn how we can start multiple services inside a single docker container.
If you want to test ClamAV it’s better to use a docker image which you can find https://hub.docker.com/r/mkodockx/docker-clamav/. The current ClamAV version is “0.102.1” but when you install in different OS using package not binary you might get older version as well. As this is an open-source you have to keep an eye on changelog and accordingly update your ClamAV version.
Setting up ClamAV using Docker
By default, ClamAV runs on 3310 port. It provides a configuration in “clamd.conf” where you can tune many parameters like Port, File Size, etc.
Pulling ClamAV Docker Image
bash-3.2$ docker pull mkodockx/docker-clamav Using default tag: latest
latest: Pulling from mkodockx/docker-clamav
Digest: sha256:b8e343e005e6475e52eabb4966803d2d7e7ee1800693a8be82e85be14714252b
Status: Image is up to date for mkodockx/docker-clamav:latest
docker.io/mkodockx/docker-clamav:latest bash-3.2$ docker image ls -a | grep docker-clamav mkodockx/docker-clamav latest b0a33e6a0b6b 3 weeks ago 319MB
Running ClamAV Docker using image
bash-3.2$ docker run -d -p 3310:3310 b0a33e6a0b6b f2276b656e96656493132b507d0e6386e8aa75220bc1b06ce309cffed5d6c1cc bash-3.2$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f2276b656e96 b0a33e6a0b6b "/bootstrap.sh" 5 seconds ago Up 3 seconds 0.0.0.0:3310->3310/tcp lucid_brahmagupta
Checking if clamd process is running inside docker
bash-3.2$ docker exec -it f2276b656e96 bash root@f2276b656e96:/# ps -ef | grep clamd clamav 16 1 65 07:40 ? 00:01:22 clamd
root 31 20 0 07:42 pts/0 00:00:00 grep clamd
Clamd.conf configuration inside docker
root@f2276b656e96:/# ls -lrt /etc/clamav/clamd.conf -rw-r - r - 1 root root 2004 Nov 24 15:42 /etc/clamav/clamd.conf root@f2276b656e96:/# cat /etc/clamav/clamd.conf ReadTimeout 180
MaxThreads 12
MaxConnectionQueueLength 15
MaxScanTime 120000
MaxScanSize 100M
MaxFileSize 25M
Docker and ClamAV Details
root@f2276b656e96:/# (cat /etc/os-release;clamd - version ) | cat PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
VERSION_CODENAME=stretch
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
ClamAV 0.101.4/25667/Wed Dec 18 09:53:32 2019
We can connect to ClamAV on localhost:3310 using TCP and send file data in the input stream format. In the below code, we are sending an input stream to ClamAV. We can also create File Hash from input Stream.
public static void scanStream(InputStream inputStream) throws IOException, NoSuchAlgorithmException {
Socket socket = null;
OutputStream outStream = null;
InputStream inStream = null;
try {
socket = new Socket("localhost",3310);
outStream = new BufferedOutputStream(socket.getOutputStream());
socket.setSoTimeout(2000);
outStream.write("zINSTREAM\0".getBytes(StandardCharsets.));
outStream.flush();
byte[] buffer = new byte[2048];
try {
inStream = socket.getInputStream();
int read = inputStream.read(buffer);
while (read >= 0) {
byte[] chunkSize = ByteBuffer.allocate(4).putInt(read).array();
outStream.write(chunkSize);
outStream.write(buffer, 0, read);
if (inStream.available() > 0) {
byte[] reply = IOUtils.toByteArray(inStream);
throw new IOException("Reply from server: " + new String(reply, StandardCharsets.));
}
read = inputStream.read(buffer);
}
outStream.write(new byte[]{0,0,0,0});
outStream.flush();
System..println(new String(IOUtils.toByteArray(inStream)));
} finally { }
}finally {
try {
if(socket != null)
socket.close();
} catch (IOException e) {
System..println("Exception occurred while closing socket = {} "+ e.getMessage());
} try {
if(inStream != null)
inStream.close();
} catch(IOException e) {
System..println("Exception occurred while closing input streams = {} "+ e.getMessage());
} try {
if(outStream != null)
outStream.close();
} catch(IOException e) {
System..println("Exception occurred while closing output streams = {} "+ e.getMessage());
}
}
}
Output
For Stream containing virusFor Stream with no virus
stream: OK�
stream: Eicar-Test-Signature FOUND�
In the next part, I will integrate the same with Sample Spring Boot Application and create APIs.
You can cross-check ClamAV’s scan results with https://virustotal.com/.
Subscribe to FAUN topics and get your weekly curated email of the must-read tech stories, news, and tutorials 🗞️
Follow us on Twitter 🐦 and Facebook 👥 and Instagram 📷 and join our Facebook and Linkedin Groups 💬