Using Docker Sandbox Kits
Have you ever felt the urge to just let your AI agents run in YOLO mode, but been too much of a responsible adult to do that? Or seen that new MCP Server with the tempting functionality that is just an npx call away from being used, but some security training (or actually common sense) told you that this isn’t such a great idea? Not sure what that says about my character, but I certainly have. It’s really tempting to let those agents just do their thing and pull in all the MCPs you can get to make them more poweful, but do I really want to give them access to my machine, let them run scripts on it, control my containers, have access to credentials, send data to whereever? Or let one of those claws run my business or personal life with access to all my credentials? Actually, that’s a hard no.
So what can I do? Fortunately, Docker came up with a cool new technology to safeguard against those issues and keep the AI in check: Sandboxes (and Sandbox Kits, but more on that later).
The TL;DR
First let me show you how that actually works. Safety often comes at the price of increased complexity and decreased efficiency, so I want to show you how it easy it is to use Sandboxes, improved with Kits, to set up an environment that GitHub Copilot can securely use to develop for Dynamics 365 Business Central and get input from Azure DevOps. My point is not that Copilot can do that1, but how easy it is to set up an environment dramatically ramping up the security of that approach, so I’ll only show the initialization and proof that the interactions work:
So why is this more secure? Check out the official docs linked above, but in a nutshell: You get to run your agent with MCPs and whatever else it might use in a hardened micro-VM, which means that it doesn’t have access to the filesystem (other than the workspace), environment or Docker daemon on your host. It also has networking policies, blocking access to everything you don’t allow and it has a very cool feature to inject credentials in a way so that they are not visible inside the micro-VM. If you want to learn more, check the following details. There I will be using Kits as you can also see with the --kit parameters in the video above, which are one way to customize Sandboxes (another is Templates, more on that potentially in another blog post).
The details: Why more secure - networking
To illustrate the networking part, I want to show you the first kit I am using. It enables the usage of the AL MCP Server through the ALTool provided by Microsoft:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
schemaVersion: "1"
kind: mixin
name: al
displayName: al
description: altool installation
network:
allowedDomains:
- dot.net
- ci.dot.net
- builds.dotnet.microsoft.com
- api.nuget.org
- "**.lencr.org"
- fps-alpaca.westeurope.cloudapp.azure.com
commands:
install:
- command: "curl -fsSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 8.0 && /home/agent/.dotnet/dotnet tool install --global Microsoft.Dynamics.BusinessCentral.Development.Tools"
user: "1000"
description: Install altool
I want to focus on the network part (lines 7-14): Here you can see that I need to give it access to a few dot.net and microsoft.com URLs as we first need to install dotnet 8 (more on installation in another detail chapter). Then we need nuget.org access to install the ALTool as it is provided as dotnet tool. And to interact with my Business Central instance running on COSMO Alpaca, the agent also has to reach the fps-alpaca… URL. The tricky one was the **.lencr.org as I ran into SSL errors. Those were caused by dotnet checking the validity of the Let’s Encrypt certificate also against online revocation lists, so I had to enable that as well.
Now why is this relevant: With this you make sure that an agent (potentially running in YOLO mode) isn’t up- or downloading secrets, information, scripts or whatever might be harmful to and from sources and targets that you don’t approve. It requires a bit more work to set it up, but makes it way more secure!
On a side note, Sandboxes come with three predefined policies for networking: “open”, which allows everything (in case you don’t want to use the feature); “balanced”, which blocks everything by default, but has some typical and trusted dev sites enabled (e.g. the basics you need for GitHub Copilot, Claude, etc.); and “locked down”, which just blocks everything. I have started with balanced, so I only had to take care of the special demands of my workload.
The details: Why more secure - credentials
For the second feature I want to highlight, credential injection, here is my second kit, which enables access to the Azure DevOps MCP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
schemaVersion: "1"
kind: mixin
name: az-devops
displayName: az-devops
description: Azure DevOps
network:
allowedDomains:
- "**.dev.azure.com"
- "**.visualstudio.com"
serviceDomains:
4psnl.visualstudio.com: az-devops
dev.azure.com: az-devops
mcp.dev.azure.com: az-devops
serviceAuth:
az-devops:
headerName: Authorization
valueFormat: "Basic %s"
credentials:
sources:
az-devops:
env:
- AZ_DEVOPS_PAT
environment:
proxyManaged:
- AZ_DEVOPS_PAT
commands:
install:
- command: "mkdir /usr/local/share/npm-global/lib"
user: "1000"
description: Create missing folder
First, you see the by now familiar network part with allowedDomains (lines 7-10), in this case allowing access to the “new” dev.azure.com and old visualstudio.com domains used for Azure DevOps. Now for the part required for credential injection:
- First, we define
serviceDomains(lines 11-14). Those are domains where we want credentials to be injected. So whenever a call is made to 4psnl.visualstudio.com, dev.azure.com or mcp.dev.azure.com, theaz-devopsconfiguration is used. - Second, we define the
serviceAuth(lines 15-18), referenced in theserviceDomains. Here you can see the definition of the name of the header and how the value is to be formatted. The%sis replaced by a secret we define later on the host. - Third, we need to configure where the credential is coming from in the
credentials(lines 20-24), in this case pointing to anenvvariable - As last step, we define the
environment(lines 26-28), letting it know that it should come from aproxyManagedvariable calledAZ_DEVOPS_PAT
Now where is the actual value coming from? I ran this little one-liner on the host2:
1
echo "$([System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("tfenster@4psbau.de:ep75nho5o3p727ezkwy4jsyjr4g6qkepyaqalwivmgcgrdhm5o5q")))" | sbx secret set -g az-devops
This defines the authentication token I want to use for the service, which will be injected into all traffic going to the domains configured above. That enables me to do something like this in my MCP configuration inside of the Sandbox:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"mcpServers": {
"az-devops": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@azure-devops/mcp",
"4psnl",
"--authentication",
"pat"
],
"env": {
"PERSONAL_ACCESS_TOKEN": "don't think so"
}
}
}
}
You can see in lines 10 and 11 that I am using a token (PAT = personal access token) for authentication, but I am not atually providing the token as you see in line 14. Instead, the Sandbox networking stack injects the token correctly, so I never need to share it with the MCP and it never is visible from within the Sandbox. Really nice, right?
The details: Another aspect of Sandbox Kits - installs
The last feature in Sandbox Kits I want to mention is the install section below commands, which covers things that run once at creation. There are also startup commands, which run on every startup, but I haven’t used those. The installs that I have used are for the MCPs and on the AL MCP, it’s a bit more complicated, as we first need dotnet and then install the ALTool via dotnet tool install:
1
2
3
4
5
commands:
install:
- command: "curl -fsSL https://dot.net/v1/dotnet-install.sh | bash -s -- --channel 8.0 && /home/agent/.dotnet/dotnet tool install --global Microsoft.Dynamics.BusinessCentral.Development.Tools"
user: "1000"
description: Install altool
As you can see, we can also set the user (ID) and description.
On the Azure DevOps MCP, the installation is actually done through the npx command in .mcp.json shown above, but that failed because of a missing folder. I could easily mitigate that with an install as well:
1
2
3
4
5
commands:
install:
- command: "mkdir /usr/local/share/npm-global/lib"
user: "1000"
description: Create missing folder
As you can see, this gives you all the flexibility you might need for setting up your toolchain and it also makes sure you get a very stable, reproducable environment.
The details: Claws…
An interesting aspect of the security model of Sandboxes, not connected to Kits, is that they run in micro-VMs, as explained in the beginning. One of the key reasons for that instead of running inside of a container is that some agents want to spin up containers, so Sandboxes need that ability to do that. But if they had access to the Docker daemon on the host, that would also give them access to other containers, again something that would create a security risk. Therefore, every Sandbox gets its own daemon inside of the micro-VM, not exposing any of the other containers potentially running.
That enables a very secure environment also for personal assistants like nanoclaw. Therefore, it was a great fit and as you can see here, that triggered a collaboration. I found it very interesting to see how a setup mostly aimed at developer security and productivity also is such a nice solution for a personal assistant, which is why I also wanted to mention that here.
Webmentions:
No webmentions were found.
No reposts were found.
.png)