refactoring and adding clamav scanning backend

This commit is contained in:
Volodymyr Smirnov 2020-10-20 17:08:40 +03:00
parent 7e63c77419
commit b1a2357b50
15 changed files with 128 additions and 46 deletions

View File

@ -0,0 +1,24 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dockerfiles/Clamav.Dockerfile" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="mindcollapse/malware-multi-scan-worker-clamav" />
<option name="buildCliOptions" value="" />
<option name="command" value="" />
<option name="containerName" value="malware-multi-scan-worker-clamav" />
<option name="entrypoint" value="" />
<option name="portBindings">
<list>
<DockerPortBindingImpl>
<option name="containerPort" value="9901" />
<option name="hostPort" value="9901" />
</DockerPortBindingImpl>
</list>
</option>
<option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="MalwareMultiScan.Backends/Dockerfiles/Clamav.Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
</component>

View File

@ -13,6 +13,11 @@ namespace MalwareMultiScan.Backends.Backends.Abstracts
{
private readonly ILogger _logger;
protected AbstractLocalProcessScanBackend(ILogger logger)
{
_logger = logger;
}
protected virtual Regex MatchRegex { get; }
protected virtual string BackendPath { get; }
protected virtual bool ParseStdErr { get; }
@ -22,11 +27,6 @@ namespace MalwareMultiScan.Backends.Backends.Abstracts
throw new NotImplementedException();
}
protected AbstractLocalProcessScanBackend(ILogger logger)
{
_logger = logger;
}
public override async Task<string[]> ScanAsync(string path, CancellationToken cancellationToken)
{
var process = new Process

View File

@ -41,7 +41,9 @@ namespace MalwareMultiScan.Backends.Backends.Abstracts
try
{
await using (var tempFileStream = File.OpenWrite(tempFile))
{
await stream.CopyToAsync(tempFileStream, cancellationToken);
}
return await ScanAsync(tempFile, cancellationToken);
}

View File

@ -0,0 +1,32 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using MalwareMultiScan.Backends.Backends.Abstracts;
using Microsoft.Extensions.Logging;
namespace MalwareMultiScan.Backends.Backends.Implementations
{
public class ClamavScanBackend : AbstractLocalProcessScanBackend
{
public ClamavScanBackend(ILogger logger) : base(logger)
{
}
public override string Name { get; } = "Clamav";
public override DateTime DatabaseLastUpdate =>
File.GetLastWriteTime("/var/lib/clamav/daily.cvd");
protected override string BackendPath { get; } = "/usr/bin/clamscan";
protected override Regex MatchRegex { get; } =
new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline);
protected override bool ParseStdErr { get; } = false;
protected override string GetBackendArguments(string path)
{
return $"--no-summary {path}";
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using MalwareMultiScan.Backends.Backends.Abstracts;
@ -9,22 +8,26 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
{
public class WindowsDefenderScanBackend : AbstractLocalProcessScanBackend
{
public WindowsDefenderScanBackend(ILogger logger) : base(logger)
{
}
public override string Name { get; } = "Windows Defender";
public override DateTime DatabaseLastUpdate =>
File.GetLastWriteTime("/opt/engine/mpavbase.vdm");
protected override string BackendPath { get; } = "/opt/mpclient";
protected override Regex MatchRegex { get; } =
new Regex(@"EngineScanCallback\(\)\: Threat (?<threat>[\S]+) identified",
new Regex(@"EngineScanCallback\(\): Threat (?<threat>[\S]+) identified",
RegexOptions.Compiled | RegexOptions.Multiline);
protected override bool ParseStdErr { get; } = true;
protected override string GetBackendArguments(string path) => path;
public WindowsDefenderScanBackend(ILogger logger) : base(logger)
protected override string GetBackendArguments(string path)
{
return path;
}
}
}

View File

@ -1,4 +1,4 @@
FROM mindcollapse/malware-multi-scan-base:latest
FROM mindcollapse/malware-multi-scan-worker:latest
ENV DEBIAN_FRONTEND noninteractive
@ -6,3 +6,5 @@ RUN apt-get update && apt-get install -y clamav
RUN freshclam --quiet
ENV MULTI_SCAN_BACKEND_BIN=/usr/bin/clamscan
ENV BackendType=Clamav

View File

@ -19,4 +19,3 @@ COPY --from=backend /opt/loadlibrary/engine /opt/engine
COPY --from=backend /opt/loadlibrary/mpclient /opt/mpclient
ENV BackendType=Defender
ENV ScanTimeout=300

View File

@ -2,6 +2,7 @@ namespace MalwareMultiScan.Shared.Data.Enums
{
public enum BackendType
{
Defender
Defender,
Clamav
}
}

View File

@ -21,7 +21,9 @@ namespace MalwareMultiScan.Worker.Controllers
var temporaryFile = Path.GetTempFileName();
await using (var temporaryFileSteam = System.IO.File.OpenWrite(temporaryFile))
{
await request.InputFile.CopyToAsync(temporaryFileSteam);
}
BackgroundJob.Enqueue<ScanJob>(
x => x.ScanFile(temporaryFile, request.CallbackUrl));
@ -33,7 +35,7 @@ namespace MalwareMultiScan.Worker.Controllers
[ProducesResponseType(StatusCodes.Status202Accepted)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Route("/scan/url")]
public IActionResult ScanUrl(UrlRequest request)
public IActionResult ScanUrl([FromForm] UrlRequest request)
{
BackgroundJob.Enqueue<ScanJob>(
x => x.ScanUrl(request.InputUrl, request.CallbackUrl));

View File

@ -6,8 +6,7 @@ COPY MalwareMultiScan.Worker /src/MalwareMultiScan.Worker
COPY MalwareMultiScan.Shared /src/MalwareMultiScan.Shared
COPY MalwareMultiScan.Backends /src/MalwareMultiScan.Backends
RUN dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true -o ./publish \
MalwareMultiScan.Worker/MalwareMultiScan.Worker.csproj
RUN dotnet publish -c Release -r linux-x64 -o ./publish MalwareMultiScan.Worker/MalwareMultiScan.Worker.csproj
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1

View File

@ -1,8 +1,11 @@
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Hangfire;
using MalwareMultiScan.Backends.Backends.Implementations;
using MalwareMultiScan.Shared.Data.Enums;
using MalwareMultiScan.Shared.Data.Responses;
@ -14,23 +17,27 @@ namespace MalwareMultiScan.Worker.Jobs
{
public class ScanJob
{
private readonly ILogger<ScanJob> _logger;
private readonly IScanBackend _backend;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<ScanJob> _logger;
private readonly int _scanTimeout;
public ScanJob(IConfiguration configuration, ILogger<ScanJob> logger)
public ScanJob(IConfiguration configuration, ILogger<ScanJob> logger,
IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
_scanTimeout = configuration.GetValue<int>("ScanTimeout");
_backend = configuration.GetValue<BackendType>("BackendType") switch
{
BackendType.Defender => new WindowsDefenderScanBackend(logger),
BackendType.Clamav => new ClamavScanBackend(logger),
_ => throw new NotImplementedException()
};
}
public async Task PostResult(ResultResponse response, Uri callbackUrl, CancellationToken cancellationToken)
private async Task PostResult(ResultResponse response, Uri callbackUrl, CancellationToken cancellationToken)
{
var serializedResponse = JsonSerializer.Serialize(
response, typeof(ResultResponse), new JsonSerializerOptions
@ -38,7 +45,15 @@ namespace MalwareMultiScan.Worker.Jobs
WriteIndented = true
});
_logger.LogInformation(serializedResponse);
_logger.LogInformation(
$"Sending following payload to {callbackUrl}: {serializedResponse}");
using var httpClient = _httpClientFactory.CreateClient();
var callbackResponse = await httpClient.PostAsync(callbackUrl,
new StringContent(serializedResponse, Encoding.UTF8, "application/json"), cancellationToken);
_logger.LogInformation($"Callback URL {callbackUrl} returned a status {callbackResponse.StatusCode}");
}
private async Task Scan(Func<CancellationToken, Task<string[]>> scanMethod, Uri callbackUrl)
@ -70,11 +85,13 @@ namespace MalwareMultiScan.Worker.Jobs
await PostResult(response, callbackUrl, cancellationToken);
}
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public async Task ScanUrl(Uri url, Uri callbackUrl)
{
await Scan(async t => await _backend.ScanAsync(url, t), callbackUrl);
}
[AutomaticRetry(Attempts = 0, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public async Task ScanFile(string file, Uri callbackUrl)
{
try

View File

@ -13,6 +13,7 @@ namespace MalwareMultiScan.Worker
{
services.AddLogging();
services.AddControllers();
services.AddHttpClient();
services.AddSingleton<ScanJob>();