[pve-devel] [PATCH qemu-server 4/5] fix #2264: add virtio-rng device
Thomas Lamprecht
t.lamprecht at proxmox.com
Fri Feb 21 10:13:54 CET 2020
Am 2/20/20 um 6:10 PM schrieb Stefan Reiter:
> Allow a user to add a virtio-rng-pci (an emulated hardware random
> number generator) to a VM with the rng0 setting. The setting is
> version_guard()-ed.
>
> Limit the selection of entropy source to one of three:
> /dev/urandom (preferred): Non-blocking kernel entropy source
> /dev/random: Blocking kernel source
> /dev/hwrng: Hardware RNG on the host for passthrough
>
> QEMU itself defaults to /dev/urandom (or the equivalent getrandom()
> call) if no source file is given, but I don't fully trust that
> behaviour to stay constant, considering the documentation [0] already
> disagrees with the code [1], so let's always specify the file ourselves.
>
> /dev/urandom is preferred, since it prevents host entropy starvation.
> The quality of randomness is still good enough to emulate a hwrng, since
> a) it's still seeded from the kernel's true entropy pool periodically
> and b) it's mixed with true entropy in the guest as well.
>
> Additionally, all sources about entropy predicition attacks I could find
> mention that to predict /dev/urandom results, /dev/random has to be
> accessed or manipulated in one way or the other - this is not possible
> from a VM however, as the entropy we're talking about comes from the
> *hosts* blocking pool.
>
> More about the entropy and security implications of the non-blocking
> interface in [2] and [3].
>
> Note further that only one /dev/hwrng exists at any given time, if
> multiple RNGs are available, only the one selected in
> '/sys/devices/virtual/misc/hw_random/rng_current' will feed the file.
> Selecting this is left as an exercise to the user, if at all required.
>
> We limit the available entropy to 1 KiB/s by default, but allow the user
> to override this. Interesting to note is that the limiter does not work
> linearly, i.e. max_bytes=1024/period=1000 means that up to 1 KiB of data
> becomes available on a 1000 millisecond timer, not that 1 KiB is
> streamed to the guest over the course of one second - hence the
> configurable period.
>
> The default used here is the same as given in the QEMU documentation [0]
> and has been verified to affect entropy availability in a guest by
> measuring /dev/random throughput. 1 KiB/s is enough to avoid any
> early-boot entropy shortages, and already has a significant impact on
> /dev/random availability in the guest.
>
> [0] https://wiki.qemu.org/Features/VirtIORNG
> [1] https://git.qemu.org/?p=qemu.git;a=blob;f=crypto/random-platform.c;h=f92f96987d7d262047c7604b169a7fdf11236107;hb=HEAD
> [2] https://lwn.net/Articles/261804/
> [3] https://lwn.net/Articles/808575/
>
> Signed-off-by: Stefan Reiter <s.reiter at proxmox.com>
> ---
>
> Includes a version bump currently set to 4.1+pve2. Would need to be changed if
> applied later.
Why?? This is a new option a user has explicit to set, either it's there or not.
I'd bump the version just for every new option...
>
> Tested with all three options (luckily enough, my system has a hwrng called
> 'ccp-1-rng', which seems to come from the AMD CPU's Cryptographic Coprocessor).
>
> /dev/random does indeed lead to entropy starvation, while /dev/urandom does not.
> rngtest (from rng-tools) reports high-quality randomness inside VMs, no matter
> what source is selected.
>
> PVE/QemuServer.pm | 73 +++++++++++++++++++++++++++++++++++++++++++
> PVE/QemuServer/PCI.pm | 1 +
> 2 files changed, 74 insertions(+)
>
> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
> index 23176dd..0065a3c 100644
> --- a/PVE/QemuServer.pm
> +++ b/PVE/QemuServer.pm
> @@ -219,6 +219,43 @@ my $spice_enhancements_fmt = {
> },
> };
>
> +my $rng_fmt = {
> + source => {
> + type => 'string',
> + enum => ['/dev/urandom', '/dev/random', '/dev/hwrng'],
> + default_key => 1,
> + description => "The file on the host to gather entropy from. In most"
> + . " cases /dev/urandom should be preferred over /dev/random"
> + . " to avoid entropy-starvation issues on the host. Using"
> + . " urandom does *not* decrease security in any meaningful"
> + . " way, as it's still seeded from real entropy, and the"
> + . " bytes provided will most likely be mixed with real"
> + . " entropy on the guest as well. /dev/hwrng can be used"
> + . " to pass through a hardware RNG from the host.",
> + },
> + max_bytes => {
> + type => 'integer',
> + description => "Maximum bytes of entropy injected into the guest every"
> + . " 'period' milliseconds. Prefer a lower value when using"
> + . " /dev/random as source. Use 0 to disable limiting"
> + . " (potentially dangerous!).",
> + optional => 1,
> +
> + # default is 1 KiB/s, provides enough entropy to the guest to avoid
> + # boot-starvation issues (e.g. systemd etc...) while allowing no chance
> + # of overwhelming the host, provided we're reading from /dev/urandom
> + default => 1024,
> + },
> + period => {
> + type => 'integer',
> + description => "Every 'period' milliseconds the entropy-injection quota"
> + . " is reset, allowing the guest to retrieve another"
> + . " 'max_bytes' of entropy.",
> + optional => 1,
> + default => 1000,
> + },
> +};
> +
> my $confdesc = {
> onboot => {
> optional => 1,
> @@ -607,6 +644,12 @@ EODESCR
> description => 'Tags of the VM. This is only meta information.',
> optional => 1,
> },
> + rng0 => {
> + type => 'string',
> + format => $rng_fmt,
> + description => "Configure a VirtIO-based Random Number Generator.",
> + optional => 1,
> + },
> };
>
> my $cicustom_fmt = {
> @@ -2348,6 +2391,16 @@ sub parse_vga {
> return $res;
> }
>
> +sub parse_rng {
> + my ($value) = @_;
> +
> + return undef if !$value;
> +
> + my $res = eval { PVE::JSONSchema::parse_property_string($rng_fmt, $value) };
> + warn $@ if $@;
> + return $res;
> +}
> +
> PVE::JSONSchema::register_format('pve-qm-usb-device', \&verify_usb_device);
> sub verify_usb_device {
> my ($value, $noerr) = @_;
> @@ -3827,6 +3880,26 @@ sub config_to_command {
> }
> }
>
> + my $rng = parse_rng($conf->{rng0}) if $conf->{rng0};
> + if ($rng && &$version_guard(4, 1, 2)) {
> + my $max_bytes = $rng->{max_bytes} // $rng_fmt->{max_bytes}->{default};
> + my $period = $rng->{period} // $rng_fmt->{period}->{default};
> +
> + my $limiter_str = "";
> + if ($max_bytes) {
> + $limiter_str = ",max-bytes=$max_bytes,period=$period";
> + }
> +
> + # mostly relevant for /dev/hwrng, but doesn't hurt to check others too
> + die "cannot create VirtIO RNG device: source file '$rng->{source}' doesn't exist\n"
> + if ! -e $rng->{source};
> +
> + my $rng_addr = print_pci_addr("rng0", $bridges, $arch, $machine_type);
> +
> + push @$devices, '-object', "rng-random,filename=$rng->{source},id=rng0";
> + push @$devices, '-device', "virtio-rng-pci,rng=rng0$limiter_str$rng_addr";
> + }
> +
> my $spice_port;
>
> if ($qxlnum) {
> diff --git a/PVE/QemuServer/PCI.pm b/PVE/QemuServer/PCI.pm
> index c3b4716..4d9ce26 100644
> --- a/PVE/QemuServer/PCI.pm
> +++ b/PVE/QemuServer/PCI.pm
> @@ -72,6 +72,7 @@ sub get_pci_addr_map {
> 'net31' => { bus => 1, addr => 26 },
> 'xhci' => { bus => 1, addr => 27 },
> 'pci.4' => { bus => 1, addr => 28 },
> + 'rng0' => { bus => 1, addr => 29 },
> 'virtio6' => { bus => 2, addr => 1 },
> 'virtio7' => { bus => 2, addr => 2 },
> 'virtio8' => { bus => 2, addr => 3 },
>
More information about the pve-devel
mailing list