Skip to content

Instantly share code, notes, and snippets.

@lessmost
Last active December 18, 2020 09:54
Show Gist options
  • Save lessmost/0d074ea2d43b03c1a3ed471573622df5 to your computer and use it in GitHub Desktop.
Save lessmost/0d074ea2d43b03c1a3ed471573622df5 to your computer and use it in GitHub Desktop.
How to create a VirtualBox machine with encrypted storage with Vagrant (2.2.7)
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Forked from https://gist.github.com/gabrielelana/9189eab5df963deba30f
# Works with Vagrant 2.2.7 (trigger)
# After the first `vagrant up` stop the VM and execute the following steps
# Take the identifier of the storage you want to encrypt
# > HDD_UUID=`VBoxManage showvminfo <VM_NAME> | grep 'SATA.*UUID' | sed 's/^.*UUID: \(.*\))/\1/'`
# Store your usernname (whitespaces are not allowed) in a variable
# > USERNAME="<YOUR_USER_NAME_WITHOUT_WHITESPACES>"
# Encrypt the storage, enter the password when asked
# > VBoxManage encryptmedium $HDD_UUID --newpassword - --newpasswordid $USERNAME --cipher "AES-XTS256-PLAIN64"
# Store the username in a file named .password_id
# > echo $USERNAME > .password_id
# Now, the next time you start the VM you'll be asked for the same password
# .password
PASSWORD_PATH = ".password"
# .password_id
PASSWORD_ID_PATH = ".password_id"
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "archlinux/archlinux"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
# config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
# vb.memory = "1024"
# end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
config.trigger.before :up do |trigger|
trigger.info = "Log: Running before up trigger..."
trigger.ruby do |env,machine|
if File.exists?(PASSWORD_ID_PATH)
unless File.exists?(PASSWORD_PATH)
password_id = File.read(PASSWORD_ID_PATH).strip
print "The VM is encrypted, please enter the password\n#{password_id}: "
password = STDIN.noecho(&:gets).strip
File.write(PASSWORD_PATH, password)
puts ""
end
end
end
end
config.trigger.after :up do |trigger|
trigger.info = "Log: Running after up trigger..."
trigger.ruby do |env,machine|
File.delete(PASSWORD_PATH) if File.exists?(PASSWORD_PATH)
end
end
config.trigger.after :destroy do |trigger|
trigger.info = "Log: Running after destroy trigger..."
trigger.ruby do |env,machine|
File.delete(PASSWORD_ID_PATH) if File.exists?(PASSWORD_ID_PATH)
end
end
config.vm.provider :virtualbox do |vb|
# vb.name = "secure"
vb.gui = false
if File.exists?(PASSWORD_ID_PATH)
password_id = File.read(PASSWORD_ID_PATH).strip
vb.customize "post-boot", ["controlvm", :id, "addencpassword", password_id, PASSWORD_PATH, "--removeonsuspend", "yes"]
end
end
end
@dgambo
Copy link

dgambo commented Dec 18, 2020

Hello ! Thanks for the very helpful gist.
I try to do kind of similar approach to you, except that I want to run the decryption command ONLY when the password entered is not blank. Also, I am not having the PASSWORD_ID file, the encryption is done manually through Virtual Box and the password id has same value as the VM name.

Do you know if there is a way to run the decryption command ONLY if the password entered at prompt is not empty ? Maybe having some condition which is triggered at "post-boot" time ? I did not find any useful informations on this topic on Internet.

I tried to use condition as you did (Except that I am evaluating PASSWORD_PATH and not PASSWORD_ID_PATH), but it seems that Vagrantfile processes the condition before I am prompted to enter the password. (and the customisation command is run even if the password entered later on is empty)

Otherwise, as a workaround, I can try maybe to define some variable at startup that will launch either or not the decryption.

e.g. - vagrant up encrypted --> Will prompt for password and will perform decryption 
vagrant up --> Will not prompt for password and not perform decryption

Thank you in advance and Merry Christmas,
David.

@dgambo
Copy link

dgambo commented Dec 18, 2020

So yes, indeed, I think that the only way to do so is to pass a variable to Vagrantfile.

if ENCRYPTION.eql?("--encryption")) print "Please enter the VM's passphrase : " password = STDIN.noecho(&:gets).strip File.write(PASSWORD_PATH, password) puts "" end

if ENCRYPTION.eql?("--encryption")
  vb.customize "post-boot",  [ "controlvm", :id, "addencpassword", vb.name, PASSWORD_PATH, "--removeonsuspend", "yes"] 
end

so basically when you do something like
vagrant --encryption reload

It will prompt you for password, then Vagrant will call VB's decryption API.

And if you do just vagrant up then it won't.

I hope this can help someone that needs to implement encryption mechanism without having to define one .password_id file.

David

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment