[PATCH proxmox-backup v2 20/27] ui: expose assigning encryption key to sync jobs

Christian Ebner
2026-04-10T16:55:35Z

This allows to select pre-defined encryption keys and assign them to
the sync job configuration.

Sync keys can be either assigned as active encryption key to sync
jobs in push direction, to be used when pushing new contents or
associated to a sync job in pull direction, then used to decrypt
contents with matching key fingerprint.

As active encryption key only ones with are not archived can be used,
while associations can be made also with archived keys, to still be
able do decrypt contents on pull and to avoid key deletion if
associated to either push or pull sync jobs.

Signed-off-by: Christian Ebner <c.ebner@proxmox.com>
---
www/Makefile | 1 +
www/form/EncryptionKeySelector.js | 96 +++++++++++++++++++++++++++++++
www/window/SyncJobEdit.js | 30 ++++++++++
3 files changed, 127 insertions(+)
create mode 100644 www/form/EncryptionKeySelector.js

diff --git a/www/Makefile b/www/Makefile
index 08ad50846..51da9d74e 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -55,6 +55,7 @@ JSSRC= \
form/GroupSelector.js \
form/GroupFilter.js \
form/VerifyOutdatedAfter.js \
+ form/EncryptionKeySelector.js \
data/RunningTasksStore.js \
button/TaskButton.js \
panel/PrunePanel.js \
diff --git a/www/form/EncryptionKeySelector.js b/www/form/EncryptionKeySelector.js
new file mode 100644
index 000000000..e0390e56a
--- /dev/null
+++ b/www/form/EncryptionKeySelector.js
@@ -0,0 +1,96 @@
+Ext.define('PBS.form.EncryptionKeySelector', {
+ extend: 'Ext.form.field.ComboBox',
+ alias: 'widget.pbsEncryptionKeySelector',
+
+ queryMode: 'local',
+
+ valueField: 'id',
+ displayField: 'id',
+
+ emptyText: gettext('None'),
+
+ listConfig: {
+ columns: [
+ {
+ dataIndex: 'id',
+ header: gettext('Key ID'),
+ sortable: true,
+ flex: 1,
+ },
+ {
+ dataIndex: 'created',
+ header: gettext('Created'),
+ sortable: true,
+ renderer: Proxmox.Utils.render_timestamp,
+ flex: 1,
+ },
+ {
+ dataIndex: 'archived-at',
+ header: gettext('Archived'),
+ renderer: (val) => (val ? Proxmox.Utils.render_timestamp(val) : ''),
+ sortable: true,
+ flex: 1,
+ },
+ ],
+ emptyText: ` ${gettext('No key accessible.')} `,
+ },
+
+ config: {
+ deleteEmpty: true,
+ extraRequestParams: {},
+ },
+ // override framework function to implement deleteEmpty behaviour
+ getSubmitData: function () {
+ let me = this;
+
+ let data = null;
+ if (!me.disabled && me.submitValue) {
+ let val = me.getSubmitValue();
+ if (val !== null && val !== '') {
+ data = {};
+ data[me.getName()] = val;
+ } else if (me.getDeleteEmpty()) {
+ data = {};
+ data.delete = me.getName();
+ }
+ }
+
+ return data;
+ },
+
+ triggers: {
+ clear: {
+ cls: 'pmx-clear-trigger',
+ weight: -1,
+ hidden: true,
+ handler: function () {
+ this.triggers.clear.setVisible(false);
+ this.setValue('');
+ },
+ },
+ },
+
+ listeners: {
+ change: function (field, value) {
+ let canClear = (value ?? '') !== '';
+ field.triggers.clear.setVisible(canClear);
+ },
+ },
+
+ initComponent: function () {
+ let me = this;
+
+ me.store = Ext.create('Ext.data.Store', {
+ model: 'pbs-encryption-keys',
+ autoLoad: true,
+ proxy: {
+ type: 'proxmox',
+ timeout: 30 * 1000,
+ url: `/api2/json/config/encryption-keys`,
+ extraParams: me.extraRequestParams,
+ },
+ });
+
+ me.callParent();
+ },
+});
diff --git a/www/window/SyncJobEdit.js b/www/window/SyncJobEdit.js
index 074c7855a..9994f14e8 100644
--- a/www/window/SyncJobEdit.js
+++ b/www/window/SyncJobEdit.js
@@ -34,6 +34,7 @@ Ext.define('PBS.window.SyncJobEdit', {
if (me.syncDirection === 'push') {
me.subject = gettext('Sync Job - Push Direction');
me.syncDirectionPush = true;
+ me.syncCryptKeyMultiSelect = false;
me.syncRemoteLabel = gettext('Target Remote');
me.syncRemoteDatastore = gettext('Target Datastore');
me.syncRemoteNamespace = gettext('Target Namespace');
@@ -560,6 +561,35 @@ Ext.define('PBS.window.SyncJobEdit', {
},
],
},
+ {
+ xtype: 'inputpanel',
+ title: gettext('Encryption'),
+ column1: [
+ {
+ xtype: 'pbsEncryptionKeySelector',
+ name: 'active-encryption-key',
+ fieldLabel: gettext('Active Encryption Key'),
+ multiSelect: false,
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ disabled: '{!syncDirectionPush}',
+ hidden: '{!syncDirectionPush}',
+ },
+ },
+ {
+ xtype: 'pbsEncryptionKeySelector',
+ name: 'associated-key',
+ fieldLabel: gettext('Associated Keys'),
+ multiSelect: true,
+ cbind: {
+ deleteEmpty: '{!isCreate}',
+ },
+ extraRequestParams: {
+ 'include-archived': true,
+ },
+ },
+ ],
+ },
],
},
});
--
2.47.3