!! REPLACED ClamAV BACKEND: clamdjs -> clamscan !!

Update your config file!
This commit is contained in:
Bobby Wibowo 2020-11-01 06:35:56 +07:00
parent 1902b1c668
commit 88f852584c
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
7 changed files with 55 additions and 45 deletions

View File

@ -112,12 +112,12 @@ You will also need to use this script to overwrite existing thumbnails if you wa
## ClamAV support
This fork has an optional virus scanning support using [ClamAV](https://www.clamav.net/), through [clamdjs](https://github.com/NingLin-P/clamdjs) library.
This fork has an optional virus scanning support using [ClamAV](https://www.clamav.net/), utilizing [clamscan](https://github.com/kylefarris/clamscan) library (Linux and OS X only).
It will scan new files right after they are uploaded. It will then alert the uploaders of the virus names in ClamAV's database if their files are dirty.
Unfortunately, this will slow down uploads processing as it has to wait for scan results before responding the uploaders, however it's still highly recommended for public usage (or at least if you find Google Safe Search too annoying).
Unfortunately, this will slow down uploads processing as it has to wait for the scans before responding the uploaders. However, it's still highly recommended for public usage, or if you're like me who find the constant buzzing from Google Safe Search too annoying.
To enable this, make sure you have ClamAV daemon running, then fill in the daemon's IP and port into your config file.
To enable this, make sure you have [ClamAV installed](https://github.com/kylefarris/clamscan#to-use-local-binary-method-of-scanning), or additionally have [ClamAV daemon running](https://github.com/kylefarris/clamscan#to-use-clamav-using-tcp-sockets). Afterwards configure `uploads.scan` options, and more importantly its sub-option `clamOptions`. Read more about it in the `config.sample.js` file.
From the config file you can also choose to exclude certain extensions from being scanned to lessen the burden on your server.
Additionally, you can also configure usergroups bypass, extensions whitelist, and max file size, to lessen the burden on your server.

View File

@ -348,13 +348,7 @@ module.exports = {
Example: 'moderator' = moderators, admins & superadmins.
*/
scan: {
enabled: false,
ip: '127.0.0.1',
port: 3310,
timeout: 180 * 1000,
chunkSize: 64 * 1024,
enabled: true,
groupBypass: 'admin', // Other group names in controllers/permissionController.js
whitelistExtensions: null, /* [
'.webp',
@ -372,7 +366,27 @@ module.exports = {
'.mov',
'.mkv'
], */
maxSize: null // '25MB' // Needs to be in MB
// Make sure maxSize is no bigger than the max size you configured for your ClamAV
maxSize: null, // Needs to be in MB
// https://github.com/kylefarris/clamscan/tree/v1.3.3#getting-started
// Breaking options (do not use): remove_infected, quarantine_infected
// Untested options (may work): scan_log, debug_mode, file_list, scan_recursively
// Supported options: clamscan, clamdscan, preference
clamOptions: {
// clamscan: {},
clamdscan: {
// When both socket and host+port are specified, it will only use socket
socket: '/var/run/clamav/clamd.ctl',
host: '127.0.0.1',
port: 3310,
timeout: 1 * 60 * 1000, // 1 minute
multiscan: true,
reload_db: false,
active: true
},
preference: 'clamdscan'
}
},
/*

View File

@ -337,7 +337,7 @@ self.actuallyUploadFiles = async (req, res, user, albumid, age) => {
throw 'Empty files are not allowed.'
}
if (utils.clamd.scanner) {
if (utils.clamscan.instance) {
const scanResult = await self.scanFiles(req, user, infoMap)
if (scanResult) throw scanResult
}
@ -444,7 +444,7 @@ self.actuallyUploadUrls = async (req, res, user, albumid, age) => {
// If no errors encountered, clear cache of downloaded files
downloaded.length = 0
if (utils.clamd.scanner) {
if (utils.clamscan.instance) {
const scanResult = await self.scanFiles(req, user, infoMap)
if (scanResult) throw scanResult
}
@ -577,7 +577,7 @@ self.actuallyFinishChunks = async (req, res, user) => {
infoMap.push({ path: destination, data })
}))
if (utils.clamd.scanner) {
if (utils.clamscan.instance) {
const scanResult = await self.scanFiles(req, user, infoMap)
if (scanResult) throw scanResult
}
@ -620,31 +620,30 @@ self.cleanUpChunks = async (uuid, onTimeout) => {
}
self.scanFiles = async (req, user, infoMap) => {
if (user && utils.clamd.groupBypass && perms.is(user, utils.clamd.groupBypass)) {
// logger.log(`[ClamAV]: Skipping ${infoMap.length} file(s), ${utils.clamd.groupBypass} group bypass`)
if (user && utils.clamscan.groupBypass && perms.is(user, utils.clamscan.groupBypass)) {
// logger.log(`[ClamAV]: Skipping ${infoMap.length} file(s), ${utils.clamscan.groupBypass} group bypass`)
return false
}
const foundThreats = []
const results = await Promise.all(infoMap.map(async info => {
if (utils.clamd.whitelistExtensions && utils.clamd.whitelistExtensions.includes(info.data.extname)) {
if (utils.clamscan.whitelistExtensions && utils.clamscan.whitelistExtensions.includes(info.data.extname)) {
return // logger.log(`[ClamAV]: Skipping ${info.data.filename}, extension whitelisted`)
}
if (utils.clamd.maxSize && info.data.size > utils.clamd.maxSize) {
return // logger.log(`[ClamAV]: Skipping ${info.data.filename}, size ${info.data.size} > ${utils.clamd.maxSize}`)
if (utils.clamscan.maxSize && info.data.size > utils.clamscan.maxSize) {
return // logger.log(`[ClamAV]: Skipping ${info.data.filename}, size ${info.data.size} > ${utils.clamscan.maxSize}`)
}
const reply = await utils.clamd.scanner.scanFile(info.path, utils.clamd.timeout, utils.clamd.chunkSize)
if (!reply.includes('OK') || reply.includes('FOUND')) {
// eslint-disable-next-line no-control-regex
const foundThreat = reply.replace(/^stream: /, '').replace(/ FOUND\u0000$/, '')
logger.log(`[ClamAV]: ${info.data.filename}: ${foundThreat} FOUND.`)
foundThreats.push(foundThreat)
const response = await utils.clamscan.instance.is_infected(info.path)
if (response.is_infected) {
logger.log(`[ClamAV]: ${info.data.filename}: ${response.viruses.join(', ')}`)
foundThreats.push(...response.viruses)
}
})).then(() => {
if (foundThreats.length) {
return `Threat found: ${foundThreats[0]}${foundThreats.length > 1 ? ', and more' : ''}.`
const more = foundThreats.length > 1
return `Threat${more ? 's' : ''} detected: ${foundThreats[0]}${more ? ', and more' : ''}.`
}
}).catch(error => {
logger.error(`[ClamAV]: ${error.toString()}`)

View File

@ -12,10 +12,9 @@ const logger = require('./../logger')
const db = require('knex')(config.database)
const self = {
clamd: {
scanner: null,
timeout: config.uploads.scan.timeout || 5000,
chunkSize: config.uploads.scan.chunkSize || 64 * 1024,
clamscan: {
instance: null,
version: null,
groupBypass: config.uploads.scan.groupBypass || null,
whitelistExtensions: (Array.isArray(config.uploads.scan.whitelistExtensions) &&
config.uploads.scan.whitelistExtensions.length)

View File

@ -1,5 +1,5 @@
const bodyParser = require('body-parser')
const clamd = require('clamdjs')
const ClamScan = require('clamscan')
const contentDisposition = require('content-disposition')
const express = require('express')
const helmet = require('helmet')
@ -245,16 +245,14 @@ safe.use('/api', api)
logger.log(`Git commit: ${utils.gitHash}`)
}
// Clamd scanner
// ClamAV scanner
if (config.uploads.scan && config.uploads.scan.enabled) {
const { ip, port } = config.uploads.scan
const version = await clamd.version(ip, port)
logger.log(`${ip}:${port} ${version}`)
utils.clamd.scanner = clamd.createScanner(ip, port)
if (!utils.clamd.scanner) {
throw 'Could not create clamd scanner'
if (!config.uploads.scan.clamOptions) {
throw 'Missing object config.uploads.scan.clamOptions (check config.sample.js)'
}
utils.clamscan.instance = await new ClamScan().init(config.uploads.scan.clamOptions)
utils.clamscan.version = await utils.clamscan.instance.get_version().then(s => s.trim())
logger.log(`Connection established with ${utils.clamscan.version}`)
}
// Cache file identifiers

View File

@ -35,7 +35,7 @@
"bcrypt": "~5.0.0",
"blake3": "~2.1.4",
"body-parser": "~1.19.0",
"clamdjs": "~1.0.2",
"clamscan": "^1.3.3",
"content-disposition": "~0.5.3",
"express": "~4.17.1",
"express-rate-limit": "~5.1.3",

View File

@ -1183,10 +1183,10 @@ ci-info@^2.0.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
clamdjs@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clamdjs/-/clamdjs-1.0.2.tgz#42b3c36370979e0c19efd4e3fbc7e0bf0006a8da"
integrity sha512-gVnX5ySMULvwYL2ykZQnP4UK4nIK7ftG6z015drJyOFgWpsqXt1Hcq4fMyPwM8LLsxfgfYKLiZi288xuTfmZBQ==
clamscan@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/clamscan/-/clamscan-1.3.3.tgz#0cf84dc3278cf7a3c81101d3e647842c34c1a825"
integrity sha512-AwaZeyECbqTWwrc5l7lLbA/cbYBTMW+VC77CnIjK3WCwTm1kGL6PHSXgDaEFsVkfrcRvPvSD1oxJpMzET9lqKg==
class-utils@^0.3.5:
version "0.3.6"