[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