Docker Support for Isolator

Introduction

Some of you may remember - and hopefully have even tried! - my Isolator project. Isolator is a framework for running isolated code for .NET. What this means is that possibly unsafe code can be run in a secure, isolated, way.

It offers some quite different isolation strategies:

  • Process isolation: the code to execute runs in another process, possibly under a different identity (on Windows only)
  • Assembly isolation: uses an assembly load context to isolate the code execution, which is then unloaded
  • Distributed isolation: the code to execute is sent to possibly another server for execution
  • Scanned: the code to execute is first checked for problematic code using my ReferencesScanner project

Now, I added support for Docker isolation! What this means is, if you have Docker running on your machine, you can spawn an image that will just be used for running your code, and after that will be gone. This provides another very restricted level of isolation.

For Docker API calls, I used Docker.DotNet, a great library for interacting with Docker from the .NET Foundation that even has a small contribution by myself. Another option would be to use TestContainers, something for later.

I strongly advise you to have a look at my first post before proceeding, so that you have the concepts present.

Using Docker Isolation

There is a new class, DockerIsolatorHost, that also implements IIsolationHost.

In order to use the new mechanism, you need to have Docker running, either locally or on a remote server that can be accessed:

using var host = new DockerIsolationHost(); //we can pass URL and credentials if we want to
var plugin = new HelloWorldPlugin(); //see example from my original post
var context = new IsolationContext
{
    Properties = new Dictionary<string, object>
    {
        ["Greeting"] = "Hello, World!"
    },
    Arguments = ["This", "is", "a", "test"]
}; 
var result = await host.ExecutePluginAsync(plugin, context); 

And that's it, everything works in exactly the same way, including:

  • The result from the plugin is returned with the right type
  • The context's properties are also updated, which means you can use them for returning values
  • The standard output and error for the execution are captured and returned

By default, an image for .NET 9 is used (mcr.microsoft.com/dotnet/runtime/9.0), but we can specify a different one by setting the NetVersion property (only major and minor versions, make sure the version exists first!):

host.NetVersion = new Version(10, 0);

Also, it is required to add a reference to Isolator.Docker Nuget package to your project, this is the one that contains the bootstrap app (not to be used directly), you don't need the Isolator package as it is imported automatically. Most important: only .NET up to version 9 packages can be used for the plugin, as that's the version that Isolator has been built against! Something to revisit soon.

Behind the scene, what it does is:

  • Creates a container from an official Microsoft .NET image (from mcr.microsoft.com/dotnet/runtime)
  • Binds the folder containing the assembly of the plugin type to the container working directory
  • Sets as the execution target a bootstrap app (from Isolator.Docker) which takes as its parameters the plugin type to execute plus the context (I'd like to get rid of this additional assembly but for now we will have to live with it)
  • Starts the container and extracts the results from its output
  • Removes the container when finished

Mind you, this is transparent to us, all we need is the code I've shown. I may be adding some more options to this one in the future, to control some aspects of Docker.

Conclusion

As always, I hope you find this useful. There may still exist some strategies to implement, feel free to suggest or ask anything by creating an issue on the GitHub repo here.

GitHub: https://github.com/rjperes/Isolator

Nuget: https://nuget.org/packages/Isolator.Docker

Comments

Popular posts from this blog

Modern Mapping with EF Core

C# Magical Syntax

.NET 10 Validation