Since Spectre and Meltdown (2 infamous side channel attack vectors on CPUs) have become public I thought about better, more secure ways to browse the web. When I read that a commercial vendor for operating systems created a solution where a browser is started in a disposable sandbox that gets discarded when you exit the browser session I thought of ways to implement this feature myself.
Since I’m a great fan of both Virtualbox and Vagrant I decided to use the combination of the two to get this done. My host runs Ubuntu 22.04 LTS, and I’m using Vagrant 2.2.19 (the one shipping with the distribution, it’s not the latest version!) as well as Virtualbox 6.1.40. Whilst the solution presented in this article provides a more secure (notice how I didn’t claim this to be secure ;) ) approach to web browsing it doesn’t keep the host up to date. Security updates for the host O/S and hypervisor (read: Virtualbox) are crucial, too.
Please be super-careful when thinking of implementing a strategy where provisioners are run always, it can and potentially will break your system! For most use cases provisioning a VM each time it starts is not what you want.
Building a “browser” VM
I started off by creating a small “browser” VM with a minimal GUI and a web browser – nothing else – and registered this system as a vagrant box. This is the first step towards my solution: being able to create/tear down the sandbox. Not perfect, and there are more secure ways, but I’m fine with my approach.
The one thing that’s necessary though is updating the VM, ideally performed automatically, at each start. Vagrant provisioners can help with that.
Defining one or more provisioners in the Vagrantfile is a great way to initially configure a VM when it is created for the first time and works really well. Provisioners thankfully do NOT run with each subsequent start of the VM. If they were run each time it would probably be a disaster for all of my other Vagrant VMs. For my sandbox browser VM though I want all packages to be updated automatically.
Switching from on-demand provisioning to automatic provisioning
As I said, VMs are provisioned once by default, subsequent starts won’t run the provisioners as you can see in the output:
$ vagrant up [output of vagrant bringing my VM up skipped] ==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision` ==> default: flag to force provisioning. Provisioners marked to run always will still run.
The section detailing provisioners in my Vagrantfile is super simple because it has to run in Linux and Windows and I’m too lazy to install Ansible on my Windows box. The above output was caused by the following directive:
Vagrant.configure("2") do |config| # ... more directives ... config.vm.provision "shell", inline: "sudo apt-get update --error-on=any && sudo apt-get dist-upgrade -y" # ... even more directives ...
Looking at the command you may have guessed that this is a Debian-based VM, and I’m neither using Flatpack nor Snaps. All packages in this environment are DEBs. That’s easier to maintain for me.
To change the provision section to always run, simply tell it to:
Vagrant.configure("2") do |config| # ... more directives ... config.vm.provision "shell", inline: "sudo apt-get update --error-on=any && sudo apt-get dist-upgrade -y", run: "always"
Next time the vagrant VM starts, the provisioner marked as “run: always” will be triggered, even though the VM wasn’t created from scratch:
$ vagrant up [output of vagrant bringing my VM up skipped once more] ==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision` ==> default: flag to force provisioning. Provisioners marked to run always will still run. ==> default: Running provisioner: shell... default: Running: inline script [output of apt-get omitted for brevity]
There you go! I could have achieved the same by telling vagrant to provision the VM using the --provision
flag but I’m sure I would have forgotten that half the time.
Anyone using Ansible can benefit from running provisioners always, too:
Vagrant.configure("2") do |config| # ... more directives ... config.vm.provision "ansible", run: "always" do |ansible| ansible.playbook = "/path/to/ansible/playbook.yaml" end
Next time the VM is started by vagrant the Ansible playbook will be executed.
Summary
Vagrant can be instructed to run provisioners always if the use case merits it. For the most part it’s not advisable to run provisioners each time the VM comes up as it might well mess with the installation already present.