[pve-devel] [PATCH manager v2 6/9] ui: allow use of mapped hardware RNGs as entropy sources for VMs

Filip Schauer f.schauer at proxmox.com
Wed Jan 29 16:53:36 CET 2025


Signed-off-by: Filip Schauer <f.schauer at proxmox.com>
---
 www/manager6/Makefile                 |  1 +
 www/manager6/form/HWRNGMapSelector.js | 99 +++++++++++++++++++++++++++
 www/manager6/qemu/HardwareView.js     |  9 ++-
 www/manager6/qemu/RNGEdit.js          | 79 ++++++++++++++-------
 4 files changed, 159 insertions(+), 29 deletions(-)
 create mode 100644 www/manager6/form/HWRNGMapSelector.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 01a95c7e..d148a1c9 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -44,6 +44,7 @@ JSSRC= 							\
 	form/GuestIDSelector.js				\
 	form/HashAlgorithmSelector.js			\
 	form/HotplugFeatureSelector.js			\
+	form/HWRNGMapSelector.js			\
 	form/IPProtocolSelector.js			\
 	form/IPRefSelector.js				\
 	form/MDevSelector.js				\
diff --git a/www/manager6/form/HWRNGMapSelector.js b/www/manager6/form/HWRNGMapSelector.js
new file mode 100644
index 00000000..1c795d2d
--- /dev/null
+++ b/www/manager6/form/HWRNGMapSelector.js
@@ -0,0 +1,99 @@
+Ext.define('PVE.form.HWRNGMapSelector', {
+    extend: 'Proxmox.form.ComboGrid',
+    alias: 'widget.pveHWRNGMapSelector',
+
+    store: {
+	fields: ['name', 'path'],
+	filterOnLoad: true,
+	sorters: [
+	    {
+		property: 'name',
+		direction: 'ASC',
+	    },
+	],
+    },
+
+    allowBlank: false,
+    autoSelect: false,
+    displayField: 'id',
+    valueField: 'id',
+
+    listConfig: {
+	width: 800,
+	columns: [
+	    {
+		header: gettext('Name'),
+		dataIndex: 'id',
+		flex: 1,
+	    },
+	    {
+		header: gettext('Status'),
+		dataIndex: 'errors',
+		flex: 2,
+		renderer: function(value) {
+		    let me = this;
+
+		    if (!Ext.isArray(value) || !value?.length) {
+			return `<i class="fa fa-check-circle good"></i> ${gettext('Mapping matches host data')}`;
+		    }
+
+		    let errors = [];
+
+		    value.forEach((error) => {
+			let iconCls;
+			switch (error?.severity) {
+			    case 'warning':
+				iconCls = 'fa-exclamation-circle warning';
+				break;
+			    case 'error':
+				iconCls = 'fa-times-circle critical';
+				break;
+			}
+
+			let message = error?.message;
+			let icon = `<i class="fa ${iconCls}"></i>`;
+			if (iconCls !== undefined) {
+			    errors.push(`${icon} ${message}`);
+			}
+		    });
+
+		    return errors.join('<br>');
+		},
+	    },
+	    {
+		header: gettext('Comment'),
+		dataIndex: 'description',
+		flex: 1,
+		renderer: Ext.String.htmlEncode,
+	    },
+	],
+    },
+
+    setNodename: function(nodename) {
+	var me = this;
+
+	if (!nodename || me.nodename === nodename) {
+	    return;
+	}
+
+	me.nodename = nodename;
+
+	me.store.setProxy({
+	    type: 'proxmox',
+	    url: `/api2/json/cluster/mapping/hwrng?check-node=${nodename}`,
+	});
+
+	me.store.load();
+    },
+
+    initComponent: function() {
+	var me = this;
+
+	var nodename = me.nodename;
+	me.nodename = undefined;
+
+        me.callParent();
+
+	me.setNodename(nodename);
+    },
+});
diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js
index c6d193fc..8085e288 100644
--- a/www/manager6/qemu/HardwareView.js
+++ b/www/manager6/qemu/HardwareView.js
@@ -315,8 +315,8 @@ Ext.define('PVE.qemu.HardwareView', {
 	rows.rng0 = {
 	    group: 45,
 	    tdCls: 'pve-itype-icon-die',
-	    editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.RNGEdit' : undefined,
-	    never_delete: !caps.nodes['Sys.Console'],
+	    editor: caps.vms['VM.Config.HWType'] || caps.mapping['Mapping.Use'] ? 'PVE.qemu.RNGEdit' : undefined,
+	    never_delete: !caps.vms['VM.Config.HWType'] && !caps.mapping['Mapping.Use'],
 	    header: gettext("VirtIO RNG"),
 	};
 
@@ -588,7 +588,6 @@ Ext.define('PVE.qemu.HardwareView', {
 	    });
 
 	    // heuristic only for disabling some stuff, the backend has the final word.
-	    const noSysConsolePerm = !caps.nodes['Sys.Console'];
 	    const noHWPerm = !caps.nodes['Sys.Console'] && !caps.mapping['Mapping.Use'];
 	    const noVMConfigHWTypePerm = !caps.vms['VM.Config.HWType'];
 	    const noVMConfigNetPerm = !caps.vms['VM.Config.Network'];
@@ -601,7 +600,7 @@ Ext.define('PVE.qemu.HardwareView', {
 	    me.down('#addAudio').setDisabled(noVMConfigHWTypePerm || isAtLimit('audio'));
 	    me.down('#addSerial').setDisabled(noVMConfigHWTypePerm || isAtLimit('serial'));
 	    me.down('#addNet').setDisabled(noVMConfigNetPerm || isAtLimit('net'));
-	    me.down('#addRng').setDisabled(noSysConsolePerm || isAtLimit('rng'));
+	    me.down('#addRng').setDisabled(noVMConfigHWTypePerm || isAtLimit('rng'));
 	    efidisk_menuitem.setDisabled(noVMConfigDiskPerm || isAtLimit('efidisk'));
 	    me.down('#addTpmState').setDisabled(noVMConfigDiskPerm || isAtLimit('tpmstate'));
 	    me.down('#addCloudinitDrive').setDisabled(noVMConfigCDROMPerm || noVMConfigCloudinitPerm || hasCloudInit);
@@ -745,7 +744,7 @@ Ext.define('PVE.qemu.HardwareView', {
 				text: gettext("VirtIO RNG"),
 				itemId: 'addRng',
 				iconCls: 'pve-itype-icon-die',
-				disabled: !caps.nodes['Sys.Console'],
+				disabled: !caps.vms['VM.Config.HWType'] && !caps.mapping['Mapping.Use'],
 				handler: editorFactory('RNGEdit'),
 			    },
 			],
diff --git a/www/manager6/qemu/RNGEdit.js b/www/manager6/qemu/RNGEdit.js
index e34e2c08..fab8c1b0 100644
--- a/www/manager6/qemu/RNGEdit.js
+++ b/www/manager6/qemu/RNGEdit.js
@@ -1,9 +1,19 @@
 Ext.define('PVE.qemu.RNGInputPanel', {
     extend: 'Proxmox.panel.InputPanel',
     xtype: 'pveRNGInputPanel',
+    mixins: ['Proxmox.Mixin.CBind'],
 
     onlineHelp: 'qm_virtio_rng',
 
+    cbindData: function(initialConfig) {
+	let me = this;
+	if (!me.pveSelNode) {
+	    throw "no pveSelNode given";
+	}
+
+	return { nodename: me.pveSelNode.data.node };
+    },
+
     onGetValues: function(values) {
 	if (values.max_bytes === "") {
 	    values.max_bytes = "0";
@@ -23,6 +33,10 @@ Ext.define('PVE.qemu.RNGInputPanel', {
 	    values.max_bytes = null;
 	}
 
+	if (values.mapping) {
+	    values.source = 'mapped';
+	}
+
 	this.callParent(arguments);
     },
 
@@ -35,27 +49,49 @@ Ext.define('PVE.qemu.RNGInputPanel', {
 		    limitWarning.setHidden(!!newVal);
 		},
 	    },
-	    '#source': {
-		change: function(el, newVal) {
-		    let limitWarning = this.lookupReference('sourceWarning');
-		    limitWarning.setHidden(newVal !== '/dev/random');
-		},
-	    },
 	},
     },
 
     items: [{
-	itemId: 'source',
-	name: 'source',
-	xtype: 'proxmoxKVComboBox',
-	value: '/dev/urandom',
-	fieldLabel: gettext('Entropy source'),
-	labelWidth: 130,
-	comboItems: [
-	    ['/dev/urandom', '/dev/urandom'],
-	    ['/dev/random', '/dev/random'],
-	    ['/dev/hwrng', '/dev/hwrng'],
-	],
+	xtype: 'fieldcontainer',
+	defaultType: 'radiofield',
+	layout: 'fit',
+	items: [{
+	    name: 'source',
+	    inputValue: '/dev/urandom',
+	    boxLabel: '/dev/urandom',
+	    checked: true,
+	},
+	{
+	    name: 'source',
+	    inputValue: '/dev/random',
+	    boxLabel: '/dev/random',
+	},
+	{
+	    name: 'source',
+	    inputValue: 'mapped',
+	    boxLabel: gettext('Use mapped Hardware RNG device'),
+	    reference: 'mapped',
+	    submitValue: false,
+	    listeners: {
+		change: function(f, value) {
+		    let me = this;
+		    if (!me.rendered) {
+			return;
+		    }
+		    me.up().down('field[name=mapping]').setDisabled(!value);
+		},
+	    },
+	},
+	{
+	    xtype: 'pveHWRNGMapSelector',
+	    name: 'mapping',
+	    cbind: { nodename: '{nodename}' },
+	    allowBlank: false,
+	    fieldLabel: gettext('Choose Device'),
+	    labelAlign: 'right',
+	    disabled: true,
+	}],
     },
     {
 	xtype: 'numberfield',
@@ -77,13 +113,6 @@ Ext.define('PVE.qemu.RNGInputPanel', {
 	labelWidth: 130,
 	emptyText: '1000',
     },
-    {
-	xtype: 'displayfield',
-	reference: 'sourceWarning',
-	value: gettext('Using /dev/random as entropy source is discouraged, as it can lead to host entropy starvation. /dev/urandom is preferred, and does not lead to a decrease in security in practice.'),
-	userCls: 'pmx-hint',
-	hidden: true,
-    },
     {
 	xtype: 'displayfield',
 	reference: 'limitWarning',
@@ -95,11 +124,13 @@ Ext.define('PVE.qemu.RNGInputPanel', {
 
 Ext.define('PVE.qemu.RNGEdit', {
     extend: 'Proxmox.window.Edit',
+    mixins: ['Proxmox.Mixin.CBind'],
 
     subject: gettext('VirtIO RNG'),
 
     items: [{
 	xtype: 'pveRNGInputPanel',
+	cbind: { pveSelNode: '{pveSelNode}' },
     }],
 
     initComponent: function() {
-- 
2.39.5





More information about the pve-devel mailing list