mirror of
https://git.proxmox.com/git/qemu-server
synced 2025-05-02 18:59:41 +00:00
start adding permission checks
This commit is contained in:
parent
26ee04b6a0
commit
a0d1b1a2fe
311
PVE/API2/Qemu.pm
311
PVE/API2/Qemu.pm
@ -33,11 +33,132 @@ my $resolve_cdrom_alias = sub {
|
||||
}
|
||||
};
|
||||
|
||||
my $check_volume_access = sub {
|
||||
my ($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool) = @_;
|
||||
|
||||
my $path;
|
||||
if (my ($sid, $volname) = PVE::Storage::parse_volume_id($volid, 1)) {
|
||||
my ($ownervm, $vtype);
|
||||
($path, $ownervm, $vtype) = PVE::Storage::path($storecfg, $volid);
|
||||
if ($vtype eq 'iso' || $vtype eq 'vztmpl') {
|
||||
# we simply allow access
|
||||
} elsif (!$ownervm || ($ownervm != $vmid)) {
|
||||
# allow if we are Datastore administrator
|
||||
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $sid, [ 'Datastore.Allocate' ]);
|
||||
}
|
||||
} else {
|
||||
die "Only root can pass arbitrary filesystem paths."
|
||||
if $authuser ne 'root@pam';
|
||||
|
||||
$path = abs_path($volid);
|
||||
}
|
||||
return $path;
|
||||
};
|
||||
|
||||
# Note: $pool is only needed when creating a VM, because pool permissions
|
||||
# are automatically inherited if VM already exists inside a pool.
|
||||
my $create_disks = sub {
|
||||
my ($rpcenv, $authuser, $storecfg, $vmid, $pool, $settings, $conf, $default_storage) = @_;
|
||||
|
||||
# check permissions first
|
||||
|
||||
my $alloc = [];
|
||||
foreach_drive($settings, sub {
|
||||
my ($ds, $disk) = @_;
|
||||
|
||||
return if drive_is_cdrom($disk);
|
||||
|
||||
my $volid = $disk->{file};
|
||||
|
||||
if ($volid =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
|
||||
my ($storeid, $size) = ($2 || $default_storage, $3);
|
||||
die "no storage ID specified (and no default storage)\n" if !$storeid;
|
||||
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $storeid, [ 'Datastore.AllocateSpace' ]);
|
||||
my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
|
||||
my $fmt = $disk->{format} || $defformat;
|
||||
push @$alloc, [$ds, $disk, $storeid, $size, $fmt];
|
||||
} else {
|
||||
my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $volid, $pool);
|
||||
die "image '$path' does not exists\n" if (!(-f $path || -b $path));
|
||||
}
|
||||
});
|
||||
|
||||
# now try to allocate everything
|
||||
|
||||
my $vollist = [];
|
||||
eval {
|
||||
foreach my $task (@$alloc) {
|
||||
my ($ds, $disk, $storeid, $size, $fmt) = @$task;
|
||||
|
||||
my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
|
||||
$fmt, undef, $size*1024*1024);
|
||||
|
||||
$disk->{file} = $volid;
|
||||
push @$vollist, $volid;
|
||||
}
|
||||
};
|
||||
|
||||
# free allocated images on error
|
||||
if (my $err = $@) {
|
||||
syslog('err', "VM $vmid creating disks failed");
|
||||
foreach my $volid (@$vollist) {
|
||||
eval { PVE::Storage::vdisk_free($storecfg, $volid); };
|
||||
warn $@ if $@;
|
||||
}
|
||||
die $err;
|
||||
}
|
||||
|
||||
# modify vm config if everything went well
|
||||
foreach my $task (@$alloc) {
|
||||
my ($ds, $disk) = @$task;
|
||||
delete $disk->{format}; # no longer needed
|
||||
$settings->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
|
||||
}
|
||||
|
||||
return $vollist;
|
||||
};
|
||||
|
||||
my $check_vm_modify_config_perm = sub {
|
||||
my ($rpcenv, $authuser, $vmid, $pool, $param) = @_;
|
||||
|
||||
return 1 if $authuser ne 'root@pam';
|
||||
|
||||
foreach my $opt (keys %$param) {
|
||||
# disk checks need to be done somewhere else
|
||||
next if PVE::QemuServer::valid_drivename($opt);
|
||||
|
||||
if ($opt eq 'sockets' || $opt eq 'cores' ||
|
||||
$opt eq 'cpu' || $opt eq 'smp' ||
|
||||
$opt eq 'cpuimit' || $opt eq 'cpuunits') {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
|
||||
} elsif ($opt eq 'boot' || $opt eq 'bootdisk') {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
|
||||
} elsif ($opt eq 'memory' || $opt eq 'balloon') {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
|
||||
} elsif ($opt eq 'args' || $opt eq 'lock') {
|
||||
die "only root can set '$opt' config\n";
|
||||
} elsif ($opt eq 'cpu' || $opt eq 'kvm' || $opt eq 'acpi' ||
|
||||
$opt eq 'vga' || $opt eq 'watchdog' || $opt eq 'tablet') {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
|
||||
} elsif ($opt =~ m/^net\d+$/) {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
|
||||
} else {
|
||||
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'vmlist',
|
||||
path => '',
|
||||
method => 'GET',
|
||||
description => "Virtual machine index (per node).",
|
||||
permissions => {
|
||||
description => "Only list VMs where you have VM.Audit permissons on /vms/<vmid>.",
|
||||
user => 'all',
|
||||
},
|
||||
proxyto => 'node',
|
||||
protected => 1, # qemu pid files are only readable by root
|
||||
parameters => {
|
||||
@ -57,10 +178,21 @@ __PACKAGE__->register_method({
|
||||
code => sub {
|
||||
my ($param) = @_;
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $vmstatus = PVE::QemuServer::vmstatus();
|
||||
|
||||
return PVE::RESTHandler::hash_to_array($vmstatus, 'vmid');
|
||||
my $res = [];
|
||||
foreach my $vmid (keys %$vmstatus) {
|
||||
next if !$rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ], 1);
|
||||
|
||||
my $data = $vmstatus->{$vmid};
|
||||
$data->{vmid} = $vmid;
|
||||
push @$res, $data;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -68,6 +200,13 @@ __PACKAGE__->register_method({
|
||||
path => '',
|
||||
method => 'POST',
|
||||
description => "Create or restore a virtual machine.",
|
||||
permissions => {
|
||||
description => "You need 'VM.Allocate' permissions on /vms/{vmid} or on the VM pool /pool/{pool}. If you create disks you need 'Datastore.AllocateSpace' on any used storage.",
|
||||
check => [ 'or',
|
||||
[ 'perm', '/vms/{vmid}', ['VM.Allocate']],
|
||||
[ 'perm', '/pool/{pool}', ['VM.Allocate'], require_param => 'pool'],
|
||||
],
|
||||
},
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
parameters => {
|
||||
@ -98,6 +237,11 @@ __PACKAGE__->register_method({
|
||||
description => "Assign a unique random ethernet address.",
|
||||
requires => 'archive',
|
||||
},
|
||||
pool => {
|
||||
optional => 1,
|
||||
type => 'string', format => 'pve-poolid',
|
||||
description => "Add the VM to the specified pool.",
|
||||
},
|
||||
}),
|
||||
},
|
||||
returns => {
|
||||
@ -108,7 +252,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -121,6 +265,8 @@ __PACKAGE__->register_method({
|
||||
my $force = extract_param($param, 'force');
|
||||
|
||||
my $unique = extract_param($param, 'unique');
|
||||
|
||||
my $pool = extract_param($param, 'pool');
|
||||
|
||||
my $filename = PVE::QemuServer::config_file($vmid);
|
||||
|
||||
@ -128,6 +274,14 @@ __PACKAGE__->register_method({
|
||||
|
||||
PVE::Cluster::check_cfs_quorum();
|
||||
|
||||
if (defined($pool)) {
|
||||
$rpcenv->check_pool_exist($pool);
|
||||
$rpcenv->check_perm_modify($authuser, "/pool/$pool");
|
||||
}
|
||||
|
||||
$rpcenv->check_storage_perm($authuser, $vmid, $pool, $storage, [ 'Datastore.AllocateSpace' ])
|
||||
if defined($storage);
|
||||
|
||||
if (!$archive) {
|
||||
&$resolve_cdrom_alias($param);
|
||||
|
||||
@ -150,15 +304,7 @@ __PACKAGE__->register_method({
|
||||
die "pipe requires cli environment\n"
|
||||
&& $rpcenv->{type} ne 'cli';
|
||||
} else {
|
||||
my $path;
|
||||
if (PVE::Storage::parse_volume_id($archive, 1)) {
|
||||
$path = PVE::Storage::path($storecfg, $archive);
|
||||
} else {
|
||||
raise_param_exc({ archive => "Only root can pass arbitrary paths." })
|
||||
if $user ne 'root@pam';
|
||||
|
||||
$path = abs_path($archive);
|
||||
}
|
||||
my $path = &$check_volume_access($rpcenv, $authuser, $storecfg, $vmid, $archive, $pool);
|
||||
die "can't find archive file '$archive'\n" if !($path && -f $path);
|
||||
$archive = $path;
|
||||
}
|
||||
@ -178,14 +324,17 @@ __PACKAGE__->register_method({
|
||||
}
|
||||
|
||||
my $realcmd = sub {
|
||||
PVE::QemuServer::restore_archive($archive, $vmid, {
|
||||
PVE::QemuServer::restore_archive($archive, $vmid, $authuser, {
|
||||
storage => $storage,
|
||||
pool => $pool,
|
||||
unique => $unique });
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmrestore', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmrestore', $vmid, $authuser, $realcmd);
|
||||
};
|
||||
|
||||
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, $param);
|
||||
|
||||
my $createfn = sub {
|
||||
|
||||
# second test (after locking test is accurate)
|
||||
@ -197,7 +346,7 @@ __PACKAGE__->register_method({
|
||||
my $vollist = [];
|
||||
|
||||
eval {
|
||||
$vollist = PVE::QemuServer::create_disks($storecfg, $vmid, $param, $storage);
|
||||
$vollist = &$create_disks($rpcenv, $authuser, $storecfg, $vmid, $pool, $param, $storage);
|
||||
|
||||
# try to be smart about bootdisk
|
||||
my @disks = PVE::QemuServer::disknames();
|
||||
@ -226,7 +375,7 @@ __PACKAGE__->register_method({
|
||||
}
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmcreate', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmcreate', $vmid, $authuser, $realcmd);
|
||||
};
|
||||
|
||||
return PVE::QemuServer::lock_config($vmid, $archive ? $restorefn : $createfn);
|
||||
@ -238,6 +387,9 @@ __PACKAGE__->register_method({
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
description => "Directory index",
|
||||
permissions => {
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -366,6 +518,9 @@ __PACKAGE__->register_method({
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
description => "Get virtual machine configuration.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -390,6 +545,16 @@ __PACKAGE__->register_method({
|
||||
return $conf;
|
||||
}});
|
||||
|
||||
my $vm_config_perm_list = [
|
||||
'VM.Config.Disk',
|
||||
'VM.Config.CDROM',
|
||||
'VM.Config.CPU',
|
||||
'VM.Config.Memory',
|
||||
'VM.Config.Network',
|
||||
'VM.Config.HWType',
|
||||
'VM.Config.Options',
|
||||
];
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
name => 'update_vm',
|
||||
path => '{vmid}/config',
|
||||
@ -397,6 +562,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Set virtual machine options.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', $vm_config_perm_list, any => 1],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => PVE::QemuServer::json_config_properties(
|
||||
@ -429,7 +597,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -444,7 +612,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
my $delete = extract_param($param, 'delete');
|
||||
my $force = extract_param($param, 'force');
|
||||
@ -464,7 +632,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
PVE::QemuServer::check_lock($conf) if !$skiplock;
|
||||
|
||||
PVE::Cluster::log_msg('info', $user, "update VM $vmid: " . join (' ', @paramarr));
|
||||
PVE::Cluster::log_msg('info', $authuser, "update VM $vmid: " . join (' ', @paramarr));
|
||||
|
||||
#delete
|
||||
foreach my $opt (PVE::Tools::split_list($delete)) {
|
||||
@ -552,7 +720,7 @@ __PACKAGE__->register_method({
|
||||
}
|
||||
}
|
||||
my $settings = { $opt => $param->{$opt} };
|
||||
PVE::QemuServer::create_disks($storecfg, $vmid, $settings, $conf);
|
||||
&$create_disks($rpcenv, $authuser, $storecfg, $vmid, undef, $settings, $conf);
|
||||
$param->{$opt} = $settings->{$opt};
|
||||
#hotplug disks
|
||||
if(!PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive)) {
|
||||
@ -597,6 +765,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Destroy the vm (also delete all used/owned volumes).",
|
||||
permissions => {
|
||||
check => [ 'perm', '/vms/{vmid}', ['VM.Allocate']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -613,13 +784,13 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $vmid = $param->{vmid};
|
||||
|
||||
my $skiplock = $param->{skiplock};
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
# test if VM exists
|
||||
my $conf = PVE::QemuServer::load_config($vmid);
|
||||
@ -634,7 +805,7 @@ __PACKAGE__->register_method({
|
||||
PVE::QemuServer::vm_destroy($storecfg, $vmid, $skiplock);
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmdestroy', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmdestroy', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -644,6 +815,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Unlink/delete disk images.",
|
||||
permissions => {
|
||||
check => [ 'perm', '/vms/{vmid}', ['VM.Config.Disk']],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -704,14 +878,14 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $vmid = $param->{vmid};
|
||||
my $node = $param->{node};
|
||||
|
||||
my $authpath = "/vms/$vmid";
|
||||
|
||||
my $ticket = PVE::AccessControl::assemble_vnc_ticket($user, $authpath);
|
||||
my $ticket = PVE::AccessControl::assemble_vnc_ticket($authuser, $authpath);
|
||||
|
||||
$sslcert = PVE::Tools::file_get_contents("/etc/pve/pve-root-ca.pem", 8192)
|
||||
if !$sslcert;
|
||||
@ -748,10 +922,10 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $user, $realcmd);
|
||||
my $upid = $rpcenv->fork_worker('vncproxy', $vmid, $authuser, $realcmd);
|
||||
|
||||
return {
|
||||
user => $user,
|
||||
user => $authuser,
|
||||
ticket => $ticket,
|
||||
port => $port,
|
||||
upid => $upid,
|
||||
@ -765,6 +939,9 @@ __PACKAGE__->register_method({
|
||||
method => 'GET',
|
||||
proxyto => 'node',
|
||||
description => "Directory index",
|
||||
permissions => {
|
||||
user => 'all',
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -804,6 +981,9 @@ __PACKAGE__->register_method({
|
||||
proxyto => 'node',
|
||||
protected => 1, # qemu pid files are only readable by root
|
||||
description => "Get virtual machine status.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.Audit' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -838,6 +1018,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Start virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -855,7 +1038,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -863,11 +1046,11 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $stateuri = extract_param($param, 'stateuri');
|
||||
raise_param_exc({ stateuri => "Only root may use this option." })
|
||||
if $stateuri && $user ne 'root@pam';
|
||||
if $stateuri && $authuser ne 'root@pam';
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
my $storecfg = PVE::Storage::config();
|
||||
|
||||
@ -881,7 +1064,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmstart', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmstart', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -891,6 +1074,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Stop virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -919,7 +1105,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -927,11 +1113,11 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
my $keepActive = extract_param($param, 'keepActive');
|
||||
raise_param_exc({ keepActive => "Only root may use this option." })
|
||||
if $keepActive && $user ne 'root@pam';
|
||||
if $keepActive && $authuser ne 'root@pam';
|
||||
|
||||
my $storecfg = PVE::Storage::config();
|
||||
|
||||
@ -946,7 +1132,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmstop', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmstop', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -956,6 +1142,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Reset virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -972,7 +1161,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -980,7 +1169,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
|
||||
|
||||
@ -992,7 +1181,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmreset', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmreset', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -1002,6 +1191,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Shutdown virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -1036,7 +1228,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -1044,11 +1236,11 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
my $keepActive = extract_param($param, 'keepActive');
|
||||
raise_param_exc({ keepActive => "Only root may use this option." })
|
||||
if $keepActive && $user ne 'root@pam';
|
||||
if $keepActive && $authuser ne 'root@pam';
|
||||
|
||||
my $storecfg = PVE::Storage::config();
|
||||
|
||||
@ -1063,7 +1255,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmshutdown', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmshutdown', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -1073,6 +1265,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Suspend virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -1089,7 +1284,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -1097,7 +1292,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
|
||||
|
||||
@ -1111,7 +1306,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmsuspend', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmsuspend', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -1121,6 +1316,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Resume virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.PowerMgmt' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -1137,7 +1335,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -1145,7 +1343,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
die "VM $vmid not running\n" if !PVE::QemuServer::check_running($vmid);
|
||||
|
||||
@ -1159,7 +1357,7 @@ __PACKAGE__->register_method({
|
||||
return;
|
||||
};
|
||||
|
||||
return $rpcenv->fork_worker('qmresume', $vmid, $user, $realcmd);
|
||||
return $rpcenv->fork_worker('qmresume', $vmid, $authuser, $realcmd);
|
||||
}});
|
||||
|
||||
__PACKAGE__->register_method({
|
||||
@ -1169,6 +1367,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Send key event to virtual machine.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -1187,7 +1388,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $node = extract_param($param, 'node');
|
||||
|
||||
@ -1195,7 +1396,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $skiplock = extract_param($param, 'skiplock');
|
||||
raise_param_exc({ skiplock => "Only root may use this option." })
|
||||
if $skiplock && $user ne 'root@pam';
|
||||
if $skiplock && $authuser ne 'root@pam';
|
||||
|
||||
PVE::QemuServer::vm_sendkey($vmid, $skiplock, $param->{key});
|
||||
|
||||
@ -1209,6 +1410,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Migrate virtual machine. Creates a new migration task.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.Migrate' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
@ -1236,7 +1440,7 @@ __PACKAGE__->register_method({
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment::get();
|
||||
|
||||
my $user = $rpcenv->get_user();
|
||||
my $authuser = $rpcenv->get_user();
|
||||
|
||||
my $target = extract_param($param, 'target');
|
||||
|
||||
@ -1252,7 +1456,7 @@ __PACKAGE__->register_method({
|
||||
my $vmid = extract_param($param, 'vmid');
|
||||
|
||||
raise_param_exc({ force => "Only root may use this option." })
|
||||
if $param->{force} && $user ne 'root@pam';
|
||||
if $param->{force} && $authuser ne 'root@pam';
|
||||
|
||||
# test if VM exists
|
||||
my $conf = PVE::QemuServer::load_config($vmid);
|
||||
@ -1272,7 +1476,7 @@ __PACKAGE__->register_method({
|
||||
PVE::QemuMigrate->migrate($target, $targetip, $vmid, $param);
|
||||
};
|
||||
|
||||
my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $user, $realcmd);
|
||||
my $upid = $rpcenv->fork_worker('qmigrate', $vmid, $authuser, $realcmd);
|
||||
|
||||
return $upid;
|
||||
}});
|
||||
@ -1284,6 +1488,9 @@ __PACKAGE__->register_method({
|
||||
protected => 1,
|
||||
proxyto => 'node',
|
||||
description => "Execute Qemu monitor commands.",
|
||||
permissions => {
|
||||
check => ['perm', '/vms/{vmid}', [ 'VM.Monitor' ]],
|
||||
},
|
||||
parameters => {
|
||||
additionalProperties => 0,
|
||||
properties => {
|
||||
|
@ -1318,61 +1318,6 @@ sub touch_config {
|
||||
utime undef, undef, $conf;
|
||||
}
|
||||
|
||||
sub create_disks {
|
||||
my ($storecfg, $vmid, $settings, $conf, $default_storage) = @_;
|
||||
|
||||
my $vollist = [];
|
||||
|
||||
eval {
|
||||
foreach_drive($settings, sub {
|
||||
my ($ds, $disk) = @_;
|
||||
|
||||
return if drive_is_cdrom($disk);
|
||||
|
||||
my $file = $disk->{file};
|
||||
|
||||
if ($file =~ m/^(([^:\s]+):)?(\d+(\.\d+)?)$/) {
|
||||
my $storeid = $2 || $default_storage;
|
||||
my $size = $3;
|
||||
my $defformat = PVE::Storage::storage_default_format($storecfg, $storeid);
|
||||
my $fmt = $disk->{format} || $defformat;
|
||||
syslog('info', "VM $vmid creating new disk - size is $size GB");
|
||||
|
||||
my $volid = PVE::Storage::vdisk_alloc($storecfg, $storeid, $vmid,
|
||||
$fmt, undef, $size*1024*1024);
|
||||
|
||||
$disk->{file} = $volid;
|
||||
delete $disk->{format}; # no longer needed
|
||||
push @$vollist, $volid;
|
||||
$settings->{$ds} = PVE::QemuServer::print_drive($vmid, $disk);
|
||||
} else {
|
||||
my $path;
|
||||
if ($disk->{file} =~ m|^/dev/.+|) {
|
||||
$path = $disk->{file};
|
||||
} else {
|
||||
$path = PVE::Storage::path($storecfg, $disk->{file});
|
||||
}
|
||||
if (!(-f $path || -b $path)) {
|
||||
die "image '$path' does not exists\n";
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
my $err = $@;
|
||||
|
||||
if ($err) {
|
||||
syslog('err', "VM $vmid creating disks failed");
|
||||
foreach my $volid (@$vollist) {
|
||||
eval { PVE::Storage::vdisk_free($storecfg, $volid); };
|
||||
warn $@ if $@;
|
||||
}
|
||||
die $err;
|
||||
}
|
||||
|
||||
return $vollist;
|
||||
}
|
||||
|
||||
sub destroy_vm {
|
||||
my ($storecfg, $vmid, $keep_empty_config) = @_;
|
||||
|
||||
@ -3157,7 +3102,7 @@ sub restore_cleanup {
|
||||
}
|
||||
|
||||
sub restore_archive {
|
||||
my ($archive, $vmid, $opts) = @_;
|
||||
my ($archive, $vmid, $user, $opts) = @_;
|
||||
|
||||
if ($archive ne '-') {
|
||||
my $firstfile = archive_read_firstfile($archive);
|
||||
@ -3168,10 +3113,11 @@ sub restore_archive {
|
||||
my $tocmd = "/usr/lib/qemu-server/qmextract";
|
||||
|
||||
$tocmd .= " --storage " . PVE::Tools::shellquote($opts->{storage}) if $opts->{storage};
|
||||
$tocmd .= " --pool " . PVE::Tools::shellquote($opts->{pool}) if $opts->{pool};
|
||||
$tocmd .= ' --prealloc' if $opts->{prealloc};
|
||||
$tocmd .= ' --info' if $opts->{info};
|
||||
|
||||
# tar option "xf" does not autodetect compression when read fron STDIN,
|
||||
# tar option "xf" does not autodetect compression when read from STDIN,
|
||||
# so we pipe to zcat
|
||||
my $cmd = "zcat -f|tar xf " . PVE::Tools::shellquote($archive) . " " .
|
||||
PVE::Tools::shellquote("--to-command=$tocmd");
|
||||
@ -3181,6 +3127,7 @@ sub restore_archive {
|
||||
|
||||
local $ENV{VZDUMP_TMPDIR} = $tmpdir;
|
||||
local $ENV{VZDUMP_VMID} = $vmid;
|
||||
local $ENV{VZDUMP_USER} = $user;
|
||||
|
||||
my $conffile = PVE::QemuServer::config_file($vmid);
|
||||
my $tmpfn = "$conffile.$$.tmp";
|
||||
|
44
qmextract
44
qmextract
@ -4,15 +4,21 @@ use strict;
|
||||
use Getopt::Long;
|
||||
use File::Path;
|
||||
use IO::File;
|
||||
use PVE::INotify;
|
||||
use PVE::JSONSchema;
|
||||
use PVE::Tools;
|
||||
use PVE::Cluster qw(cfs_read_file);
|
||||
use PVE::RPCEnvironment;
|
||||
use PVE::QemuServer;
|
||||
|
||||
my @std_opts = ('storage=s', 'info', 'prealloc');
|
||||
$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
|
||||
|
||||
die "please run as root\n" if $> != 0;
|
||||
|
||||
my @std_opts = ('storage=s', 'pool=s', 'info', 'prealloc');
|
||||
|
||||
sub print_usage {
|
||||
print STDERR "usage: $0 [--storage=<storeid>] [--info] [--prealloc] <archive> <vmid>\n\n";
|
||||
print STDERR "usage: $0 [--storage=<storeid>] [--pool=<poolid>] [--info] [--prealloc] <archive> <vmid>\n\n";
|
||||
}
|
||||
|
||||
my $opts = {};
|
||||
@ -21,6 +27,14 @@ if (!GetOptions ($opts, @std_opts)) {
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
PVE::INotify::inotify_init();
|
||||
|
||||
my $rpcenv = PVE::RPCEnvironment->init('cli');
|
||||
|
||||
$rpcenv->init_request();
|
||||
$rpcenv->set_language($ENV{LANG});
|
||||
$rpcenv->set_user('root@pam');
|
||||
|
||||
sub extract_archive {
|
||||
# NOTE: this is run as tar subprocess (--to-command)
|
||||
|
||||
@ -43,6 +57,9 @@ sub extract_archive {
|
||||
my $vmid = $ENV{VZDUMP_VMID};
|
||||
PVE::JSONSchema::pve_verify_vmid($vmid);
|
||||
|
||||
my $user = $ENV{VZDUMP_USER};
|
||||
$rpcenv->check_user_enabled($user);
|
||||
|
||||
if ($opts->{info}) {
|
||||
print STDERR "reading archive member '$filename'\n";
|
||||
} else {
|
||||
@ -96,7 +113,7 @@ sub extract_archive {
|
||||
print STDERR "restoring old style vzdump archive - " .
|
||||
"no device map inside archive\n";
|
||||
die "can't restore old style archive to storage '$opts->{storage}'\n"
|
||||
if $opts->{storage} && $opts->{storage} ne 'local';
|
||||
if defined($opts->{storage}) && $opts->{storage} ne 'local';
|
||||
|
||||
my $dir = "/var/lib/vz/images/$vmid";
|
||||
mkpath $dir;
|
||||
@ -120,9 +137,24 @@ sub extract_archive {
|
||||
"($filesize != $info->{size})\n";
|
||||
}
|
||||
|
||||
# check permission for all used storages
|
||||
my $pool = $opts->{pool};
|
||||
if ($user ne 'root@pam') {
|
||||
if (defined($opts->{storage})) {
|
||||
my $sid = $opts->{storage} || 'local';
|
||||
$rpcenv->check_storage_perm($user, $vmid, $pool, $sid, ['Datastore.AllocateSpace']);
|
||||
} else {
|
||||
foreach my $fn (keys %$map) {
|
||||
my $fi = $map->{$fn};
|
||||
my $sid = $fi->{storeid} || 'local';
|
||||
$rpcenv->check_storage_perm($user, $vmid, $pool, $sid, ['Datastore.AllocateSpace']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $storeid;
|
||||
if ($opts->{storage}) {
|
||||
$storeid = $opts->{storage};
|
||||
if (defined($opts->{storage})) {
|
||||
$storeid = $opts->{storage} || 'local';
|
||||
} else {
|
||||
$storeid = $info->{storeid} || 'local';
|
||||
}
|
||||
@ -172,7 +204,7 @@ if (scalar(@ARGV) == 2) {
|
||||
|
||||
PVE::Cluster::check_cfs_quorum();
|
||||
|
||||
PVE::QemuServer::restore_archive($archive, $vmid, $opts);
|
||||
PVE::QemuServer::restore_archive($archive, $vmid, 'root@pam', $opts);
|
||||
|
||||
} elsif (scalar(@ARGV) == 0 && $ENV{TAR_FILENAME}) {
|
||||
extract_archive();
|
||||
|
Loading…
Reference in New Issue
Block a user