Scanning files with ClamAV inside a dockerized node application

This is a setup guide for a simple upload server with ClamAV virus scanning function.

The sample application is written in Typescript but it also work on any NodeJS application.

Dockerfile

This Dockerfile install virus scanner ClamAV and supervisor also it is based on node. If your application is Java or any other languages, you can replace the FROM line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
FROM node:8
MAINTAINER Yeung Yiu Hung <hkclex@gmail.com>

# Install ClamAV and supervisord
# Debian Base to use
ENV DEBIAN_VERSION jessie

# Install ClamAV and supervisor
RUN echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION main contrib non-free" > /etc/apt/sources.list && \
echo "deb http://http.debian.net/debian/ $DEBIAN_VERSION-updates main contrib non-free" >> /etc/apt/sources.list && \
echo "deb http://security.debian.org/ $DEBIAN_VERSION/updates main contrib non-free" >> /etc/apt/sources.list && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y -qq \
clamav-daemon \
clamav-freshclam \
libclamunrar7 \
supervisor \
wget && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*


# Initial update of av databases
RUN wget -O /var/lib/clamav/main.cvd http://database.clamav.net/main.cvd && \
wget -O /var/lib/clamav/daily.cvd http://database.clamav.net/daily.cvd && \
wget -O /var/lib/clamav/bytecode.cvd http://database.clamav.net/bytecode.cvd && \
chown clamav:clamav /var/lib/clamav/*.cvd

# Update Permission
RUN mkdir /var/run/clamav && \
chown clamav:clamav /var/run/clamav && \
chmod 750 /var/run/clamav

# Configuration update
RUN sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/clamd.conf && \
echo "TCPSocket 3310" >> /etc/clamav/clamd.conf && \
sed -i 's/^Foreground .*$/Foreground true/g' /etc/clamav/freshclam.conf

# Volume provision
VOLUME ["/var/lib/clamav"]

WORKDIR /server

COPY . /server
RUN npm install && npm run postinstall

# Copy supervisor config
COPY ./configs/supervisord.conf /etc/supervisor/conf.d/supervisord-nodejs.conf

EXPOSE 3000
CMD ["/usr/bin/supervisord", "-n"]

supervisor.conf

supervisor is a service that let you run multiple service at once. In our case we need freshclam for updating virus database, clamd as our virus scanner and node for our server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[supervisord]
nodaemon=true


[program:clamd]
directory=/
command=clamd &
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:freshclam]
directory=/
command=freshclam -d
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:fileservice]
directory=/server
command=npm run debug
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
redirect_stderr=true

Server Code

Here is a simple upload server in Typescript

You will need:

  • multer
  • clamav.js
  • express
1
npm i multer clamav.js express --save
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import express from "express";

import multer = require("multer");
import * as stream from "stream";

import clamav from "clamav.js";
import { RequestHandler } from "express-serve-static-core";
import { read } from "fs";

var upload = multer({
storage: multer.memoryStorage()
});

export class Application {
private app = express();

version(): string {
return "1.0";
}

start() {
console.log("Application Started");
this.app.post("/photos/upload", upload.single("photo"), function(
req,
res,
next
) {
const readStream = new stream.Readable();
readStream.push(req.file.buffer);
readStream.push(null);
clamav
.createScanner(3310, "127.0.0.1")
.scan(readStream, function(err, object, malicious) {
if (err) {
console.log(object.path + ": " + err);
next(err);
} else if (malicious) {
console.log(object.path + ": " + malicious + " FOUND");
next(new Error("Virus Detected"));
} else {
console.log(object.path + ": OK");
res.send("OK");
}
});
});

this.app.use(function(err, req, res, next) {
if (err !== null) {
console.log(err);
res.send({ result: "fail", error: err.message });
} else {
next();
}
});

this.app.listen(3000, function() {
console.log("Server listening on port 3000");
});
}

stop(): boolean {
return true;
}
}

const app = new Application();
app.start();

Testing it

You can upload a file to /photos/upload with this content to test the virus scanner

1
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

You can find the complete project here: https://github.com/darkcl/simple-upload

Share Comments