mirror of
https://github.com/volodymyrsmirnov/MalwareMultiScan.git
synced 2025-08-24 13:32:22 +00:00
change the communication protocol to RMQ
This commit is contained in:
parent
b6c3cb4131
commit
29156b7f10
@ -8,11 +8,11 @@ namespace MalwareMultiScan.Api.Controllers
|
|||||||
[Route("download")]
|
[Route("download")]
|
||||||
public class DownloadController : Controller
|
public class DownloadController : Controller
|
||||||
{
|
{
|
||||||
private readonly ScanResultsService _scanResultsService;
|
private readonly ScanResultService _scanResultService;
|
||||||
|
|
||||||
public DownloadController(ScanResultsService scanResultsService)
|
public DownloadController(ScanResultService scanResultService)
|
||||||
{
|
{
|
||||||
_scanResultsService = scanResultsService;
|
_scanResultService = scanResultService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
@ -20,7 +20,7 @@ namespace MalwareMultiScan.Api.Controllers
|
|||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> Index(string id)
|
public async Task<IActionResult> Index(string id)
|
||||||
{
|
{
|
||||||
var fileStream = await _scanResultsService.ObtainFile(id);
|
var fileStream = await _scanResultService.ObtainFile(id);
|
||||||
|
|
||||||
if (fileStream == null)
|
if (fileStream == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MalwareMultiScan.Api.Services;
|
||||||
|
using MalwareMultiScan.Shared.Attributes;
|
||||||
|
using MalwareMultiScan.Shared.Data.Responses;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace MalwareMultiScan.Api.Controllers
|
namespace MalwareMultiScan.Api.Controllers
|
||||||
@ -7,9 +14,45 @@ namespace MalwareMultiScan.Api.Controllers
|
|||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
public class QueueController : Controller
|
public class QueueController : Controller
|
||||||
{
|
{
|
||||||
public IActionResult Index()
|
private readonly ScanResultService _scanResultService;
|
||||||
|
|
||||||
|
public QueueController( ScanResultService scanResultService)
|
||||||
{
|
{
|
||||||
return Ok();
|
_scanResultService = scanResultService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("file")]
|
||||||
|
[ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
public async Task<IActionResult> ScanFile([FromForm] IFormFile file)
|
||||||
|
{
|
||||||
|
var result = await _scanResultService.CreateScanResult();
|
||||||
|
|
||||||
|
string storedFileId;
|
||||||
|
|
||||||
|
await using (var uploadFileStream = file.OpenReadStream())
|
||||||
|
storedFileId = await _scanResultService.StoreFile(file.Name, uploadFileStream);
|
||||||
|
|
||||||
|
await _scanResultService.QueueUrlScan(result, Url.Action("Index", "Download", new {id = storedFileId},
|
||||||
|
Request.Scheme, Request.Host.Value));
|
||||||
|
|
||||||
|
return Created(Url.Action("Index", "ScanResults", new {id = result.Id},
|
||||||
|
Request.Scheme, Request.Host.Value), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("url")]
|
||||||
|
[ProducesResponseType(typeof(ResultResponse), StatusCodes.Status201Created)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
public async Task<IActionResult> ScanUrl([FromForm] [UrlValidation] Uri url)
|
||||||
|
{
|
||||||
|
var result = await _scanResultService.CreateScanResult();
|
||||||
|
|
||||||
|
var resultUrl = Url.Action("Index", "ScanResults", new {id = result.Id},
|
||||||
|
Request.Scheme, Request.Host.Value);
|
||||||
|
|
||||||
|
await _scanResultService.QueueUrlScan(result, url.ToString());
|
||||||
|
|
||||||
|
return Created(resultUrl, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,41 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MalwareMultiScan.Api.Data.Configuration;
|
|
||||||
using MalwareMultiScan.Api.Data.Response;
|
|
||||||
using MalwareMultiScan.Api.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace MalwareMultiScan.Api.Controllers
|
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("backends")]
|
|
||||||
[Produces("application/json")]
|
|
||||||
public class ScanBackendsController : Controller
|
|
||||||
{
|
|
||||||
private readonly ScanBackendService _scanBackendService;
|
|
||||||
|
|
||||||
public ScanBackendsController(ScanBackendService scanBackendService)
|
|
||||||
{
|
|
||||||
_scanBackendService = scanBackendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<ScanBackendResponse> GetScanBackendResponse(ScanBackend backend)
|
|
||||||
{
|
|
||||||
return new ScanBackendResponse
|
|
||||||
{
|
|
||||||
Id = backend.Id,
|
|
||||||
Name = backend.Name,
|
|
||||||
Online = await _scanBackendService.Ping(backend)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[ProducesResponseType(typeof(ScanBackendResponse[]), StatusCodes.Status200OK)]
|
|
||||||
public async Task<IActionResult> Index()
|
|
||||||
{
|
|
||||||
return Ok(await Task.WhenAll(
|
|
||||||
_scanBackendService.List.Select(GetScanBackendResponse).ToArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MalwareMultiScan.Api.Data.Models;
|
||||||
using MalwareMultiScan.Api.Services;
|
using MalwareMultiScan.Api.Services;
|
||||||
using MalwareMultiScan.Shared.Data.Responses;
|
using MalwareMultiScan.Shared.Data.Responses;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
@ -11,21 +12,23 @@ namespace MalwareMultiScan.Api.Controllers
|
|||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
public class ScanResultsController : Controller
|
public class ScanResultsController : Controller
|
||||||
{
|
{
|
||||||
private readonly ScanResultsService _scanResultsService;
|
private readonly ScanResultService _scanResultService;
|
||||||
|
|
||||||
public ScanResultsController(ScanResultsService scanResultsService)
|
public ScanResultsController(ScanResultService scanResultService)
|
||||||
{
|
{
|
||||||
_scanResultsService = scanResultsService;
|
_scanResultService = scanResultService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(typeof(ScanResult), StatusCodes.Status200OK)]
|
||||||
public async Task<IActionResult> Index(string id, [FromBody] ResultResponse result)
|
public async Task<IActionResult> Index(string id)
|
||||||
{
|
{
|
||||||
await _scanResultsService.UpdateScanResultForBackend(
|
var scanResult = await _scanResultService.GetScanResult(id);
|
||||||
id, result.Backend, true, result.Success, result.Threats);
|
|
||||||
|
|
||||||
return NoContent();
|
if (scanResult == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(scanResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,6 +4,5 @@ namespace MalwareMultiScan.Api.Data.Configuration
|
|||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Endpoint { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,8 +11,10 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EasyNetQ" Version="5.6.0" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.11.3" />
|
<PackageReference Include="MongoDB.Driver" Version="2.11.3" />
|
||||||
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.11.3" />
|
<PackageReference Include="MongoDB.Driver.GridFS" Version="2.11.3" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" />
|
||||||
<PackageReference Include="YamlDotNet" Version="8.1.2" />
|
<PackageReference Include="YamlDotNet" Version="8.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
41
MalwareMultiScan.Api/Services/ReceiverHostedService.cs
Normal file
41
MalwareMultiScan.Api/Services/ReceiverHostedService.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EasyNetQ;
|
||||||
|
using MalwareMultiScan.Shared.Data.Messages;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Api.Services
|
||||||
|
{
|
||||||
|
public class ReceiverHostedService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IBus _bus;
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly ScanResultService _scanResultService;
|
||||||
|
|
||||||
|
public ReceiverHostedService(IBus bus, IConfiguration configuration, ScanResultService scanResultService)
|
||||||
|
{
|
||||||
|
_bus = bus;
|
||||||
|
_configuration = configuration;
|
||||||
|
_scanResultService = scanResultService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_bus.Receive<ScanResultMessage>(_configuration.GetValue<string>("ResultsSubscriptionId"), async message =>
|
||||||
|
{
|
||||||
|
await _scanResultService.UpdateScanResultForBackend(
|
||||||
|
message.Id, message.Backend, true, true, message.Threats);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_bus.Dispose();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using EasyNetQ;
|
||||||
using MalwareMultiScan.Api.Data.Configuration;
|
using MalwareMultiScan.Api.Data.Configuration;
|
||||||
using MalwareMultiScan.Shared.Data.Requests;
|
using MalwareMultiScan.Api.Data.Models;
|
||||||
|
using MalwareMultiScan.Shared.Data.Messages;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
using YamlDotNet.Serialization.NamingConventions;
|
using YamlDotNet.Serialization.NamingConventions;
|
||||||
|
|
||||||
@ -14,14 +13,12 @@ namespace MalwareMultiScan.Api.Services
|
|||||||
{
|
{
|
||||||
public class ScanBackendService
|
public class ScanBackendService
|
||||||
{
|
{
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IBus _bus;
|
||||||
private readonly ILogger<ScanBackendService> _logger;
|
|
||||||
|
|
||||||
public ScanBackendService(IConfiguration configuration, IHttpClientFactory httpClientFactory,
|
public ScanBackendService(IConfiguration configuration, IBus bus)
|
||||||
ILogger<ScanBackendService> logger)
|
|
||||||
{
|
{
|
||||||
_httpClientFactory = httpClientFactory;
|
_bus = bus;
|
||||||
_logger = logger;
|
|
||||||
var configurationPath = configuration.GetValue<string>("BackendsConfiguration");
|
var configurationPath = configuration.GetValue<string>("BackendsConfiguration");
|
||||||
|
|
||||||
if (!File.Exists(configurationPath))
|
if (!File.Exists(configurationPath))
|
||||||
@ -38,73 +35,12 @@ namespace MalwareMultiScan.Api.Services
|
|||||||
|
|
||||||
public ScanBackend[] List { get; }
|
public ScanBackend[] List { get; }
|
||||||
|
|
||||||
public async Task<bool> Ping(ScanBackend backend)
|
public async Task QueueUrlScan(ScanResult result, ScanBackend backend, string fileUrl)
|
||||||
{
|
{
|
||||||
var cancellationTokenSource = new CancellationTokenSource(
|
await _bus.SendAsync(backend.Id, new ScanRequestMessage
|
||||||
TimeSpan.FromSeconds(1));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
using var httpClient = _httpClientFactory.CreateClient();
|
Id = result.Id,
|
||||||
|
Uri = new Uri(fileUrl)
|
||||||
var pingResponse = await httpClient.GetAsync(
|
|
||||||
new Uri(new Uri(backend.Endpoint), "/ping"), cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
pingResponse.EnsureSuccessStatusCode();
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.LogError(
|
|
||||||
exception, $"Failed to ping {backend.Id}");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> QueueScan(ScanBackend backend, string uri, HttpContent content)
|
|
||||||
{
|
|
||||||
var cancellationTokenSource = new CancellationTokenSource(
|
|
||||||
TimeSpan.FromSeconds(5));
|
|
||||||
|
|
||||||
using var httpClient = _httpClientFactory.CreateClient();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var response = await httpClient.PostAsync(
|
|
||||||
new Uri(new Uri(backend.Endpoint), uri), content, cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.LogError(
|
|
||||||
exception, $"Failed to initiate scan against {backend.Id}");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> QueueFileScan(
|
|
||||||
ScanBackend backend, string fileName, Stream fileStream, string callbackUrl)
|
|
||||||
{
|
|
||||||
return await QueueScan(backend, "/scan/file", new MultipartFormDataContent
|
|
||||||
{
|
|
||||||
{new StringContent(callbackUrl), nameof(FileRequest.CallbackUrl)},
|
|
||||||
{new StreamContent(fileStream), nameof(FileRequest.InputFile), fileName}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> QueueUrlScan(
|
|
||||||
ScanBackend backend, string url, string callbackUrl)
|
|
||||||
{
|
|
||||||
return await QueueScan(backend, "/scan/url", new MultipartFormDataContent
|
|
||||||
{
|
|
||||||
{new StringContent(callbackUrl), nameof(UrlRequest.CallbackUrl)},
|
|
||||||
{new StringContent(url), nameof(UrlRequest.InputUrl)}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -13,7 +14,7 @@ using MongoDB.Driver.GridFS;
|
|||||||
|
|
||||||
namespace MalwareMultiScan.Api.Services
|
namespace MalwareMultiScan.Api.Services
|
||||||
{
|
{
|
||||||
public class ScanResultsService
|
public class ScanResultService
|
||||||
{
|
{
|
||||||
private const string CollectionName = "ScanResults";
|
private const string CollectionName = "ScanResults";
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ namespace MalwareMultiScan.Api.Services
|
|||||||
|
|
||||||
private readonly ScanBackendService _scanBackendService;
|
private readonly ScanBackendService _scanBackendService;
|
||||||
|
|
||||||
public ScanResultsService(IMongoDatabase db, ScanBackendService scanBackendService)
|
public ScanResultService(IMongoDatabase db, ScanBackendService scanBackendService)
|
||||||
{
|
{
|
||||||
_scanBackendService = scanBackendService;
|
_scanBackendService = scanBackendService;
|
||||||
|
|
||||||
@ -32,15 +33,27 @@ namespace MalwareMultiScan.Api.Services
|
|||||||
|
|
||||||
public async Task<ScanResult> CreateScanResult()
|
public async Task<ScanResult> CreateScanResult()
|
||||||
{
|
{
|
||||||
var scanResult = new ScanResult();
|
var scanResult = new ScanResult
|
||||||
|
{
|
||||||
|
Results = _scanBackendService.List.ToDictionary(
|
||||||
|
k => k.Id, v => new ScanResultEntry())
|
||||||
|
};
|
||||||
|
|
||||||
await _collection.InsertOneAsync(scanResult);
|
await _collection.InsertOneAsync(scanResult);
|
||||||
|
|
||||||
return scanResult;
|
return scanResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateScanResultForBackend(
|
public async Task<ScanResult> GetScanResult(string id)
|
||||||
string resultId, string backendId, bool completed, bool succeeded, string[] threats = null)
|
{
|
||||||
|
var result = await _collection.FindAsync(
|
||||||
|
Builders<ScanResult>.Filter.Where(r => r.Id == id));
|
||||||
|
|
||||||
|
return await result.FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateScanResultForBackend(string resultId, string backendId,
|
||||||
|
bool completed = false, bool succeeded = false, string[] threats = null)
|
||||||
{
|
{
|
||||||
var filterScanResult = Builders<ScanResult>.Filter.Where(r => r.Id == resultId);
|
var filterScanResult = Builders<ScanResult>.Filter.Where(r => r.Id == resultId);
|
||||||
|
|
||||||
@ -54,31 +67,16 @@ namespace MalwareMultiScan.Api.Services
|
|||||||
await _collection.UpdateOneAsync(filterScanResult, updateScanResult);
|
await _collection.UpdateOneAsync(filterScanResult, updateScanResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task QueueFileScan(ScanResult result, string fileName, Stream fileStream, string callbackUrl)
|
public async Task QueueUrlScan(ScanResult result, string fileUrl)
|
||||||
{
|
{
|
||||||
foreach (var backend in _scanBackendService.List)
|
foreach (var backend in _scanBackendService.List)
|
||||||
{
|
await _scanBackendService.QueueUrlScan(result, backend, fileUrl);
|
||||||
var queueResult = await _scanBackendService.QueueFileScan(
|
|
||||||
backend, fileName, fileStream, callbackUrl);
|
|
||||||
|
|
||||||
await UpdateScanResultForBackend(result.Id, backend.Id, !queueResult, queueResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task QueueUrlScan(ScanResult result, string url, string callbackUrl)
|
|
||||||
{
|
|
||||||
foreach (var backend in _scanBackendService.List)
|
|
||||||
{
|
|
||||||
var queueResult = await _scanBackendService.QueueUrlScan(
|
|
||||||
backend, url, callbackUrl);
|
|
||||||
|
|
||||||
await UpdateScanResultForBackend(result.Id, backend.Id, !queueResult, queueResult);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> StoreFile(string fileName, Stream fileStream)
|
public async Task<string> StoreFile(string fileName, Stream fileStream)
|
||||||
{
|
{
|
||||||
var objectId = await _bucket.UploadFromStreamAsync(fileName, fileStream);
|
var objectId = await _bucket.UploadFromStreamAsync(
|
||||||
|
fileName, fileStream);
|
||||||
|
|
||||||
return objectId.ToString();
|
return objectId.ToString();
|
||||||
}
|
}
|
@ -1,8 +1,11 @@
|
|||||||
|
using EasyNetQ;
|
||||||
using MalwareMultiScan.Api.Services;
|
using MalwareMultiScan.Api.Services;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace MalwareMultiScan.Api
|
namespace MalwareMultiScan.Api
|
||||||
@ -18,8 +21,11 @@ namespace MalwareMultiScan.Api
|
|||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
services.AddSingleton(x =>
|
||||||
|
RabbitHutch.CreateBus(_configuration.GetConnectionString("RabbitMQ")));
|
||||||
|
|
||||||
services.AddSingleton<ScanBackendService>();
|
services.AddSingleton<ScanBackendService>();
|
||||||
services.AddSingleton<ScanResultsService>();
|
services.AddSingleton<ScanResultService>();
|
||||||
|
|
||||||
services.AddSingleton(
|
services.AddSingleton(
|
||||||
serviceProvider =>
|
serviceProvider =>
|
||||||
@ -32,13 +38,45 @@ namespace MalwareMultiScan.Api
|
|||||||
|
|
||||||
services.AddControllers();
|
services.AddControllers();
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
|
|
||||||
|
services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SwaggerDoc("MalwareMultiScan",
|
||||||
|
new OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = "MalwareMultiScan",
|
||||||
|
Version = "1.0.0"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddHostedService<ReceiverHostedService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
{
|
{
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
var forwardingOptions = new ForwardedHeadersOptions
|
||||||
|
{
|
||||||
|
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||||
|
};
|
||||||
|
|
||||||
|
forwardingOptions.KnownNetworks.Clear();
|
||||||
|
forwardingOptions.KnownProxies.Clear();
|
||||||
|
|
||||||
|
app.UseForwardedHeaders(forwardingOptions);
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints => endpoints.MapControllers());
|
||||||
|
|
||||||
|
app.UseSwagger();
|
||||||
|
|
||||||
|
app.UseSwaggerUI(options =>
|
||||||
|
{
|
||||||
|
options.DocumentTitle = "MalwareMultiScan";
|
||||||
|
|
||||||
|
options.SwaggerEndpoint(
|
||||||
|
$"/swagger/{options.DocumentTitle}/swagger.json", options.DocumentTitle);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,11 +9,13 @@
|
|||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"Mongo": "mongodb://localhost:27017"
|
"Mongo": "mongodb://localhost:27017",
|
||||||
|
"RabbitMQ": "host=localhost"
|
||||||
},
|
},
|
||||||
|
|
||||||
"DatabaseName": "MalwareMultiScan",
|
"DatabaseName": "MalwareMultiScan",
|
||||||
|
|
||||||
"BackendsConfiguration": "backends.yaml",
|
"ResultsSubscriptionId": "mms.results",
|
||||||
"StoreFileUploads": true
|
|
||||||
|
"BackendsConfiguration": "backends.yaml"
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,2 @@
|
|||||||
- id: windows-defender
|
- id: dummy
|
||||||
name: Windows Defender
|
name: Dummy Backend
|
||||||
endpoint: http://localhost:9902
|
|
||||||
|
|
||||||
- id: clamav
|
|
||||||
name: ClamAV
|
|
||||||
endpoint: http://localhost:9901
|
|
@ -17,6 +17,8 @@ namespace MalwareMultiScan.Backends.Backends.Implementations
|
|||||||
protected override Regex MatchRegex { get; } =
|
protected override Regex MatchRegex { get; } =
|
||||||
new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline);
|
new Regex(@"(\S+): (?<threat>[\S]+) FOUND", RegexOptions.Compiled | RegexOptions.Multiline);
|
||||||
|
|
||||||
|
protected override bool ThrowOnNonZeroExitCode { get; } = false;
|
||||||
|
|
||||||
protected override string GetBackendArguments(string path)
|
protected override string GetBackendArguments(string path)
|
||||||
{
|
{
|
||||||
return $"--no-summary {path}";
|
return $"--no-summary {path}";
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MalwareMultiScan.Shared.Interfaces;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Backends.Backends.Implementations
|
||||||
|
{
|
||||||
|
public class DummyScanBackend : IScanBackend
|
||||||
|
{
|
||||||
|
public string Id { get; } = "dummy";
|
||||||
|
|
||||||
|
public Task<string[]> ScanAsync(string path, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string[]> ScanAsync(Uri uri, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string[]> ScanAsync(IFormFile file, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string[]> ScanAsync(Stream stream, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Task<string[]> Scan()
|
||||||
|
{
|
||||||
|
return Task.FromResult(new[] {"Malware.Dummy.Result"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using MalwareMultiScan.Backends.Backends.Implementations;
|
||||||
|
using MalwareMultiScan.Shared.Data.Enums;
|
||||||
|
using MalwareMultiScan.Shared.Interfaces;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Scanner.Extensions
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static void AddScanningBackend(this IServiceCollection services, BackendType type)
|
||||||
|
{
|
||||||
|
using var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
var logger = provider.GetService<ILogger>();
|
||||||
|
|
||||||
|
services.AddSingleton<IScanBackend>(type switch
|
||||||
|
{
|
||||||
|
BackendType.Dummy => new DummyScanBackend(),
|
||||||
|
BackendType.Defender => new WindowsDefenderScanBackend(logger),
|
||||||
|
BackendType.Clamav => new ClamavScanBackend(logger),
|
||||||
|
BackendType.DrWeb => new DrWebScanBackend(logger),
|
||||||
|
BackendType.Kes => new KesScanBackend(logger),
|
||||||
|
BackendType.Comodo => new ComodoScanBackend(logger),
|
||||||
|
BackendType.Sophos => new SophosScanBackend(logger),
|
||||||
|
BackendType.McAfee => new McAfeeScanBackend(logger),
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj
Normal file
16
MalwareMultiScan.Scanner/MalwareMultiScan.Scanner.csproj
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EasyNetQ" Version="5.6.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.8" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\MalwareMultiScan.Backends\MalwareMultiScan.Backends.csproj" />
|
||||||
|
<ProjectReference Include="..\MalwareMultiScan.Shared\MalwareMultiScan.Shared.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
31
MalwareMultiScan.Scanner/Program.cs
Normal file
31
MalwareMultiScan.Scanner/Program.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using MalwareMultiScan.Scanner.Extensions;
|
||||||
|
using MalwareMultiScan.Shared.Data.Enums;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Scanner
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
await Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureAppConfiguration(configure =>
|
||||||
|
{
|
||||||
|
configure.AddJsonFile("settings.json");
|
||||||
|
configure.AddEnvironmentVariables();
|
||||||
|
})
|
||||||
|
.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
services.AddLogging();
|
||||||
|
|
||||||
|
services.AddScanningBackend(
|
||||||
|
context.Configuration.GetValue<BackendType>("BackendType"));
|
||||||
|
|
||||||
|
services.AddHostedService<ScanHostedService>();
|
||||||
|
}).RunConsoleAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
MalwareMultiScan.Scanner/Properties/launchSettings.json
Normal file
10
MalwareMultiScan.Scanner/Properties/launchSettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"MalwareMultiScan.Scanner": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"environmentVariables": {
|
||||||
|
"DOTNET_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
MalwareMultiScan.Scanner/ScanHostedService.cs
Normal file
73
MalwareMultiScan.Scanner/ScanHostedService.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EasyNetQ;
|
||||||
|
using MalwareMultiScan.Shared.Data.Messages;
|
||||||
|
using MalwareMultiScan.Shared.Interfaces;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Scanner
|
||||||
|
{
|
||||||
|
public class ScanHostedService : IHostedService
|
||||||
|
{
|
||||||
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly ILogger<ScanHostedService> _logger;
|
||||||
|
private readonly IScanBackend _scanBackend;
|
||||||
|
|
||||||
|
private IBus _bus;
|
||||||
|
|
||||||
|
public ScanHostedService(ILogger<ScanHostedService> logger, IConfiguration configuration, IScanBackend scanBackend)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_configuration = configuration;
|
||||||
|
_scanBackend = scanBackend;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_bus = RabbitHutch.CreateBus(
|
||||||
|
_configuration.GetConnectionString("RabbitMQ"));
|
||||||
|
|
||||||
|
_bus.Receive<ScanRequestMessage>(
|
||||||
|
_scanBackend.Id, Scan);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_bus.Dispose();
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Scan(ScanRequestMessage message)
|
||||||
|
{
|
||||||
|
_logger.LogInformation(
|
||||||
|
$"Starting scan of {message.Uri} via backend {_scanBackend.Id}");
|
||||||
|
|
||||||
|
var cancellationTokenSource = new CancellationTokenSource(
|
||||||
|
TimeSpan.FromSeconds(_configuration.GetValue<int>("MaxScanningTime")));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _bus.SendAsync(_configuration.GetValue<string>("ResultsSubscriptionId"), new ScanResultMessage
|
||||||
|
{
|
||||||
|
Id = message.Id,
|
||||||
|
Backend = _scanBackend.Id,
|
||||||
|
|
||||||
|
Threats = await _scanBackend.ScanAsync(
|
||||||
|
message.Uri, cancellationTokenSource.Token)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
exception, "Scanning failed with exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
MalwareMultiScan.Scanner/settings.json
Normal file
20
MalwareMultiScan.Scanner/settings.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"BackendType": "Dummy",
|
||||||
|
|
||||||
|
"MaxWorkers": 5,
|
||||||
|
"MaxScanningTime": 60,
|
||||||
|
|
||||||
|
"ResultsSubscriptionId": "mms.results",
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RabbitMQ": "host=localhost;prefetchcount=1"
|
||||||
|
}
|
||||||
|
}
|
@ -9,8 +9,8 @@ namespace MalwareMultiScan.Shared.Attributes
|
|||||||
{
|
{
|
||||||
var uri = (Uri) value;
|
var uri = (Uri) value;
|
||||||
|
|
||||||
if (uri == null || uri.Scheme != "http" && uri.Scheme != "https")
|
if (uri == null || !uri.IsAbsoluteUri || uri.Scheme != "http" && uri.Scheme != "https")
|
||||||
return new ValidationResult("Only http(s) URLs are supported");
|
return new ValidationResult("Only http(s) absolute URLs are supported");
|
||||||
|
|
||||||
return ValidationResult.Success;
|
return ValidationResult.Success;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ namespace MalwareMultiScan.Shared.Data.Enums
|
|||||||
{
|
{
|
||||||
public enum BackendType
|
public enum BackendType
|
||||||
{
|
{
|
||||||
|
Dummy,
|
||||||
Defender,
|
Defender,
|
||||||
Clamav,
|
Clamav,
|
||||||
DrWeb,
|
DrWeb,
|
||||||
|
11
MalwareMultiScan.Shared/Data/Messages/ScanRequestMessage.cs
Normal file
11
MalwareMultiScan.Shared/Data/Messages/ScanRequestMessage.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MalwareMultiScan.Shared.Data.Messages
|
||||||
|
{
|
||||||
|
public class ScanRequestMessage
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
public Uri Uri { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
namespace MalwareMultiScan.Shared.Data.Messages
|
||||||
|
{
|
||||||
|
public class ScanResultMessage
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Backend { get; set; }
|
||||||
|
public string[] Threats { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Backends",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "MalwareMultiScan.Api\MalwareMultiScan.Api.csproj", "{7B63B897-D390-4617-821F-F96799CBA2F4}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Api", "MalwareMultiScan.Api\MalwareMultiScan.Api.csproj", "{7B63B897-D390-4617-821F-F96799CBA2F4}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MalwareMultiScan.Scanner", "MalwareMultiScan.Scanner\MalwareMultiScan.Scanner.csproj", "{8A16A3C4-2AE3-4F63-8280-635FF7878080}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -30,5 +32,9 @@ Global
|
|||||||
{7B63B897-D390-4617-821F-F96799CBA2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{7B63B897-D390-4617-821F-F96799CBA2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
{7B63B897-D390-4617-821F-F96799CBA2F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8A16A3C4-2AE3-4F63-8280-635FF7878080}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
Loading…
x
Reference in New Issue
Block a user