Enables code execution within a Docker sandbox
All checks were successful
Build and Push Docker Images / build (src/LiquidCode.Tester.Gateway/Dockerfile, git.nullptr.top/liquidcode/liquidcode-tester-gateway-roman, gateway) (push) Successful in 53s
Build and Push Docker Images / build (src/LiquidCode.Tester.Worker/Dockerfile, git.nullptr.top/liquidcode/liquidcode-tester-worker-roman, worker) (push) Successful in 3m51s
All checks were successful
Build and Push Docker Images / build (src/LiquidCode.Tester.Gateway/Dockerfile, git.nullptr.top/liquidcode/liquidcode-tester-gateway-roman, gateway) (push) Successful in 53s
Build and Push Docker Images / build (src/LiquidCode.Tester.Worker/Dockerfile, git.nullptr.top/liquidcode/liquidcode-tester-worker-roman, worker) (push) Successful in 3m51s
Adds a Docker-based execution sandbox to enhance the security and resource management of code execution. This change introduces: - New execution services for C#, C++, Java, Kotlin, and Python that utilize the sandbox. - Configuration options for enabling/disabling the sandbox and specifying Docker images for different languages. - Batch execution support for C++ to improve the efficiency of generating answer files. - Docker CLI installation in the worker's Dockerfile. The sandbox provides improved isolation and resource control during code execution, preventing potential security vulnerabilities and resource exhaustion.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using LiquidCode.Tester.Worker.Services;
|
||||
using LiquidCode.Tester.Worker.Services.Sandbox;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -32,6 +33,7 @@ public class ExecutionServiceFactoryTests
|
||||
services.AddSingleton<KotlinExecutionService>();
|
||||
services.AddSingleton<CSharpExecutionService>();
|
||||
services.AddSingleton<PythonExecutionService>();
|
||||
services.AddSingleton<IExecutionSandbox>(_ => Mock.Of<IExecutionSandbox>());
|
||||
|
||||
_serviceProvider = services.BuildServiceProvider();
|
||||
_factory = new ExecutionServiceFactory(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using LiquidCode.Tester.Worker.Services;
|
||||
using LiquidCode.Tester.Worker.Services.Sandbox;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace LiquidCode.Tester.Worker.Tests;
|
||||
@@ -12,28 +15,45 @@ namespace LiquidCode.Tester.Worker.Tests;
|
||||
public class PolygonPackageIntegrationTests
|
||||
{
|
||||
private readonly PackageParserService _parserService;
|
||||
private readonly SandboxOptions _sandboxOptions;
|
||||
|
||||
public PolygonPackageIntegrationTests()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder().Build();
|
||||
var configuration = BuildConfiguration();
|
||||
|
||||
var cppCompilation = new CppCompilationService(NullLogger<CppCompilationService>.Instance, configuration);
|
||||
var cppExecution = new CppExecutionService(NullLogger<CppExecutionService>.Instance);
|
||||
var loggerFactory = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.SetMinimumLevel(LogLevel.Debug);
|
||||
builder.AddConsole();
|
||||
});
|
||||
|
||||
_sandboxOptions = new SandboxOptions();
|
||||
configuration.GetSection("Sandbox").Bind(_sandboxOptions);
|
||||
|
||||
if (!_sandboxOptions.Enabled)
|
||||
{
|
||||
_sandboxOptions.Enabled = true;
|
||||
}
|
||||
|
||||
var cppCompilation = new CppCompilationService(loggerFactory.CreateLogger<CppCompilationService>(), configuration);
|
||||
var sandbox = new ExecutionSandbox(Options.Create(_sandboxOptions), loggerFactory.CreateLogger<ExecutionSandbox>());
|
||||
var cppExecution = new CppExecutionService(sandbox, loggerFactory.CreateLogger<CppExecutionService>());
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<IConfiguration>(configuration);
|
||||
services.AddSingleton(cppCompilation);
|
||||
services.AddSingleton<IExecutionSandbox>(sandbox);
|
||||
services.AddSingleton(cppExecution);
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
var compilationFactory = new CompilationServiceFactory(serviceProvider, NullLogger<CompilationServiceFactory>.Instance);
|
||||
var executionFactory = new ExecutionServiceFactory(serviceProvider, NullLogger<ExecutionServiceFactory>.Instance);
|
||||
var answerGenerator = new AnswerGenerationService(compilationFactory, executionFactory, NullLogger<AnswerGenerationService>.Instance);
|
||||
var polygonParser = new PolygonProblemXmlParser(NullLogger<PolygonProblemXmlParser>.Instance);
|
||||
var compilationFactory = new CompilationServiceFactory(serviceProvider, loggerFactory.CreateLogger<CompilationServiceFactory>());
|
||||
var executionFactory = new ExecutionServiceFactory(serviceProvider, loggerFactory.CreateLogger<ExecutionServiceFactory>());
|
||||
var answerGenerator = new AnswerGenerationService(compilationFactory, executionFactory, loggerFactory.CreateLogger<AnswerGenerationService>());
|
||||
var polygonParser = new PolygonProblemXmlParser(loggerFactory.CreateLogger<PolygonProblemXmlParser>());
|
||||
|
||||
_parserService = new PackageParserService(
|
||||
NullLogger<PackageParserService>.Instance,
|
||||
loggerFactory.CreateLogger<PackageParserService>(),
|
||||
polygonParser,
|
||||
answerGenerator,
|
||||
cppCompilation);
|
||||
@@ -47,6 +67,20 @@ public class PolygonPackageIntegrationTests
|
||||
return;
|
||||
}
|
||||
|
||||
var dockerExecutable = string.IsNullOrWhiteSpace(_sandboxOptions.DockerExecutable)
|
||||
? "docker"
|
||||
: _sandboxOptions.DockerExecutable;
|
||||
|
||||
if (!IsExecutableAvailable(dockerExecutable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.True(
|
||||
_sandboxOptions.Languages.TryGetValue("cpp", out var cppLanguage) &&
|
||||
!string.IsNullOrWhiteSpace(cppLanguage.ExecutionImage),
|
||||
"Sandbox configuration must specify an execution image for C++.");
|
||||
|
||||
var packageDirectory = ResolvePackageDirectory("exam-queue-17");
|
||||
Assert.True(Directory.Exists(packageDirectory));
|
||||
|
||||
@@ -70,13 +104,16 @@ public class PolygonPackageIntegrationTests
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!string.IsNullOrEmpty(package.ExtractionRoot) && Directory.Exists(package.ExtractionRoot))
|
||||
if (!KeepTemporaryExtraction())
|
||||
{
|
||||
Directory.Delete(package.ExtractionRoot, recursive: true);
|
||||
}
|
||||
else if (Directory.Exists(package.WorkingDirectory))
|
||||
{
|
||||
Directory.Delete(package.WorkingDirectory, recursive: true);
|
||||
if (!string.IsNullOrEmpty(package.ExtractionRoot) && Directory.Exists(package.ExtractionRoot))
|
||||
{
|
||||
Directory.Delete(package.ExtractionRoot, recursive: true);
|
||||
}
|
||||
else if (Directory.Exists(package.WorkingDirectory))
|
||||
{
|
||||
Directory.Delete(package.WorkingDirectory, recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,10 +180,29 @@ public class PolygonPackageIntegrationTests
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private static string ResolvePackageDirectory(string folderName)
|
||||
private static IConfiguration BuildConfiguration()
|
||||
{
|
||||
var repositoryRoot = ResolveRepositoryRoot();
|
||||
|
||||
return new ConfigurationBuilder()
|
||||
.SetBasePath(repositoryRoot)
|
||||
.AddJsonFile("src/LiquidCode.Tester.Worker/appsettings.json", optional: false)
|
||||
.AddJsonFile("src/LiquidCode.Tester.Worker/appsettings.Development.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
}
|
||||
|
||||
private static string ResolveRepositoryRoot()
|
||||
{
|
||||
var baseDirectory = AppContext.BaseDirectory;
|
||||
var root = Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "..", ".."));
|
||||
return Path.Combine(root, folderName);
|
||||
return Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "..", ".."));
|
||||
}
|
||||
|
||||
private static string ResolvePackageDirectory(string folderName)
|
||||
{
|
||||
return Path.Combine(ResolveRepositoryRoot(), folderName);
|
||||
}
|
||||
|
||||
private static bool KeepTemporaryExtraction() =>
|
||||
string.Equals(Environment.GetEnvironmentVariable("LC_KEEP_POLYGON_TEMP"), "1", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user