ui: storage/PBS: allow to download/print new encryption key

note that the key is really important, add our recommended backup
strategy (copy to PW manager, save onto secured USB drive, print
paperkey and secure it) with the respective buttons to do so.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2020-11-15 14:54:19 +01:00
parent 71dec88f99
commit d1a7c6ee15

View File

@ -1,3 +1,4 @@
/*global QRCode*/
Ext.define('Proxmox.form.PBSEncryptionCheckbox', { Ext.define('Proxmox.form.PBSEncryptionCheckbox', {
extend: 'Ext.form.field.Checkbox', extend: 'Ext.form.field.Checkbox',
xtype: 'pbsEncryptionCheckbox', xtype: 'pbsEncryptionCheckbox',
@ -60,11 +61,203 @@ Ext.define('Proxmox.form.PBSEncryptionCheckbox', {
vm.set('isCreate', me.isCreate); vm.set('isCreate', me.isCreate);
}, },
}); });
Ext.define('PVE.Storage.PBSKeyShow', {
extend: 'Ext.window.Window',
alias: ['widget.pveKeyShow'],
mixins: ['Proxmox.Mixin.CBind'],
width: 600,
modal: true,
resizable: false,
title: gettext('Important: Save your Encryption Key'),
// avoid that esc closes this by mistake, force user to more manual action
onEsc: Ext.emptyFn,
closable: false,
items: [
{
xtype: 'form',
layout: {
type: 'vbox',
align: 'stretch',
},
bodyPadding: 10,
border: false,
defaults: {
anchor: '100%',
border: false,
padding: '10 0 0 0',
},
items: [
{
xtype: 'textfield',
fieldLabel: gettext('Key'),
labelWidth: 30,
inputId: 'encryption-key-value',
cbind: {
value: '{key}',
},
editable: false,
},
{
xtype: 'component',
html: gettext('Keep your master key safe, but easily accessible for disaster recovery.')
+ '<br>' + gettext('We recommend the following safe-keeping strategy:'),
},
{
xtyp: 'container',
layout: 'hbox',
items: [
{
xtype: 'component',
html: '1. ' + gettext('Save the key in your password manager.'),
flex: 1,
},
{
xtype: 'button',
text: gettext('Copy Key'),
iconCls: 'fa fa-clipboard x-btn-icon-el-default-toolbar-small',
cls: 'x-btn-default-toolbar-small proxmox-inline-button',
width: 110,
handler: function(b) {
document.getElementById('encryption-key-value').select();
document.execCommand("copy");
},
},
],
},
{
xtype: 'container',
layout: 'hbox',
items: [
{
xtype: 'component',
html: '2. ' + gettext('Download the key to a USB (pen) drive, placed in secure vault.'),
flex: 1,
},
{
xtype: 'button',
text: gettext('Download'),
iconCls: 'fa fa-download x-btn-icon-el-default-toolbar-small',
cls: 'x-btn-default-toolbar-small proxmox-inline-button',
width: 110,
handler: function(b) {
let win = this.up('window');
let pveID = PVE.ClusterName || window.location.hostname;
let name = `pve-${pveID}-storage-${win.sid}.enc`;
let hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/text,' + encodeURI(win.key);
hiddenElement.target = '_blank';
hiddenElement.download = name;
hiddenElement.click();
},
},
],
},
{
xtype: 'container',
layout: 'hbox',
items: [
{
xtype: 'component',
html: '3. ' + gettext('Print as paperkey, laminated and placed in secure vault.'),
flex: 1,
},
{
xtype: 'button',
text: gettext('Print Key'),
iconCls: 'fa fa-print x-btn-icon-el-default-toolbar-small',
cls: 'x-btn-default-toolbar-small proxmox-inline-button',
width: 110,
handler: function(b) {
let win = this.up('window');
win.paperkey(win.key);
},
},
],
},
],
},
{
xtype: 'component',
border: false,
padding: '10 10 10 10',
userCls: 'pmx-hint',
html: gettext('Please save the encryption key - loosing it will render any backup created with it unuseable'),
},
],
buttons: [
{
text: gettext('Close'),
handler: function(b) {
let win = this.up('window');
win.close();
},
},
],
paperkey: function(key) {
let me = this;
const qrwidth = 500;
let qrdiv = document.createElement('div');
let qrcode = new QRCode(qrdiv, {
width: qrwidth,
height: qrwidth,
correctLevel: QRCode.CorrectLevel.H,
});
qrcode.makeCode(key);
let printFrame = document.createElement("iframe");
Object.assign(printFrame.style, {
position: "fixed",
right: "0",
bottom: "0",
width: "0",
height: "0",
border: "0",
});
const prettifiedKey = JSON.stringify(JSON.parse(key), null, 2);
const keyQrBase64 = qrdiv.children[0].toDataURL("image/png");
const html = `<html><head><script>
window.addEventListener('DOMContentLoaded', (ev) => window.print());
</script><style>@media print and (max-height: 150mm) {
h4, p { margin: 0; font-size: 1em; }
}</style></head><body style="padding: 5px;">
<h4>Encryption Key - Storage '${me.sid}'</h4>
<p style="font-size: 1.2em; font-family: monospace; white-space: pre-wrap;">
-----BEGIN PROXMOX BACKUP KEY-----
${prettifiedKey}
-----END PROXMOX BACKUP KEY-----</p>
<center><img style="width: 100%; max-width: ${qrwidth}px;" src="${keyQrBase64}"></center>
</body></html>`;
printFrame.src = "data:text/html;base64," + btoa(html);
document.body.appendChild(printFrame);
},
});
Ext.define('PVE.storage.PBSInputPanel', { Ext.define('PVE.storage.PBSInputPanel', {
extend: 'PVE.panel.StorageBase', extend: 'PVE.panel.StorageBase',
//onlineHelp: 'storage_pbs', //onlineHelp: 'storage_pbs',
apiCallDone: function(success, response, options) {
let res = response.result.data;
if (!(res && res.config && res.config['encryption-key'])) {
return;
}
let key = res.config['encryption-key'];
Ext.create('PVE.Storage.PBSKeyShow', {
autoShow: true,
sid: res.storage,
key: key,
});
},
initComponent: function() { initComponent: function() {
var me = this; var me = this;