Move arm64 image to new location
[training-lab.git] / doc / notes.txt
1 Training labs VMs
2 =================
3
4 Nesting virtual machines for fun and profit!
5
6 Things to worry about are marked below in *********************
7
8 The goal
9 ========
10
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.
16
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:
19
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
23     here on)
24
25  2. a further emulated machine where Arm programs can be run, tested
26     and debugged (called "runtime VM" from here on)
27
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.
33
34 More (obvious?) terminology: we'll call the engineer's machine the
35 "host".
36
37 [1] https://azeria-labs.com/writing-arm-shellcode/
38
39 Planning and design
40 ===================
41
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
52 tools).
53
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.
66
67 [2] https://www.vagrantup.com/
68 [3] https://www.virtualbox.org/
69 [4] https://www.qemu.org/
70
71 Expected workflow
72 =================
73
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
78 target.
79
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. 
83
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:
89
90  1. Create and run a Virtualbox VM, using an existing stable Ubuntu
91     image. This is trivial out-of-the box Vagrant usage.
92
93  2. Configure that VM for data sharing and access to the toolchain
94     VM. Again, this is common Vagrant usage.
95
96  3. Inside the toolchain VM, apply any updates that might be needed
97     then install the extra packages we need (qemu and
98     cross-toolchains)
99
100  4. Download the runtime VM image from our own source (TBD, probably
101     Sharepoint somewhere?)
102
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.
106
107  6. Download the desired training materials - source code, docs, etc.
108
109  7. Tell the user that they're good to go. Point to the start of the
110     training material
111
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 ************************************
124
125 Technical details and (possible) troubles
126 =========================================
127
128 SSH access
129 ----------
130
131 As it starts the toolchain VM, Vagrant generates a throwaway SSH
132 key. It stores the private key in
133
134   .vagrant/machines/default/virtualbox/private_key
135
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.
142
143 To enable consistent-ish access to the toolchain VM and the runtime
144 VM, I first tried to set up extra forwarding at the Virtualbox layer:
145
146   HOST     TOOLCHAIN VM     RUNTIME VM
147   2222 <-> 22               ---
148   2223 <-> 2222         <-> 22 
149
150 ... but that did not work - connections from to port 2223 on the host
151 would fail with very little diagnostics available. In the end, I went
152 with using the toolchain VM as a proxy or "jump host". This involves
153 adding some extra ssh configuration. As I was already thinking about
154 adding an extra wrapper script to help with consistent access
155 *anyway*, this is not too difficult to set up.
156
157 To make authentication work in both VMs, we use the same SSH
158 keypair. When starting the runtime VM inside the toolchain VM, we
159 simply copy the SSH public key into the shared data directory
160 /vagrant/runtime. The runtime VM is configured to use that location
161 for the authorized_keys file - see below. The local ssh config we're
162 using specifies the same private key for both. Easy!
163
164 The provided script "vm_ssh" does the right thing on Linux (and
165 MacOS).
166
167 ************************************
168 We may need a tweaked equivalent "vm_ssh.bat" for Windows to use the
169 right style of file name, let's see
170 ************************************
171
172 Data access
173 -----------
174
175 By default, Vagrant shares the "project" directory (i.e. the directory
176 where the Vagrantfile lives) into the toolchain VM as /vagrant
177 [5]. This is a really useful feature. We can extend this feature
178 ourselves, sharing the same directory from the toolchain VM to the
179 runtime VM. To do that, we use qemu's built-in support for a "Plan 9"
180 filesystem export (the "-virtfs" command line option). Then we mount
181 that filesystem inside the runtime VM on /vagrant too.
182
183 This will give the user a consistent easy place to share their data,
184 e.g. when compiling and running test programs. We can also use it for
185 our own internal purposes, e.g. for sharing the SSH authorized_keys
186 file.
187
188 We will also try to download and store the runtime VM image and
189 associated files here. That will save us having to make space for them
190 inside the small toolchain VM.
191
192 ************************************
193 I'm *not* sure how well this will work in performance terms - how
194 fast is the virtualbox shared filesystem? Testing needed...
195 ************************************
196
197 [5] https://www.vagrantup.com/intro/getting-started/synced_folders.html
198
199 Setup of the toolchain VM
200 -------------------------
201
202 This *should* be trivial, given the idea behind Vagrant. It's just a
203 case of generating a Vagrantfile with some config in it. Put that in a
204 git repo and tell the user:
205
206  * install vagrant, virtualbox and git for their OS
207  * git pull <foo> lab.git
208  * cd lab.git
209  * vagrant up
210
211 ************************************
212 However, things did not work quite so smoothly during development. For
213 the directory-sharing feature that we're expecting to use, Virtualbox
214 depends on its guest (our toolchain VM) having a guest utilities
215 package installed ("virtualbox-guest-utils" on Ubuntu"). Initial
216 testing with a Debian image did not work. Virtualbox started up (with
217 warnings about a version mismatch). Annoyingly, Vagrant apparently
218 noticed the failure and fell back to using a one-time rsync at VM
219 startup. This gave the appearance of working sharing, but did not stay
220 in sync. I've added extra config to the Vagrantfile to force *only*
221 Virtualbox-style sharing.
222
223 Testing with some other Ubuntu boxes also failed - I tried a few
224 variants of the (very new!) 20.04 release and they exhibited a range
225 of problems giving unreliable startup. I've switched to 18.04 (aka
226 "ubuntu/bionic64") and (so far!) that has worked flawlessly.
227 ************************************
228
229 Setup of the runtime VMs
230 ------------------------
231
232 This is a little more involved, as we don't have easy-to-use tools
233 like vagrant here. It's easy enough to write scripts to drive qemu
234 virtual machines, but the images themselves need to be created or
235 borrowed from elsewhere.
236
237 I've simply created a Debian 10.3 (Buster) arm64 image for now, using
238 qemu and kvm on an arm64 host. It's set up to boot via UEFI using
239 qemu's pflash interface. Inside the machine I've made the following
240 changes:
241
242  * Set up to EFI boot via the removable media path (in case EFI boot
243    variables get lost or corrupted)
244
245  * Dropped the grub boot delay down to 0s
246
247  * Added an fstab entry to mount the /vagrant filesystem from the host
248    using the plan9 fs. This does *not* always work automatically due
249    to startup timing. Made it "noauto":
250
251    host0           /vagrant    9p      version=9p2000.L,nofail,noauto   0 0
252
253    and added an extra @reboot cron job for it - see later.
254
255  * Added a "vagrant" user
256
257  * Symlink that user's .ssh/authorized_keys to
258    /vagrant/runtime/vagrant-pub-key, to allow passwordless ssh login
259
260  * Added passwordless sudo access for the vagrant user
261
262  * Add a startup script to mount /vagrant and do other startup stuff,
263    then run any provisioning if needed
264
265  * Add an @reboot cron job to run that script
266    @reboot    /usr/local/bin/runtime_vm_startup
267
268 I've now created a simple armhf (32-bit Arm) image with a similar
269 setup.
270
271 ************************************
272 It's also possible to use a different image here, but we'll need to
273 find and test with them.
274 ************************************