From c6a605f9eee58fa44348af8313927bfc04386717 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 12 Nov 2015 14:00:25 +0100 Subject: [PATCH] LXC::Setup: id_map support for file wrappers 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 | 10 +++++----- src/PVE/LXC/Setup.pm | 8 ++++++++ src/PVE/LXC/Setup/Base.pm | 34 +++++++++++++++++++++++++++++----- 4 files changed, 75 insertions(+), 10 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..cb9bbff 100644 --- a/src/PVE/LXC/Create.pm +++ b/src/PVE/LXC/Create.pm @@ -27,11 +27,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.39.5