]> git.proxmox.com Git - pve-container.git/commitdiff
mountpoints: create parent dirs with correct owner
authorFabian Grünbichler <f.gruenbichler@proxmox.com>
Wed, 24 Jul 2019 11:37:13 +0000 (13:37 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 14 Nov 2019 17:34:51 +0000 (18:34 +0100)
otherwise unprivileged containers might end up with directories that
they cannot modify since they are owned by the user root in the host
namespace, instead of root inside the container.

note: the problematic behaviour is only exhibited when an intermediate
directory needs to be created, e.g. a mountpoint /test/mp gets mounted,
and /test does not yet exist.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
(cherry picked from commit 4c98d66ce75d83185f1ec99e170e6f950f0c68f5)
[ Thomas: backport followup fix: locally define AT_EMPTY_PATH ]
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
src/PVE/LXC.pm
src/PVE/VZDump/LXC.pm
src/lxc-pve-prestart-hook

index 4922fb00188ac0565a5819b847041df2b8eff828..af52a4c0897d32cc67b21395cca14262092b7e3c 100644 (file)
@@ -1177,13 +1177,15 @@ sub mount_all {
     my $volid_list = PVE::LXC::Config->get_vm_volumes($conf);
     PVE::Storage::activate_volumes($storage_cfg, $volid_list);
 
+    my (undef, $rootuid, $rootgid) = parse_id_maps($conf);
+
     eval {
        PVE::LXC::Config->foreach_mountpoint($conf, sub {
            my ($ms, $mountpoint) = @_;
 
            $mountpoint->{ro} = 0 if $ignore_ro;
 
-           mountpoint_mount($mountpoint, $rootdir, $storage_cfg);
+           mountpoint_mount($mountpoint, $rootdir, $storage_cfg, undef, $rootuid, $rootgid);
         });
     };
     if (my $err = $@) {
@@ -1258,8 +1260,8 @@ sub run_with_loopdev {
 #   * the 2nd deepest directory (parent of the above)
 #   * directory name of the last directory
 # So that the path $2/$3 should lead to $1 afterwards.
-sub walk_tree_nofollow($$$) {
-    my ($start, $subdir, $mkdir) = @_;
+sub walk_tree_nofollow($$$;$$) {
+    my ($start, $subdir, $mkdir, $rootuid, $rootgid) = @_;
 
     # splitdir() returns '' for empty components including the leading /
     my @comps = grep { length($_)>0 } File::Spec->splitdir($subdir);
@@ -1288,6 +1290,10 @@ sub walk_tree_nofollow($$$) {
 
            $next = PVE::Tools::openat(fileno($fd), $component, O_NOFOLLOW | O_DIRECTORY);
            die "failed to create path: $dir: $!\n" if !$next;
+
+           use constant AT_EMPTY_PATH => 0x1000A;
+           PVE::Tools::fchownat(fileno($next), '', $rootuid, $rootgid, AT_EMPTY_PATH)
+               if defined($rootuid) && defined($rootgid);
        }
 
        close $second if defined($last_component);
@@ -1381,17 +1387,17 @@ sub bindmount {
 # from $rootdir and $mount and walk the path from $rootdir to the final
 # directory to check for symlinks.
 sub __mount_prepare_rootdir {
-    my ($rootdir, $mount) = @_;
+    my ($rootdir, $mount, $rootuid, $rootgid) = @_;
     $rootdir =~ s!/+!/!g;
     $rootdir =~ s!/+$!!;
     my $mount_path = "$rootdir/$mount";
-    my ($mpfd, $parentfd, $last_dir) = walk_tree_nofollow($rootdir, $mount, 1);
+    my ($mpfd, $parentfd, $last_dir) = walk_tree_nofollow($rootdir, $mount, 1, $rootuid, $rootgid);
     return ($rootdir, $mount_path, $mpfd, $parentfd, $last_dir);
 }
 
 # use $rootdir = undef to just return the corresponding mount path
 sub mountpoint_mount {
-    my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
+    my ($mountpoint, $rootdir, $storage_cfg, $snapname, $rootuid, $rootgid) = @_;
 
     my $volid = $mountpoint->{volume};
     my $mount = $mountpoint->{mp};
@@ -1408,7 +1414,7 @@ sub mountpoint_mount {
     
     if (defined($rootdir)) {
        ($rootdir, $mount_path, $mpfd, $parentfd, $last_dir) =
-           __mount_prepare_rootdir($rootdir, $mount);
+           __mount_prepare_rootdir($rootdir, $mount, $rootuid, $rootgid);
     }
     
     my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
@@ -1999,7 +2005,7 @@ sub run_unshared {
 }
 
 my $copy_volume = sub {
-    my ($src_volid, $src, $dst_volid, $dest, $storage_cfg, $snapname, $bwlimit) = @_;
+    my ($src_volid, $src, $dst_volid, $dest, $storage_cfg, $snapname, $bwlimit, $rootuid,  $rootgid) = @_;
 
     my $src_mp = { volume => $src_volid, mp => '/', ro => 1 };
     $src_mp->{type} = PVE::LXC::Config->classify_mountpoint($src_volid);
@@ -2011,10 +2017,10 @@ my $copy_volume = sub {
     eval {
        # mount and copy
        mkdir $src;
-       mountpoint_mount($src_mp, $src, $storage_cfg, $snapname);
+       mountpoint_mount($src_mp, $src, $storage_cfg, $snapname, $rootuid, $rootgid);
        push @mounted, $src;
        mkdir $dest;
-       mountpoint_mount($dst_mp, $dest, $storage_cfg);
+       mountpoint_mount($dst_mp, $dest, $storage_cfg, undef, $rootuid, $rootgid);
        push @mounted, $dest;
 
        $bwlimit //= 0;
@@ -2070,7 +2076,7 @@ sub copy_volume {
        }
 
        run_unshared(sub {
-           $copy_volume->($mp->{volume}, $src, $new_volid, $dest, $storage_cfg, $snapname, $bwlimit);
+           $copy_volume->($mp->{volume}, $src, $new_volid, $dest, $storage_cfg, $snapname, $bwlimit, $rootuid, $rootgid);
        });
     };
     if (my $err = $@) {
index 4ebfc1e173f8f2dd69e6b3c0556c7b32463b97ed..ad5ecc8de4e6d553c905834eaf3854acbebbf347 100644 (file)
@@ -114,6 +114,9 @@ sub prepare {
 
     my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
     $task->{userns_cmd} = PVE::LXC::userns_command($id_map);
+    $task->{rootuid} = $rootuid;
+    $task->{rootgid} = $rootgid;
+
 
     my $volids = $task->{volids} = [];
     PVE::LXC::Config->foreach_mountpoint($conf, sub {
@@ -219,7 +222,7 @@ sub snapshot {
     PVE::Storage::activate_volumes($storage_cfg, $volids, 'vzdump');
     foreach my $disk (@$disks) {
        $disk->{dir} = "${rootdir}$disk->{mp}";
-       PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, 'vzdump');
+       PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, 'vzdump', $task->{rootuid}, $task->{rootgid});
     }
 
     $task->{snapdir} = $rootdir;
@@ -309,7 +312,7 @@ sub archive {
        my $storage_cfg = $self->{storecfg};
        foreach my $disk (@$disks) {
            $disk->{dir} = "${rootdir}$disk->{mp}";
-           PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg);
+           PVE::LXC::mountpoint_mount($disk, $rootdir, $storage_cfg, undef, $task->{rootuid}, $task->{rootgid});
            # add every enabled mountpoint (since we use --one-file-system)
            # mp already starts with a / so we only need to add the dot
            push @sources, ".$disk->{mp}";
index 3ff8d7997a660988a0be1b05eb49315084314c19..18b60cf5533d39460cdd1913aa9b44dc3aa9ee4f 100755 (executable)
@@ -85,11 +85,13 @@ __PACKAGE__->register_method ({
        unlink $devlist_file;
        my $devices = [];
 
+       my (undef, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+
        my $setup_mountpoint = sub {
            my ($ms, $mountpoint) = @_;
 
            #return if $ms eq 'rootfs';
-           my (undef, undef, $dev) = PVE::LXC::mountpoint_mount($mountpoint, $rootdir, $storage_cfg);
+           my (undef, undef, $dev) = PVE::LXC::mountpoint_mount($mountpoint, $rootdir, $storage_cfg, undef, $rootuid, $rootgid);
            push @$devices, $dev if $dev && $mountpoint->{quota};
        };