File: README.md

package info (click to toggle)
qemu-web-desktop 23.02.16%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 2,008 kB
  • sloc: perl: 1,303; sh: 98; makefile: 72
file content (742 lines) | stat: -rw-r--r-- 40,022 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
# qemu-web-desktop
<br>
<img src="src/html/desktop/images/darts_logo.png" height=200>
<br>

Data Analysis Remote Treatment Service (DARTS) is a remote desktop service that launches virtual machines in the cloud, and displays them in your browser. These machines can be used for e.g. scientific data treatment.

##### Table of Contents
- [What is provided by this service](#what-is-provided-by-this-service)
- [Installation](#installation)
- [Configuration](#customize-to-your-needs)
- [Managing virtual machines](#managing-virtual-machines)
- [Usage](#usage-as-a-web-service)
- [Installation with GPU](#installation-gpu-pass-through)
- [How it works](#how-it-works)

## What is provided by this service

The service allows authorized users to launch a remote virtual machine, and display it in a browser window. No additional software installation is needed on the client side. This project has been developed on a Debian-class system, and is thus suited for it.

Once installed (see below), connect to:
- http://localhost/qemu-web-desktop

Basically a user can enter some login/password or email, then specify what resources are needed (cpu, memory). It is also possible to select the type of machine (system), and if the machine is accessed only once, or can be accessed many times. After accepting the Terms and Conditions, the user can click on "Create".

<br>
<img src="src/html/desktop/images/machine-specs.png">
<br>

The user credentials can be tested against IMAP, SMTP, and LDAP. In this case, a user ID (login name) and password are required. Authentication can also be achieved using an email sent with connection information.

When authentication is successful, a virtual machine is launched and can be displayed in the browser window. 

<br>
<img src="src/html/desktop/images/create.png">
<br>

Just click the link, and you will access the virtual machine remote desktop. Use full screen, adapt screen resolution and keyboard layout, and you'll be good to go !

<br>
<img src="src/html/desktop/images/display.png">
<br>

Features
--------
- Supports authentication using SMTP, IMAP, LDAP and email. See "customize" section below.
- Checks the server load to avoid DoS. See "customize" section below.
- The browser can be closed and re-connected later while the session is still running. The connection information can also be shared to allow multiple users to collaborate. It is important to remember the session URL. :warning: Beware: all users will have mouse/keyboard control, so that friendly collaboration rules must be set in place.
- No need to install anything on the client side.
- The rendering of the web service is responsive design. It adapts to the browser window size.
- Can monitor running sessions.
- Can mount host volumes.
- Automatically clean-up sessions that have passed their life-time.
- Can optionally assign physical GPU to sessions (see below).
- Can optionally insert and execute scripts in the virtual machine boot process.
- Can optionally distribute the sessions work-load over a farm of servers.


--------------------------------------------------------------------------------

## Installation

### Installation in an ideal world (automatic)

This service has been packaged for Debian. Get the [qemu-web-desktop.deb](https://packages.debian.org/sid/qemu-web-desktop) or type (from Debian 12):
```
sudo apt install qemu-web-desktop
```

Otherwise, install required packages. On a Debian-class system:
```bash
sudo apt install apache2 libapache2-mod-perl2 libapache2-mpm-itk
sudo apt install novnc websockify confget
sudo apt install qemu-kvm bridge-utils qemu iptables dnsmasq libguestfs-tools
sudo apt install libcgi-pm-perl liblist-moreutils-perl libsys-cpu-perl libsys-cpuload-perl libsys-meminfo-perl libnet-dns-perl libproc-background-perl  libproc-processtable-perl libemail-valid-perl libnet-smtps-perl libmail-imapclient-perl libnet-ldap-perl libemail-valid-perl libjson-perl libwww-perl libtext-qrcode-perl
```

:warning: if you just cloned the repository, make sure you collect the src/html/desktop/machines directory with [LFS](https://git-lfs.github.com/). Install it with `sudo apt install git-lfs` then, from the repo, `git-lfs install; git lfs pull`.

Then simply go in the `src` directory and type:
```bash
cd src
sudo make install
```

On a Debian class system, this should be enough.
In case the `make` command fails, you may proceed with a manual installation, as follows.

### Installation step by step (manual)

Make sure permissions are set:
```bash
sudo adduser --system --home /var/lib/qemu-web-desktop --force-badname _qemu-web-desktop
sudo chmod 755 /etc/qemu-ifup
```
- copy the `html/machines` and `html/snapshots` directories into `/var/lib/qemu-web-desktop`.
- copy the html directory (except `machines` and `snapshots`) content into e.g. `/usr/share/qemu-web-desktop/html`. You should now have a 'desktop' item there.
- copy the cgi-bin directory content into e.g. `/usr/lib/cgi-bin` (Apache2 / Debian).
- copy the `apache.conf` file as e.g. `/etc/apache2/conf-available/qemu-web-desktop.conf`.
- uncomment or add available VM's in `/etc/qemu-web-desktop/machines.conf`.

and finally:
```bash
sudo chown -R _qemu-web-desktop /usr/share/qemu-web-desktop/html/
sudo find /usr/share/qemu-web-desktop/html/ -type f -exec chmod a+r {} +
sudo find /usr/share/qemu-web-desktop/html/ -type d -exec chmod a+rx {} +
sudo chmod 755 /usr/lib/cgi-bin/qemu-web-desktop.pl
sudo a2enconf qemu-web-desktop
sudo a2enmod cgi
sudo a2enmod include
sudo service apache2 restart
sudo qwdctl download
```

The installation steps for GPU pass-through are described at the end of this documentation.

To un-install, just do a `cd src; sudo make uninstall`.

--------------------------------------------------------------------------------

## Customize to your needs

The service configuration resides in a set of files, as follows:

| File | Description |
|------------------|-------------|
| `/etc/qemu-web-desktop/config.pl` | Main service configuration file. The `config_script` allows further customization of VM's when they boot. |
| `/usr/share/qemu-web-desktop/html/desktop/index.html` | Form. Edit to change appearance, comment/un-comment optional features (GPU, scripts...) |
| `/etc/qemu-web-desktop/machines.conf` | List the available VM's to propose in the service. Run `sudo qwdctl download` after edits. |
| `/etc/qemu-web-desktop/config.pl`:`check_user_custom` | A function reference that is executed when the user clicks on _Create_ to e.g. perform further checks. |
| `/etc/qemu-web-desktop/config.pl`:`config_script` | Auto start scripts. This allows to configure the session _during_ its boot. An example can be `$config{config_script}=("https://gitlab.com/soleil-data-treatment/infra-config/-/raw/master/hosts/grades-vm/SOLEIL-DARTS-auto-login.sh")`. |


Edit the `/etc/qemu-web-desktop/config.pl` file:

- adapt location of files (esp. directories to `machines`,`snapshots`).
- adapt the default specification of virtual machines (cpu, mem).
- adapt the restrictions for using the service (number of connections, load limit).
- adapt the user credential tests you wish to use. They are all tested one after the other, until one works.

If the VMs you wish to run use a different architecture than the host
running the service, you also need to adapt the `$config{qemu_exec}`
variable to the architecture you wish to emulate. Note that you also
need to install the appropriate variant of qemu-system-XXX.

Most options below can be changed in the configuration script, or overridden with command line argument `--name=value`.

Un-comment at will sections in `/usr/share/qemu-web-desktop/html/desktop/index.html` to activate support for GPU, user scripts, and one-shot sessions (which use multiple ports). 

Changes to the configuration are immediate, and there is no need to restart the
server nor the web service.

We list below some of the common options.

### Location of files and directories

The main configuration file is `/etc/qemu-web-desktop/config.pl`.
Web pages are usually in `/usr/share/qemu-web-desktop/html/desktop`.
Virtual machines are usually in `/var/lib/qemu-web-desktop`.
List of available virtual machines for `qwdctl` in `/etc/qemu-web-desktop/machines.conf`.
These settings should be kept to their default for an Apache web server.

| Locations | Default | Description |
|------------------|---------|-------------|
| `dir_html` | /usr/share/qemu-web-desktop/html   | HTML server root. Contains the `index.html` form |
| `dir_service`  | /var/lib/qemu-web-desktop     | Location of virtual machines |
| `dir_machines` | /var/lib/qemu-web-desktop/machines | Full path to machines (ISO,VM) |
| `dir_snapshots` | /var/lib/qemu-web-desktop/snapshots | Where snapshots are stored |
| `dir_cfg` | /tmp | Temporary files (JSON for sessions) |
| `dir_novnc` | /usr/share/novnc | Location of noVNC directory, must contain `vnc.html` |
| `dir_websockify` | websockify | Location of Websockify executable |
| `dir_mounts` | (/mnt,/media) | Volumes from host to mount in guests. Use e.g. `mount -t 9p -o trans=virtio,access=client host_media /mnt/media` in guest. The last word of the mount path is used to build the 9p label `host_<last_word>`.|

### Server settings

| Important options | Default | Description |
|------------------|---------|-------------|
| `snapshot_lifetime` | 86400   | Maximum time in seconds above which sessions are stopped |
| `service_max_load`  | 0.8     | Maximal load of the machine, in 0-1 where 1 means all CPU's are used |
| `service_max_instance_nb` | 10 | Maximum number of simultaneous sessions |
| `service_port` | 6080 | The port to which the display will be broadcast. This is shown in the client URL. When `service_port_multiple` is set, the port is chosen randomly in `[service_port:service_port+service_max_instance_nb]` |
| `service_port_vnc` | 5901 | The base internal VNC port to use |
| `service_port_multiple` | 0 | When true, use one websockify port per instance (e.g. :6080+rand). When false, a single port is used for all sessions with random tokens, except for one-shot sessions. |
| `service_proxy` | "" | A proxy URL to pass through when getting external scripts, e.g.  "http://xxx.yy.z:port/". This is used for the auto-start script option (see [Usage](#usage-as-a-web-service)). |
| `certificate_crt` | /etc/apache2/certificate/apache-certificate.crt | A certificate CERT bundle in order to use HTTPS. The KEY must also be available. The web server should use the same certificates. |
| `certificate_key` | /etc/apache2/certificate/apache.key | A certificate KEY in order to use HTTPS. The CERT must also be available. The web server should use the same certificates. |
| `fallback_servers` | "" | A comma-separated list of servers, e.g. `http://server1,server2,195.221.4.1`. URL, server names and IP are allowed. When the current server is overloaded (cpu,mem,GPU), the request is sent to the other servers. |
| `config_script` | ("") | An array of strings specifying scripts to execute at boot, as root (see below). |

#### Using `config_script`

Each string can be given as:

- a URL "http://some/url"
- a path "/some/local/path/to/script"
- a string starting with `exec:` followed by shell commands separated by EOL or `;`
- a string starting with `virt-customize:` followed by one-line commands separated by EOL `\n`. (see: https://libguestfs.org/virt-customize.1.html). 

The symbols `@USER@` `@SESSION_NAME@` and `@VM@` are replaced by the user name, the session ID, and the virtual machine name. 

In addition, when the above script description is preceded by `if(EXPR):`, the given expression is evaluated (with Perl) and the script is only executed when result is True. The `EXPR` condition may use the `@...@` symbols above.

This way, it is possible to specify scripts that apply to given virtual machines with e.g.:

- `if("@VM@" =~ /debian/i): http://some/url` (only for _debian_ VM's, case insensitive).
- `if("@VM@" =~ /debian/): exec: touch /tmp/my_script_is_executed` (only for _debian_ VM's, case sensitive).
- `if("@VM@" =~ /unstable/i): https://gitlab.com/soleil-data-treatment/soleil-software-projects/trunk-in-my-car/-/raw/main/SOLEIL-DARTS-auto-login.sh` for our auto-login process at Synchrotron SOLEIL.
- `if("@USER@" =~ /farhie/): http://some/url` (only for a given user).

Scripts can be specified in `/etc/qemu-web-desktop/config.pl: config{config_script}`, as well as in the `index.html` form by un-commented the corresponding section. A text box then allows to enter the script description, but this setting is not recommended as it potentially provides a root access to all VM's and all users.


#### Security aspects

| :warning: Note about the used ports |
|---|
| <b>Highest security:</b> The default setting is `service_port_multiple=0` which indicates that a single port is used for all sessions. The 'one-shot' and the auto-start user script options in the `/usr/share/qemu-web-desktop/html/desktop/index.html` file should be left commented (inactivated). The HTTPS certificates should as well be set (see above). The user scripts should also better be inactivated in the form, and the VM's should not allow administrator privileges (e.g. `sudo`). These are the recommended settings for a secured network. |
| <b>Medium security:</b> Optionally un-comment the 'one-shot' option in the `/usr/share/qemu-web-desktop/html/desktop/index.html` file. One specific port will be used for these sessions, and be closed as soon as the session browser tab is closed. Other sessions will use a single shared port when `service_port_multiple=0`. |
| <b>Low security:</b> When `service_port_multiple=1`, each session has its own communication port. You can un-comment the 'one-shot' and the auto-start user script sections in the `/usr/share/qemu-web-desktop/html/desktop/index.html` file.  The ports `service_port` up to `service_port+service_max_instance_nb` must be allowed on the network. |

### User credential settings

It is possible to activate more than one authentication mechanism, which are tested until one works. The details of the SMTP, IMAP and LDAP server settings should be set in the `/etc/qemu-web-desktop/config.pl` script.

| User authentication | Default | Description |
|------------------|---------|-------------|
| `check_user_with_email` | 0 | When set and user ID is an email, a message with the connection information is sent as authentication |
| `check_user_with_imap` | 0 | When set, the user ID/password is checked against specified IMAP server |
| `check_user_with_smtp` | 0 | When set, the user ID/password is checked against specified SMTP server |
| `check_user_with_ldap` | 0 | When set, the user ID/password is checked against specified LDAP server |
| `check_user_custom`    | "" | May point to a function reference to allow any identification mechanism (see below) |

##### Custom authentication mechanism

It is possible to define custom authentication mechanisms via a user function that should get `(user, pw, authenticated)` as arguments and return a string starting by "SUCCESS" or "FAILED". 
The default return value should be the previous authenticator results. 
Any "SUCCESS" in the returned string fully qualifies the authentication.

In practice, define such a function in `/etc/qemu-web-desktop/config.pl` as:
```perl
sub check_user_func {
  my $user          = shift;
  my $pw            = shift;
  my $authenticated = shift; # previous authenticator results
  my $res           = "";
  
  # choose state depending on $user and $pw, as well as previous $authenticated
  res = "SUCCESS: [Custom] $user authenticated.";

  res = "FAILED: [Custom] $user failed authentication.";

  return "$authenticated and $res";
}

$config{check_user_custom} = \&check_user_func;
```

or directly as an anonymous function
```perl
 $config{check_user_custom} = sub { ... };
```

Such a function could be used as an independent authenticator when other authenticators have failed. Then, its result should be a concatenation of the new message with previous ones (such as above):
```perl
return "$authenticated and $res";
```

But the custom function could also be used as a further check, validating a previously successful identification, such as in:
```perl
# any previous authenticator was successful
if (index($authenticated, "SUCCESS") >= 0) {
  # make further checks on user credentials 
  if ($user and $pw) {
    return "SUCCESS $user with custom auth.";
  } else {
    return "FAILED $user with custom auth.";
  }
} else {
  return $authenticated;
}
```
In this example, the returned string supersedes the previously set authentication when it was successful (else it remains as before, e.g FAILED).

##### IMAP/SSL encryption level

:warning: The SSL encryption level of the IMAP server (for user credentials) should match that of the server running the remote desktop service. 
The current Debian rules are to use SSL v1.2 or v1.3. In case the user IMAP authentication brings errors such as:
```
IMAP error "Unable to connect to <server>: SSL connect attempt failed error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol."
```
which appears in the Apache2 error log (`/var/log/apache2/error.log`), then you may [downgrade the SSL encryption](https://stackoverflow.com/questions/53058362/openssl-v1-1-1-ssl-choose-client-version-unsupported-protocol) requirement in the file `/etc/ssl/openssl.cnf` in a section such as:
```
[system_default_sect]
MinProtocol = TLSv1
CipherString = DEFAULT@SECLEVEL=1
```

### Distributing the sessions work-load (load-levelling, scale-up)

It is possible to distribute the sessions over a set of servers. Each server has 
its own settings (load, GPU, ...). When the current server is overloaded 
(number of sessions,  cpu, mem, GPU), the request is sent to other servers in 
the list, until one can provide the service. A session must still fit on a single server (it can not be split into parts on different servers).

The only requirements are:

- install DARTS/qemu-web-desktop on all servers.
- configure each server with their own settings.
- install the same virtual machines on all servers (must have same name).
- set `fallback_servers` to a comma-separated list of servers, e.g. `http://server1,server2,195.221.4.1`. All of URL, server names and IP are allowed. The URL should be preferred as it indicates the protocol to use (http or https).
- make sure fallback servers are reachable. However when a server is down, it is ignored, so that the computing infrastructure can cope with failures.

The list of fallback servers may be the same for all servers in the farm, so 
that the workload is fully shared and distributed equally, whatever be the used entry point. You may favour one entry point, and distribute the load to other servers. But you may as well define specific fallback lists on various servers, to allow 
different entry points and workload distributions. For instance you may group 
servers providing GPU's and similar resources. You may as well redirect to shared 
servers when local private servers are filled, but not the other 
way round, to secure some resources. Only the `fallback_servers` list has to be 
specified to scale-up your infrastructure.

There is no need to install any other complex load-leveller system. The DARTS
system is a decentralized cluster. Any of its elements is an entry point to the
service, and distributes the load automatically when needed.
Following the above procedure provides a very fast way to scale-up a compute 
infrastructure. Just install a new computer with DARTS/qemu-web-desktop, and add
its name to the other nodes. It will immediately be callable.

--------------------------------------------------------------------------------

## Managing Virtual machines

### Creating virtual machines

It is possible to create a VM from an ISO, just like you would boot physically. An empty disk is first created (here with size 10GB).
```bash
qemu-img create -f qcow2 machine1.qcow2 10G
```
Then you should boot from an ISO file (here indicated as `file.iso`)
```bash
qemu-system-x86_64  -m 4096 -smp 4 -hda machine1.qcow2 -name MySuperbVM -boot d -cdrom file.iso  -device ich9-ahci,id=ahci -enable-kvm -cpu host -vga qxl -netdev user,id=mynet0 -device virtio-net,netdev=mynet0 -device virtio-balloon
```
and install the system on the prepared disk. 

You may also convert an existing VDI/VMDK file (VirtualBox and VMWare formats - here `file.vmdk`) into QCOW2 for QEMU (here machine1.qcow2`) with command:
```bash
qemu-img convert -f vmdk -O qcow2 file.vmdk machine1.qcow2
```

Last, you may dump an existing physical disk (with a functional system - here from device `dev/sda`) into a QCOW2 format:
```bash
qemu-img convert -o qcow2 /dev/sda machine1.qcow2
```

The QCOW2 format allows to resize disks, for instance with:
```bash
qemu-img resize machine1.qcow2 +50G
```

If a VM file gets too large, you can (re)compress it with command:
```
qemu-img convert -O qcow2 -c image.qcow2_backup image_compressed.qcow2
```

### Adding virtual machines to the service

The easiest is to make use of the `qwdctl` tool (see below). Other options are activated by un-commenting sections in the file `/usr/share/qemu-web-desktop/html/desktop/index.html`.

#### Automatic configuration of virtual machines via `qwdctl`

Each entry in the configuration file `/etc/qemu-web-desktop/machines.conf` spans on 2 or 3 lines:

-  [name.ext]
-  url=[URL to ISO, QCOW2, VDI, VMDK, RAW virtual machine disk] (optional if the file is already present)
-  description=[description to be shown in the login page] 

Images listed in the configuration file without a `url=` parameter are expected to be downloaded by hand and installed into `/var/lib/qemu-web-desktop/machines` by the local administrator. in this case, just specify the [name.ext] and [description]. Images with a `[url]` line are downloaded (requires a configured network connection).

Then actually launch
```
sudo -E qwdctl download
```
or, to only update existing machines, use:
```
sudo -E qwdctl refresh
```

#### Manual configuration of virtual machines

You may also do this by hand. Place any ISO, QCOW2, VDI, VMDK, RAW virtual machine file in e.g.
`/var/lib/qemu-web-desktop/machines`.

```bash
$ ls /var/lib/qemu-web-desktop/machines

dsl.iso    slax.iso    machine1.qcow2 ...
```
Then create/edit the `/usr/share/qemu-web-desktop/html/machines.html` (link from `/var/lib/qemu-web-desktop/machines.html`) web page and add entries to reflect the VM files in `html/machines`:
```html
  <option value="slax.iso">Slax (Debian)</option>
  <option value="dsl.iso">Damn Small Linux</option>
  ...
  <option value="machine1.qcow2">My superb VM</option>
  ...
```

You can also comment/uncomment sections (e.g. GPU, user script, one-shot) at will in the main web page `/usr/share/qemu-web-desktop/html/desktop/index.html`. Defaults will then be used.

:+1: This project provides minimal ISO's for testing (in `html/desktop/machines`):

- [Slax](https://www.slax.org/) a modern, yet very compact Debian system (265 MB)
- [DSL](http://www.damnsmalllinux.org/) a very compact, old-style Linux (50 MB)

We also recommend:

- https://puppylinux-woof-ce.github.io/index.html
- https://www.bodhilinux.com/

There exist some virtual machine repositories, for instance:

- https://marketplace.opennebula.systems/appliance
- https://github.com/palmercluff/qemu-images
- https://www.osboxes.org

### Starting, sharing, stopping, re-connecting

The DARTS/qemu-web-desktop service allows to connect and re-connect to active sessions (except for one-shot sessions). It is also possible to share the connection link to an active session, so that multiple users can see and interact on the same environment. Last, sessions can be ended either from the sessions themselves (find the 'Shutdown' item), or aborted with the 'Stop' button shown in the session information page.

All of these actions are possible when selecting the 'Manage sessions' button on the right of the service page, with proper credentials. Only your sessions will be listed.

--------------------------------------------------------------------------------

## Usage: as a web service

First make sure the service has been installed in the `html/desktop` root level of the host, and the `cgi-bin/qemu-web-desktop.pl` e.g. in the `/usr/lib/cgi-bin`.

Open a browser and go to:

- http://localhost/qemu-web-desktop/

Customize your machine with the usual settings:

- the user credentials. The protocol used for checking depends on the `/etc/qemu-web-desktop/config.pl` settings (default is 'no check').
- the system (virtual machine) to boot.
- the number of cores (#CPU).
- the amount of memory.
- the life-time.

Optionally (when un-commenting sections in the web form `/usr/share/qemu-web-desktop/html/desktop/index.html`)

- a GPU request (the system must have been configured as explained below).
- the auto-start script to execute at boot. This script may contain any set of commands (installations via `apt`, `pip` or `conda`, configuration, start-up of a service or application, ...). The symbols `@USER@` `@SESSION_NAME@` and `@VM@` are replaced by the user name, the session ID, and the virtual machine name. The script is executed with administrator privileges in the guest virtual machine. In case the server is protected by a proxy, the `service_proxy` must be set in the `/etc/qemu-web-desktop/config.pl` file to access external resources. This option is not recommended for high security systems. The script may be:
    * a path to a script on the server
    * a URL to a distant file (URL such as on <a href="github.com">github.com</a> or <a href="gitlab.com">gitlab.com</a> - make sure to provide a raw content). We provide as an example the script https://gitlab.com/soleil-data-treatment/soleil-software-projects/trunk-in-my-car/-/raw/main/start-script.sh
    * a string starting with `exec:` or `bash:` and followed by shell commands separated by ';' or EOL. An example would be `exec: touch /tmp/hello`.
- the one-shot button, which creates virtual machines allowing only a single connection.

Then press the **Create** button. After about 10 seconds, information is displayed. Follow instructions, click the given link or scan the QR code to connect the display. You can of course access the service remotely if the server is on a network.

Connect within a browser to the displayed IP, such as:

- http://localhost:6080/vnc.html?resize=remote&path=?token=jNIjYTUn

Once done with the session, make sure you shut-down the remote desktop session. Do not just close the browser, suspend or logout. This is to free the resources for others once you do not need the session anymore.

You can close the browser any time, and reconnect later: the session remains active, any calculation will proceed. To reconnect you may click again on the link. If you have lost this link, first click on the **Manage sessions** button (or select the same item in the machine list), then find the relevant session in the table, and select the CONNECT item. You may as well stop the session prematurely with the STOP button.

Last, it is possible to send the session link to your colleagues (or the QR code), so that you all see the same desktop and work together. 
You should however notice that rules must be adopted to share your multiple keyboards and mice.

Once the maximum life-time is over, the session is automatically stopped and cleaned-up. There is no way to recover a cleared session.

## Usage: local (for testing)

It is possible to test that all works by launching a Slax distribution.

```bash
cd qemu-web-desktop/src
perl cgi-bin/qemu-web-desktop.pl --dir_service=html/desktop \
  --dir_html=html --dir_snapshots=/tmp --qemu_video=std \
  --dir_machines=html/desktop/machines/
```
which uses the configuration file at `/etc/qemu-web-desktop/config.pl` (must be there).

A text is displayed (HTML format) in the terminal, which indicates a URL.

Connect with a web browser to the displayed URL, such as:
- http://localhost:6080/vnc.html?resize=remote&path=?token=jNIjYTUn

The `qemu-web-desktop.pl` script can be used as a command with additional arguments. The
full list of supported options is obtained with:
```bash
qemu-web-desktop.pl --help
```

You can force a session to stop with:
```bash
qemu-web-desktop.pl --session_stop=/path/to/json
```

And you can stop and clear all sessions with:
```bash
qemu-web-desktop.pl --session_purge=1
```

Last, you can monitor all running sessions, with:
```bash
qemu-web-desktop.pl --service_monitor=1 > /tmp/mon.html
firefox /tmp/mon.html
```
which generates an HTML file and renders it in a browser.

:warning: For all the above commands, make sure you have to permissions to access the `dir_snapshots` and `dir_cfg` directories. You can specify these with:
```bash
qemu-web-desktop.pl  ... --dir_snapshots=/tmp --dir_cfg=/tmp
```

--------------------------------------------------------------------------------

## Installation: GPU pass-through

It is possible to use a physical GPU inside virtual machine sessions.

:warning: This GPU is exclusively attached to the virtual machine, and can not anymore be used on the server for display. This implies that you should have at least two distinct GPU's (of different model).

In the following, we assume we have a server with an AMD CPU, and NVIDIA GPU's, all running on a Debian system. The first step is to ensure that your server can detach a GPU from the host system. The feature which is used is called IOMMU/VFIO.
```
$ sudo dmesg | grep "AMD-Vi\|Intel VT-d"
[    1.059323] AMD-Vi: IOMMU performance counters supported
$ lscpu | grep -i "Virtualisation"
Virtualisation :                        AMD-V
$ egrep -q '^flags.*(svm|vmx)' /proc/cpuinfo && echo virtualization extensions available
virtualization extensions available
$ lspci -nnv | grep "VGA\|Audio\|Kernel driver in use: snd_hda_intel\|Kernel driver in use: nouveau\|Kernel driver in use: nvidia\|Kernel driver in use: nouveaufb\|Kernel driver in use: radeon"
4c:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP108 [10de:1d01] (rev a1) (prog-if 00 [VGA controller])
    Kernel driver in use: nvidia
4c:00.1 Audio device [0403]: NVIDIA Corporation GP108 High Definition Audio Controller [10de:0fb8] (rev a1)
    Subsystem: ASUSTeK Computer Inc. GP108 High Definition Audio Controller [1043:8746]
    Kernel driver in use: snd_hda_intel
4d:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP108 [10de:1d01] (rev a1) (prog-if 00 [VGA controller])
    Kernel driver in use: nvidia
4d:00.1 Audio device [0403]: NVIDIA Corporation GP108 High Definition Audio Controller [10de:0fb8] (rev a1)
    Subsystem: ASUSTeK Computer Inc. GP108 High Definition Audio Controller [1043:8621]
    Kernel driver in use: snd_hda_intel
```
which results in a list of available GPU. In the following, we assume we have two low-cost/power NVIDIA GT 1030 (384 cores, 2 GB memory) cards, on PCI addresses `4c:00` and `4d:00`. It is important to also take note of the hardware vendor:model code for the GPU, here `10de:1d01`.

In the following step, we detach these GT 1030 cards at boot. In the `/etc/default/grub` file activate IOMMU, and flag the vendor:model codes (here with video and sound parts - multiple cards are possible separated with commas):
```
GRUB_CMDLINE_LINUX_DEFAULT = "quiet amd_iommu=on iommu=pt vfio-pci.ids=10de:1d01,10de:0fb8"
```
For Intel CPU's, you would use option `intel_iommu=on`.
This GPU information should also be added as a `modprobe` option. Create for instance the file `/etc/modprobe.d/vfio.conf` with content:
```bash
# /etc/modprobe.d/vfio.conf
options vfio-pci ids=10de:1d01,10de:0fb8 disable_vga=1
```
and push necessary modules into the kernel by adding:
```
# /etc/initramfs-tools/modules
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
vhost-netdev
```
into file `/etc/initramfs-tools/modules`.

Finally reconfigure the boot and linux kernel, and restart the server:
```bash
sudo update-initramfs -u
sudo update-grub
sudo reboot
```
After reboot, the command `lspci -nnk` will show the detached cards as used by the `vfio-pci` kernel driver.

:warning: all identical GPU of that model (`10de:1d01`) are detached. It is not possible to keep one on the server, and send the other same model to the VM. This is why at least two different GPU models are physically needed in the computer.

It is now necessary to configure the system so that the Apache user can launch qemu with IOMMU/VFIO pass-through. Else you get errors such as:

`VFIO: ... permission denied`

Change VFIO access rules so that group `kvm` can use it. Add in file `/etc/udev/rules.d/10-qemu-hw-users.rules`:
```
# /etc/udev/rules.d/10-qemu-hw-users.rules
SUBSYSTEM=="vfio", OWNER="root", GROUP="kvm"
```
then restart `udev`
```bash
sudo udevadm control --reload-rules
sudo udevadm trigger
```

Uncomment the GPU-passthrough section in the index.html file in the `src/html/desktop` directory.

Then, for testing purposes, launch a `qemu` command, such as:

```
qemu-system-x86_64  -m 4096 -smp 4 -hda debian10.qcow2 -name Debian -device ich9-ahci,id=ahci -enable-kvm -cpu host,kvm=off -vga qxl -netdev user,id=mynet0 -device virtio-net,netdev=mynet0 -device virtio-balloon -device vfio-pci,host=0000:4c:00.0,multifunction=on
```

You may specify a list of black-listed GPU in the `$config{gpu_blacklist}` item,
to e.g. reserved a GPU for other purposes, or isolate a defective device.

### Common issues with GPU passthrough

#### Memory error (vfio_dma)
When running the web service, you may experience in the Apache error.log messages like:
```
qemu-system-x86_64: -device vfio-pci,host=0000:4c:00.0,multifunction=on: VFIO_MAP_DMA: -12
qemu-system-x86_64: -device vfio-pci,host=0000:4c:00.0,multifunction=on: vfio_dma_map(0x55966269d230, 0x100000, 0xbff00000, 0x7f55b7f00000) = -12 (Cannot allocate memory)
```
as well as:
```
vfio_pin_pages_remote: RLIMIT_MEMLOCK (65536) exceeded
```
is `dmesg` which is triggered by a low memory allocation threshold `ulimit`.

Adapt the memory pre-allocation for the GPU. This is done in `/etc/security/limits.conf` by adding lines at the end:
```
# /etc/security/limits.conf
*    soft memlock 20000000
*    hard memlock 20000000
@kvm soft memlock unlimited
@kvm hard memlock unlimited
```
The value is given in Kb, here 20 GB for all users, and unlimited for group `kvm`. Perhaps this 20 GB value should match the internal GPU memory.

Do something similar when Apache starts with SystemD e.g. in `/etc/systemd/system/multi-user.target.wants/apache2.service`
```
# /etc/systemd/system/multi-user.target.wants/apache2.service
[Service]
...
LimitMEMLOCK=infinity
```

It is also possible (and recommended) to configure the Apache service without modifying the whole systemd script. Just use:

    sudo systemctl edit apache2.service

and enter the content of the 'override' file /etc/systemd/system/apache2.service.d/override.conf

```
# /etc/systemd/system/apache2.service.d/override.conf
[Service]
LimitMEMLOCK=infinity
```



#### IOMMU groups (group not viable)

The GPU are attached to physical PCI connectors, which arrangement is handled by the system with a topology seen in the IOMMU groups. But, in order for QEMU/KVM to pass-through a device (GPU), it must be bound to a single IOMMU. In case the GPU is part of an IOMMU with other stuff in, all these must also be detached via the VFIO driver. 

Then you will see error messages such as
```
group 60 is not viable
Please ensure all devices within the iommu_group are bound to their vfio bus driver.

```

First check that indeed your GPU are not alone in their IOMMU group. The following command displays the IOMMU groups and the attached devices.
```
$ for d in /sys/kernel/iommu_groups/*/devices/*; do n=${d#*/iommu_groups/*}; n=${n%%/*}; printf 'IOMMU Group %s ' "$n"; lspci -nns "${d##*/}"; done;
```

To solve this,  you may move physically the GPU cards to other PCI-slots in order to find better arrangements. But this is not always effective, nor possible. 

You can further allow your BIOS to shuffle a little the IOMMU groups with the settings (for AMD CPU's), e.g.:

- mode NUMA BIOS/AMD CBS/DF/Memory addressing/NBS4
- mode BIOS/AMD CBS/NBIO/PCIe ARI=Enabled
- mode BIOS/AMD CBS/NBIO/IOMMU=Enabled

Last, when all this fails, a definitive solution is to use a special patch for the Linux kernel, known as `pcie_acs_override`. You will need to use a special kernel from e.g. https://liquorix.net/#install Make sure you get the same Linux kernel version as the one you currently run, so that GPU drivers (NVIDIA) are compatible. On a Debian system, you would for instance add `deb http://liquorix.net/debian bullseye main` to `/etc/apt/sources.list`, and issue:
```
sudo apt install linux-headers-5.10.0-17.1-liquorix-amd64 linux-image-5.10.0-17.1-liquorix-amd64`
```

And finally add into the `/etc/default/grub`
```
GRUB_CMDLINE_LINUX_DEFAULT= ... pcie_acs_override=downstream,multifunction
```
and reboot. Now there should be one IOMMU group per device.

## How it works

A static HTML page with an attached style sheet (handling responsive design), calls a perl CGI on the Apache server. This CGI creates a snapshot of the selected virtual machine (so that local changes by the user do not affect the master VM files). A [`qemu`](https://www.qemu.org/) command line is assembled, typically (here 4 SMP cores and 8 GB memory):
```bash
qemu-system-x86_64  -m 8192 -smp 4 -hda machine1-snapshot.qcow2 -device ich9-ahci,id=ahci -enable-kvm -cpu host -vga qxl -netdev user,id=mynet0 -device virtio-net,netdev=mynet0 -device virtio-balloon
```
The integrated QEMU VNC server is also launched, so that we can access the VM display. As indicated, we also use the `virtio-balloon` device, which allows to share the unused memory when multiple VM's are launched. When IOMMU/VFIO GPU are available, their PCI slot is passed to QEMU with the `virtio-pci` option.

A websocket is attached to the QEMU VNC, and redirected to a noVNC port, so that we can display the VM screen in a browser.

A monitoring page is also handled by the CGI script, to display the server load and running sessions. These can be killed one-by-one, or all at once.

The perl CGI script that does all the job fits in only 1600 lines.

--------------------------------------------------------------------------------

## Credits

(c) 2020- Emmanuel Farhi - GRADES - Synchrotron Soleil. AGPL3.

- https://gitlab.com/soleil-data-treatment/soleil-software-projects/remote-desktop

This project has received support from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 957189.

We have benefited from the following web resources.

### Debian/Ubuntu documentation

- https://doc.ubuntu-fr.org/vfio (in French)
- https://alpha.lordran.net/posts/2018/05/12/vfio/ (in French)
- https://passthroughpo.st/gpu-debian/
- https://wiki.debian.org/VGAPassthrough
- https://davidyat.es/2016/09/08/gpu-passthrough/
- https://heiko-sieger.info/low-end-kvm-virtual-machine/

### Other documentation

- https://mathiashueber.com/windows-virtual-machine-gpu-passthrough-ubuntu/
- https://wiki.archlinux.org/index.php/PCI_passthrough_via_OVMF
- https://neg-serg.github.io/2017/06/pci-pass/ (ARCH linux)
- https://wiki.gentoo.org/wiki/GPU_passthrough_with_libvirt_qemu_kvm (Gentoo)
- https://medium.com/@calerogers/gpu-virtualization-with-kvm-qemu-63ca98a6a172

### VirtualBox documentation

- https://docs.oracle.com/en/virtualization/virtualbox/6.0/admin/pcipassthrough.html

## Building a Debian package

To build a Debian package out of this repository or https://salsa.debian.org/debian/qemu-web-desktop, use:
```
sudo apt install git-buildpackage dh-apache2 dh-sysuser devscripts pandoc
make deb
sudo apt install ../qemu-web-desktop_*_amd64.deb
# uncomment [slax] and [dsl] entries in /etc/qemu-web-desktop except for 'url' lines.
sudo qwdctl refresh
```
will create a `.deb` package in the directory level above.