mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-08-05 12:46:25 +00:00
vncproxy: allow to request a generated VNC password
We used the VNC API $ticket as password for VNC, but QEMU limits the password to the first 8 chars and ignores the rest[0]. As our tickets start with a static string (e.g., "PVE") the entropy was a bit limited. For Proxmox VE this does not matters much as the noVNC viewer provided by has to go always over the API call, and so a valid ticket and correct permissions for the requested VM are enforced anyway. This patch helps external users, which often use NoVNC-Websockify, circumventing the API and relying solely on the VNC password to avoid snooping on VNC sessions. A 'generate-password' parameter is added, if set a password from good entropy (using libopenssl) is generated. For simplicity of mapping random bits to ranges we extract 6 bit of entropy per character and add the integer value of '!' (first printable ASCII char) to that. This way we get 64^8 possibilities, which even with millions of guesses per second one would need years of guessing and mostly just DDOS the server with websocket upgrade requests. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> Tested-By: Dominik Csapak <d.csapak@proxmox.com> Reviewed-By: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
1b7824d349
commit
3c5bdde815
@ -7,6 +7,7 @@ use Net::SSLeay;
|
|||||||
use POSIX;
|
use POSIX;
|
||||||
use IO::Socket::IP;
|
use IO::Socket::IP;
|
||||||
use URI::Escape;
|
use URI::Escape;
|
||||||
|
use Crypt::OpenSSL::Random;
|
||||||
|
|
||||||
use PVE::Cluster qw (cfs_read_file cfs_write_file);;
|
use PVE::Cluster qw (cfs_read_file cfs_write_file);;
|
||||||
use PVE::RRD;
|
use PVE::RRD;
|
||||||
@ -1589,6 +1590,19 @@ __PACKAGE__->register_method({
|
|||||||
return undef;
|
return undef;
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
# uses good entropy, each char is limited to 6 bit to get printable chars simply
|
||||||
|
my $gen_rand_chars = sub {
|
||||||
|
my ($length) = @_;
|
||||||
|
|
||||||
|
die "invalid length $length" if $length < 1;
|
||||||
|
|
||||||
|
my $min = ord('!'); # first printable ascii
|
||||||
|
my @rand_bytes = split '', Crypt::OpenSSL::Random::random_bytes($length);
|
||||||
|
my $str = join('', map { chr((ord($_) & 0x3F) + $min) } @rand_bytes);
|
||||||
|
|
||||||
|
return $str;
|
||||||
|
};
|
||||||
|
|
||||||
my $sslcert;
|
my $sslcert;
|
||||||
|
|
||||||
__PACKAGE__->register_method({
|
__PACKAGE__->register_method({
|
||||||
@ -1610,6 +1624,12 @@ __PACKAGE__->register_method({
|
|||||||
type => 'boolean',
|
type => 'boolean',
|
||||||
description => "starts websockify instead of vncproxy",
|
description => "starts websockify instead of vncproxy",
|
||||||
},
|
},
|
||||||
|
'generate-password' => {
|
||||||
|
optional => 1,
|
||||||
|
type => 'boolean',
|
||||||
|
default => 0,
|
||||||
|
description => "Generates a random password to be used as ticket instead of the API ticket.",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
returns => {
|
returns => {
|
||||||
@ -1617,6 +1637,12 @@ __PACKAGE__->register_method({
|
|||||||
properties => {
|
properties => {
|
||||||
user => { type => 'string' },
|
user => { type => 'string' },
|
||||||
ticket => { type => 'string' },
|
ticket => { type => 'string' },
|
||||||
|
password => {
|
||||||
|
optional => 1,
|
||||||
|
description => "Returned if requested with 'generate-password' param."
|
||||||
|
." Consists of printable ASCII characters ('!' .. '~').",
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
cert => { type => 'string' },
|
cert => { type => 'string' },
|
||||||
port => { type => 'integer' },
|
port => { type => 'integer' },
|
||||||
upid => { type => 'string' },
|
upid => { type => 'string' },
|
||||||
@ -1644,6 +1670,10 @@ __PACKAGE__->register_method({
|
|||||||
my $authpath = "/vms/$vmid";
|
my $authpath = "/vms/$vmid";
|
||||||
|
|
||||||
my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
|
my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
|
||||||
|
my $password = $ticket;
|
||||||
|
if ($param->{'generate-password'}) {
|
||||||
|
$password = $gen_rand_chars->(8);
|
||||||
|
}
|
||||||
|
|
||||||
$sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
|
$sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
|
||||||
if !$sslcert;
|
if !$sslcert;
|
||||||
@ -1680,7 +1710,7 @@ __PACKAGE__->register_method({
|
|||||||
'-perm', 'Sys.Console'];
|
'-perm', 'Sys.Console'];
|
||||||
|
|
||||||
if ($param->{websocket}) {
|
if ($param->{websocket}) {
|
||||||
$ENV{PVE_VNC_TICKET} = $ticket; # pass ticket to vncterm
|
$ENV{PVE_VNC_TICKET} = $password; # pass ticket to vncterm
|
||||||
push @$cmd, '-notls', '-listen', 'localhost';
|
push @$cmd, '-notls', '-listen', 'localhost';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1690,7 +1720,7 @@ __PACKAGE__->register_method({
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
$ENV{LC_PVE_TICKET} = $ticket if $websocket; # set ticket with "qm vncproxy"
|
$ENV{LC_PVE_TICKET} = $password if $websocket; # set ticket with "qm vncproxy"
|
||||||
|
|
||||||
$cmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
|
$cmd = [@$remcmd, "/usr/sbin/qm", 'vncproxy', $vmid];
|
||||||
|
|
||||||
@ -1725,13 +1755,16 @@ __PACKAGE__->register_method({
|
|||||||
|
|
||||||
PVE::Tools::wait_for_vnc_port($port);
|
PVE::Tools::wait_for_vnc_port($port);
|
||||||
|
|
||||||
return {
|
my $res = {
|
||||||
user => $authuser,
|
user => $authuser,
|
||||||
ticket => $ticket,
|
ticket => $ticket,
|
||||||
port => $port,
|
port => $port,
|
||||||
upid => $upid,
|
upid => $upid,
|
||||||
cert => $sslcert,
|
cert => $sslcert,
|
||||||
};
|
};
|
||||||
|
$res->{password} = $password if $param->{'generate-password'};
|
||||||
|
|
||||||
|
return $res;
|
||||||
}});
|
}});
|
||||||
|
|
||||||
__PACKAGE__->register_method({
|
__PACKAGE__->register_method({
|
||||||
|
Loading…
Reference in New Issue
Block a user