Sandboxed Development Environments

Published:

As supply chain attacks and typosquatting are becoming more prevalent, it is important to sandbox your development environment to minimize the risks. The technology has finally progressed to the point that remote development tools let us create sandboxed environments. This contains the access a malicious programs have if such packages get installed.

In this article, I describe a simple way to create a containerized development environment with Vagrant.

Vagrant

Vagrant manages the virtual machines for you, it has good support for VirtualBox machines. Vagrant should work in all platforms, but I find it easiest in Linux.

To get started, start the installation process:

  • Enable virtualization technology in BIOS. It should be under the CPU settings, the names varies e.g. AMD-V, or VT-x
  • Install VirtualBox
  • Install Vagrant

Vagrantfile

Each project you have needs Vagrantfile which defines the environment your program runs. The default one can be generated using command vagrant init. The basic configuration will look like this

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
  config.ssh.port = 2222
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.synced_folder ".", "/home/vagrant/app"

  config.vm.provider "virtualbox" do |vb|
    vb.memory = 8192
    vb.cpus = 1
  end

  config.vm.provision "shell", path: "Vagrant.provision.sh"

It is important that you allocate enough memory as the remote IDE will run inside the container. The CPU count does not matter but it should not exceed the number of cores you have in the host.

Provisioning

Then you'll have another file named Vagrant.provision.sh which contains the commands you need when the machine is created the first time. An example to install a Node.js would look like this

#!/usr/bin/env bash

echo "Installing Node.js version 16..."
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
apt install nodejs -y

The script is a normal bash script, to make the script idempotent you may want to check if the command exists

if ! [ -x "$(`command -v node`)"]; then
  curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
  apt install nodejs -y
fi

Docker

The Docker containers can be run inside the virtual machine:

config.vm.network "forwarded_port", guest: 5432, host: 5432
config.vm.provision "docker" do |d|
    d.run "postgres:14",
      args: "-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432"
end

Alternatively, you can install Docker Compose in the provisioning script and run that. Running the docker containers directly is simpler if there aren't many of them.

JetBrains Gateway

After starting the virtual machine using vagrant up, you can use JetBrains Gateway to install the remote IDE using SSH. Notice that all plugins need to be installed after the remote IDE is installed.

The following connection credentials are to be used:

ConfigurationValue
Usernamevagrant
Host127.0.0.1
Port2222
Private key/home/YOUR_USERNAME/.vagrant.d/insecure_private_key

There are some environments where Remote IDEs are redundant as the IDE interpreters can use the same modules as the virtual machine. In those cases, you can use the IDE natively but run the program itself in virtual machine.

Visual Studio Code Remote SSH

Install the Remote Development extension and connect to the machine using the same configurations described above.

Provisioning Scripts for Common Environments

Node.js

Do not install from Snap, those packages do not work well with postinstall scripts. Instead, install them from NodeSource or use Node Version Manager.

#!/usr/bin/env bash

if ! [ -x "$(`command -v node`)"]; then
  echo "Installing Node.js version 16..."
  curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
  apt install nodejs -y
  npm install -g pnpm
fi

cd app
pnpm install