4 Nesting virtual machines for fun and profit!
6 Things to worry about are marked below in *********************
11 We have a need for an easy-to-use environment in a security training
12 lab setup. The training is designed to educate engineers about various
13 common security problems and (more importantly) how to fix them. Some
14 online training courses from Azeria labs [1] are the initial
15 exercises, but the work is expected to expand into other areas too.
17 The goal is simple (make it easy!), but the setup needed for this is
18 less so. We want to have have tools to provision the following:
20 1. a ready-to-use VM environment that engineers can deploy on their
21 existing x86-based computers (Windows/Linux/Mac), including all the
22 toolchain etc. needed for the work (called "toolchain VM" from
25 2. a further emulated machine where Arm programs can be run, tested
26 and debugged (called "runtime VM" from here on)
28 For simplicity of deployment, it makes sense to have the runtime VM
29 hosted *within* the toolchain VM, with automatic configuration. That
30 way, the engineers using the training labs will have one logical piece
31 to worry about: they will not need to worry about the details of
32 setting up and configuring emulation, for example.
34 More (obvious?) terminology: we'll call the engineer's machine the
37 [1] https://azeria-labs.com/writing-arm-shellcode/
42 Vagrant [2] seems to be a good fit for deploying and controlling the
43 toolchain VM. It can use a wide range of VM and container technologies
44 as a backend, but the best supported is Virtualbox [3], and that is
45 cross-platform - it can run on all three of our desired
46 platforms. Once Virtualbox is installed, Vagrant deals with all the
47 details of downloading a "box" (a configured VM image) and starting
48 it. It uses a simple "Vagrantfile" to define how a box is defined,
49 configured and provisioned. Out of the box, Vagrant sets up a shared
50 directory from the host to the box (useful for data sharing), and also
51 ssh access from the host to the box (useful for running command line
54 For our runtime VM, we will use qemu [4]. It's a powerful emulator
55 platform that supports all manner of different architectures. It can
56 run individual binaries in emulation ("qemu-user") and this uses
57 libraries etc. as normal for the emulated platform and translates
58 instructions and system calls as needed. But for our purposes it looks
59 better to run in "qemu-system" mode; this emulates a complete machine,
60 then runs a kernel and userland on top of that. While this takes a
61 little more setup initially, it's often more reliable (qemu-user is
62 known to struggle with more complicated binaries using threads, for
63 example). We'll therefore also need to provide an Arm VM image of the
64 runtime VM for qemu to use. This is a little more involved than just
65 running Vagrant, but not too difficult.
67 [2] https://www.vagrantup.com/
68 [3] https://www.virtualbox.org/
69 [4] https://www.qemu.org/
74 As Vagrant will share a directory between the host and the toolchain
75 VM, this will allow engineers to use their normal native editor and
76 other tools on their host machine, but also to use the
77 cross-toolchains in the toolchain VM to compile them for the Arm
80 We will *also* try to share that same directory with the runtime
81 VM. That will give us a simple view of the whole project in terms of
82 editing, building and running code.
84 Deployment should be as simple as possible. It might be time-consuming
85 on first run due to the need to download two VM images, but that's OK
86 so long as they're not *too* big. The Vagrantfile for the toolchain VM
87 will be the only piece needed by the end user, and it should be
88 responsible for doing everything else from there down:
90 1. Create and run a Virtualbox VM, using an existing stable Ubuntu
91 image. This is trivial out-of-the box Vagrant usage.
93 2. Configure that VM for data sharing and access to the toolchain
94 VM. Again, this is common Vagrant usage.
96 3. Inside the toolchain VM, apply any updates that might be needed
97 then install the extra packages we need (qemu and
100 4. Download the runtime VM image from our own source (TBD, probably
101 Sharepoint somewhere?)
103 5. Start the runtime VM image and *also* set up data sharing and
104 access to it. This will need a little bit more configuration in
105 Vagrant, but should be simple enough.
107 6. Download the desired training materials - source code, docs, etc.
109 7. Tell the user that they're good to go. Point to the start of the
112 ************************************
113 Step #5 is (by far) the hardest piece here. Either we need to
114 preconfigure the runtime VM in certain ways, *or* we'll need to
115 download a generic image and modify/configure it in the field before
116 we start it. The first option is easier to achieve for now, and much
117 faster to deploy - we don't end up installing packages at runtime onto
118 an emulated system. BUT: it also means that we'll have to maintain
119 that runtime VM image separately rather than using a generic image. If
120 we deploy multiple different sets of training material using this
121 setup, we could end up having to maintain multiple slightly-different
122 versions of the runtime VM.
123 ************************************
125 Technical details and (possible) troubles
126 =========================================
131 As it starts the toolchain VM, Vagrant generates a throwaway SSH
132 key. It stores the private key in
134 .vagrant/machines/default/virtualbox/private_key
136 and injects the public key into the toolchain VM at startup. It is
137 stored under /home/vagrant/.ssh/authorized_keys, as you'd normally
138 expect for ssh key access. Vagrant also sets up port forwarding
139 between port 2222 of the host machine and port 22 on the toolchain
140 VM. "vagrant ssh" is then a simple wrapper around ssh to use the right
141 key, username and IP address etc.
143 We can borrow some of the same setup for the runtime VM. When starting
144 it from inside the toolchain VM, we can copy the SSH public key into
145 the shared data directory /vagrant. We can set up port forwarding
146 within the toolchain VM too, and expose that in the
147 Vagrantfile. Arbitrarily, let's use port 2223 for the ssh connection
148 to the runtime VM. That will give us the following port usage:
150 HOST TOOLCHAIN VM RUNTIME VM
154 It's worth adding a single wrapper script to make the ssh calls more
155 consistent, rather than "vagrant ssh" for the toolchain VM and
156 something different / more complicated for the runtime VM.
161 By default, Vagrant shares the "project" directory (i.e. the directory
162 where the Vagrantfile lives) into the toolchain VM as /vagrant
163 [5]. This is a really useful feature. We can extend this feature
164 ourselves, sharing the same directory from the toolchain VM to the
165 runtime VM. To do that, we use qemu's built-in support for a "Plan 9"
166 filesystem export (the "-virtfs" command line option). Then we mount
167 that filesystem inside the runtime VM on /vagrant too.
169 This will give the user a consistent easy place to share their data,
170 e.g. when compiling and running test programs. We can also use it for
171 our own internal purposes, e.g. for sharing the SSH authorized_keys
174 We will also try to download and store the runtime VM image and
175 associated files here. That will save us having to make space for them
176 inside the small toolchain VM.
178 ************************************
179 I'm *not* sure how well this will work in performance terms - how
180 fast is the virtualbox shared filesystem? Testing needed...
181 ************************************
183 [5] https://www.vagrantup.com/intro/getting-started/synced_folders.html
185 Setup of the toolchain VM
186 -------------------------
188 This *should* be trivial, given the idea behind Vagrant. It's just a
189 case of generating a Vagrantfile with some config in it. Put that in a
190 git repo and tell the user:
192 * install vagrant, virtualbox and git for their OS
193 * git pull <foo> lab.git
197 ************************************
198 However, things did not work quite so smoothly during development. For
199 the directory-sharing feature that we're expecting to use, Virtualbox
200 depends on its guest (our toolchain VM) having a guest utilities
201 package installed ("virtualbox-guest-utils" on Ubuntu"). Initial
202 testing with a Debian image did not work. Virtualbox started up (with
203 warnings about a version mismatch). Annoyingly, Vagrant apparently
204 noticed the failure and fell back to using a one-time rsync at VM
205 startup. This gave the appearance of working sharing, but did not stay
206 in sync. I've added extra config to the Vagrantfile to force *only*
207 Virtualbox-style sharing.
209 Testing with some other Ubuntu boxes also failed - I tried a few
210 variants of the (very new!) 20.04 release and they exhibited a range
211 of problems giving unreliable startup. I've switched to 18.04 (aka
212 "ubuntu/bionic64") and so far that has worked flawlessly.
213 ************************************