Quarkus Antivirus

A Quarkus extension that lets you scan files for viruses using a pluggable engine architecture.

Out of the box these engines are supported by this extension:

  • ClamAV which is a Linux Native antivirus server

  • VirusTotal which is a REST API to check the Hash of a file to see if it has already been reported for viruses

Installation

If you want to use this extension, you need to add the io.quarkiverse.antivirus:quarkus-antivirus extension first to your build file.

For instance, with Maven, add the following dependency to your POM file:

<dependency>
    <groupId>io.quarkiverse.antivirus</groupId>
    <artifactId>quarkus-antivirus</artifactId>
    <version>0.0.5</version>
</dependency>

Configuration

Now that you configured your POM to use the service, now you need to configure which scanner(s) you want to use in application.properties:

ClamAV

ClamAV is an open source Linux based virus scanning engine. In development mode a DevService will start a ClamAV instance for you on TCP port 3310, so you can test locally during development. If you override with your own host and port the DevService will not start.

quarkus.antivirus.clamav.enabled=true
quarkus.antivirus.clamav.health.enabled=true
%prod.quarkus.antivirus.clamav.host=192.168.7.72
%prod.quarkus.antivirus.clamav.port=3310

VirusTotal

VirusTotal is a REST API that analyses suspicious files to detect malware using over 70 antivirus scanners. VirusTotal checks the hash of a file to see if it has been scanned and what the results are. You can set the threshold of how many of the 70+ engines you want to report the file as malicious before you consider it a malicious file using the minimum-votes property.

quarkus.antivirus.virustotal.enabled=true
quarkus.antivirus.virustotal.key=<YOUR API KEY>
quarkus.antivirus.virustotal.minimum-votes=1

Usage

Simply inject the Antivirus service, and it will run the scan against all configured services. It works against InputStream so it can be used in any Quarkus application it is not constrained to REST applications only.

@Path("/antivirus")
@ApplicationScoped
public class AntivirusResource {

    @Inject
    Antivirus antivirus;

    @PUT
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/upload")
    public Response upload(@MultipartForm @Valid final UploadRequest fileUploadRequest) {
        final String fileName = fileUploadRequest.getFileName();
        final InputStream data = fileUploadRequest.getData();
        try {
            // copy the stream to make it resettable
            final ByteArrayInputStream inputStream = new ByteArrayInputStream(
                    IOUtils.toBufferedInputStream(data).readAllBytes());

            // scan the file and check the results
            List<AntivirusScanResult> results = antivirus.scan(fileName, inputStream);
            for (AntivirusScanResult result : results) {
                if (result.getStatus() != Response.Status.OK.getStatusCode()) {
                    throw new WebApplicationException(result.getMessage(), result.getStatus());
                }
            }

            // reset the stream
            inputStream.reset();

            // write the file out to disk
            final File tempFile = File.createTempFile("fileName", "tmp");
            IOUtils.copy(inputStream, new FileOutputStream(tempFile));
        } catch (AntivirusException | IOException e) {
            throw new BadRequestException(e);
        }

        return Response.ok().build();
    }
}

Pluggable

We can’t anticipate every antivirus engine out there and some may be proprietary. However, the architecture is designed to be pluggable, so you can plug your own engine in. Simply produce a bean that extends the AntivirusEngine interface, and it will be picked up and used.

@ApplicationScoped
public class MyCustomEngine implements AntivirusEngine {

    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public AntivirusScanResult scan(final String filename, final InputStream inputStream) {
        // scan your file here
    }
}

Dev UI

The Dev UI will let you inspect the running ClamAV Dev Service.

Antivirus DevUI Card

You can also inspect the container image that was started under Dev Services:

Antivirus Dev Service

You can view all of ClamAV’s container logs right in the DevUI log.

Antivirus Logs

Extension Configuration Reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

If Dev Services for ClamAV has been explicitly enabled or disabled. Dev Services are generally enabled by default, unless there is an existing configuration present.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_ENABLED

boolean

true

The ClamAV container image to use.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_IMAGE_NAME

string

clamav/clamav

Indicates if the ClamAV server managed by Quarkus Dev Services is shared. When shared, Quarkus looks for running containers using label-based service discovery. If a matching container is found, it is used, and so a second one is not started. Otherwise, Dev Services for ClamAV starts a new container.

The discovery uses the quarkus-dev-service-clamav label. The value is configured using the service-name property.

Container sharing is only used in dev mode.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_SHARED

boolean

true

The value of the quarkus-dev-service-clamav label attached to the started container. This property is used when shared is set to true. In this case, before starting a container, Dev Services for Minio looks for a container with the quarkus-dev-service-clamav label set to the configured value. If found, it will use this container instead of starting a new one. Otherwise, it starts a new container with the quarkus-dev-service-clamav label set to the specified value.

This property is used when you need multiple shared ClamAV servers.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_SERVICE_NAME

string

clamav

The ClamAV container image to use.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_STARTUP_TIMEOUT

int

1800

Flag to enable the FreshClam daemon to update the virus database daily. Default it is disabled.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_FRESH_CLAM

boolean

false

Enable or disable ClamAV container logging

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_DEVSERVICE_LOGGING

boolean

true

If ClamAv registers in the health check by pinging the service.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_HEALTH_ENABLED

boolean

true

Property to enable or disable the virus scanner. Useful for developers who don’t want to scan files locally.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_ENABLED

boolean

false

The IP Address of the machine where ClamAV is running.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_HOST

string

The Port of the machine where ClamAV is running.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_PORT

int

3310

The timeout of how much time to give CLamAV to scan the virus before failing.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_SCAN_TIMEOUT

int

60000

Size in bytes of the chunks of data to stream to the scanner at a time.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_CHUNK_SIZE

int

10240

The timeout of how much time to give CLamAV to scan the virus before failing.

Environment variable: QUARKUS_ANTIVIRUS_CLAMAV_PING_TIMEOUT

int

2000

Property to enable or disable the virus scanner. Useful for developers who don’t want to scan files locally.

Environment variable: QUARKUS_ANTIVIRUS_VIRUSTOTAL_ENABLED

boolean

false

The API endpoint for VirusTotal.

Environment variable: QUARKUS_ANTIVIRUS_VIRUSTOTAL_URL

string

https://www.virustotal.com/api/v3/files/%s

The API key for VirusTotal.

Environment variable: QUARKUS_ANTIVIRUS_VIRUSTOTAL_KEY

string

VirusTotal checks over 70+ different engine for virus and collates a count of how many of those 70 reported a file as malicious. This number lets you control how many engines have to report a file is malicious to raise an exception.

Environment variable: QUARKUS_ANTIVIRUS_VIRUSTOTAL_MINIMUM_VOTES

int

1