[pve-devel] [RFC pve-container 2/6] LXC::Setup: id_map support for file wrappers

Wolfgang Bumiller w.bumiller at proxmox.com
Thu Nov 12 14:00:25 CET 2015


when an id_map is configured for the container or the
unprivileged flag set (which implies the default userid
map), the file access wrappers (LXC::Setup::Plugin::ct_*
functions) will use the id_map to fixup ownership of created
files.
---
 src/PVE/LXC.pm            | 33 +++++++++++++++++++++++++++++++++
 src/PVE/LXC/Create.pm     |  9 +++++----
 src/PVE/LXC/Setup.pm      |  8 ++++++++
 src/PVE/LXC/Setup/Base.pm | 34 +++++++++++++++++++++++++++++-----
 4 files changed, 75 insertions(+), 9 deletions(-)

diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index dc6d318..f38c5bd 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -2356,4 +2356,37 @@ sub complete_ctid_running {
     return &$complete_ctid_full(1);
 }
 
+sub parse_id_maps {
+    my ($conf) = @_;
+
+    my $id_map = [];
+    my $rootuid = 0;
+    my $rootgid = 0;
+
+    my $lxc = $conf->{lxc};
+    foreach my $entry (@$lxc) {
+	my ($key, $value) = @$entry;
+	next if $key ne 'lxc.id_map';
+	if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
+	    my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
+	    push @$id_map, [$type, $ct, $host, $length];
+	    if ($ct == 0) {
+		$rootuid = $host if $type eq 'u';
+		$rootgid = $host if $type eq 'g';
+	    }
+	} else {
+	    die "failed to parse id_map: $value\n";
+	}
+    }
+
+    if (!@$id_map && $conf->{unprivileged}) {
+	# Should we read them from /etc/subuid?
+	$id_map = [ ['u', '0', '100000', '65536'],
+	            ['g', '0', '100000', '65536'] ];
+	$rootuid = $rootgid = 100000;
+    }
+
+    return ($id_map, $rootuid, $rootgid);
+}
+
 1;
diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
index be90d66..5f76b88 100644
--- a/src/PVE/LXC/Create.pm
+++ b/src/PVE/LXC/Create.pm
@@ -28,10 +28,11 @@ sub restore_archive {
     my $userns_cmd = [];
 
 #    we always use the same mapping: 'b:0:100000:65536'
-#    if ($conf->{'lxc.id_map'}) {
-#	$userns_cmd = ['lxc-usernsexec', '-m', 'b:0:100000:65536', '--'];
-#	PVE::Tools::run_command(['chown', '-R', '100000:100000', $rootdir]);
-#    }
+    my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+    if (@$id_map) {
+	$userns_cmd = ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];
+	PVE::Tools::run_command(['chown', '-R', "$rootuid:$rootgid", $rootdir]);
+    }
 
     my $cmd = [@$userns_cmd, 'tar', 'xpf', $archive, '--totals',
                @$PVE::LXC::COMMON_TAR_FLAGS,
diff --git a/src/PVE/LXC/Setup.pm b/src/PVE/LXC/Setup.pm
index 9dde1e8..dc1457a 100644
--- a/src/PVE/LXC/Setup.pm
+++ b/src/PVE/LXC/Setup.pm
@@ -57,6 +57,14 @@ sub new {
 
     # Cache some host files we need access to:
     $plugin->{host_resolv_conf} = PVE::INotify::read_file('resolvconf');
+
+    # pass on user namespace information:
+    my ($id_map, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+    if (@$id_map) {
+	$plugin->{id_map} = $id_map;
+	$plugin->{rootuid} = $rootuid;
+	$plugin->{rootgid} = $rootgid;
+    }
     
     return $self;
 }
diff --git a/src/PVE/LXC/Setup/Base.pm b/src/PVE/LXC/Setup/Base.pm
index d114fb3..0d50583 100644
--- a/src/PVE/LXC/Setup/Base.pm
+++ b/src/PVE/LXC/Setup/Base.pm
@@ -443,11 +443,23 @@ sub post_create_hook {
 # File access wrappers for container setup code.
 # For user-namespace support these might need to take uid and gid maps into account.
 
+sub ct_reset_ownership {
+    my ($self, @files) = @_;
+    my $conf = $self->{conf};
+    return if !$self->{id_map};
+    my $uid = $self->{rootuid};
+    my $gid = $self->{rootgid};
+    chown($uid, $gid, @files);
+}
+
 sub ct_mkdir {
     my ($self, $file, $mask) = @_;
     # mkdir goes by parameter count - an `undef' mode acts like a mode of 0000
-    return CORE::mkdir($file, $mask) if defined ($mask);
-    return CORE::mkdir($file);
+    if (defined($mask)) {
+	return CORE::mkdir($file, $mask) && $self->ct_reset_ownership($file);
+    } else {
+	return CORE::mkdir($file) && $self->ct_reset_ownership($file);
+    }
 }
 
 sub ct_unlink {
@@ -471,12 +483,23 @@ sub ct_open_file_read {
 sub ct_open_file_write {
     my $self = shift;
     my $file = shift;
-    return IO::File->new($file, O_WRONLY | O_CREAT, @_);
+    my $fh = IO::File->new($file, O_WRONLY | O_CREAT, @_);
+    $self->ct_reset_ownership($fh);
+    return $fh;
 }
 
 sub ct_make_path {
     my $self = shift;
-    File::Path::make_path(@_);
+    if ($self->{id_map}) {
+	my $opts = pop;
+	if (ref($opts) eq 'HASH') {
+	    $opts->{owner} = $self->{rootuid} if !defined($self->{owner});
+	    $opts->{group} = $self->{rootgid} if !defined($self->{group});
+	}
+	File::Path::make_path(@_, $opts);
+    } else {
+	File::Path::make_path(@_);
+    }
 }
 
 sub ct_symlink {
@@ -516,7 +539,8 @@ sub ct_file_get_contents {
 
 sub ct_file_set_contents {
     my ($self, $file, $data, $perms) = @_;
-    return PVE::Tools::file_set_contents($file, $data, $perms);
+    PVE::Tools::file_set_contents($file, $data, $perms);
+    $self->ct_reset_ownership($file);
 }
 
 1;
-- 
2.1.4




More information about the pve-devel mailing list