App-LXC-Container
view release on metacpan or search on metacpan
};
like($@,
qr{^bad MASTER value 'G,-,-'$re_msg_tail},
'missing network ID in MASTER fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf', '#MASTER:G0,-,-');
eval {
$_ = App::LXC::Container::Run->new('run-test-broken', '', '');
};
like($@,
qr{^bad MASTER value 'G0'$re_msg_tail},
'wrong network ID in MASTER fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-broken.conf');
eval {
$_ = App::LXC::Container::Run->new('run-test-broken', '', '');
};
like($@,
qr{^directory /[^']+/lxc/run-test-broken.conf is missing$re_msg_tail},
'wrong rootfs entry in master configuration fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf', 'lxc.rootfs.path=/tmp');
eval {
$_ = App::LXC::Container::Run->new('run-test-broken', '', '');
};
like($@,
qr{^bad directory '/tmp'$re_msg_tail},
'bad rootfs entry in master configuration fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf',
'#MASTER:G42,-,-', 'lxc.net.0.ipv4.address = 10.0.3.47/24');
eval {
$_ = App::LXC::Container::Run->new('run-test-broken', '', '');
};
like($@,
qr{^bad MASTER value '10.0.3.47 \(!~ 42\$\)'$re_msg_tail},
'inconsistent network entry in master configuration fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf');
eval {
$_ = App::LXC::Container::Run->new('run-test-broken', '', '');
};
like($@,
qr{^bad MASTER value '\?\?\?'$re_msg_tail},
'empty master configuration fails');
_remove_file('/lxc/run-test-broken.conf');
_setup_file('/lxc/run-test-broken.conf',
'#MASTER:L42,-,-',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-broken',
'lxc.net.0.ipv4.address = 10.0.3.42/24');
$ENV{ALC_DEBUG} = 0; # cover branch in App::LXC::Container::run
_setup_file('/lxc-ls', '#!/bin/sh', 'exit 0'); # lxc-ls runs before nft!
_chmod(0755, '/lxc-ls');
$ENV{PATH} = TMP_PATH . ':/bin:/usr/bin'; # open will fail
stderr_like
{ eval "App::LXC::Container::run('run-test-broken');"; }
qr{^.*"nft": [^:]+App/LXC/Container/Run\.pm line \d+\.$},
'1st (mocked) nft list for local network fails with correct output';
like($@,
qr{^error running 'nft list ruleset inet' [^:]+: 0$re_eval},
'1st (mocked) nft list for local network fails with correct message');
$ENV{ALC_DEBUG} = 'x';
$ENV{PATH} = $test_path; # close will fail
eval { App::LXC::Container::run('run-test-broken'); };
like($@,
qr{^error running 'nft list ruleset inet' [^:]+: 256$re_msg_tail},
'2nd (mocked) nft list for local network fails');
delete $ENV{ALC_DEBUG};
#########################################################################
# tests with 1st (simple) valid configuration:
_setup_dir('/lxc/run-test-1');
_remove_file('/lxc/run-test-1.conf');
_setup_file('/lxc/run-test-1.conf',
'#MASTER:N,-,-',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-1',
'lxc.idmap = u 0 0 1',
'lxc.idmap = u 1 1 1',
'lxc.idmap = u 2 100002 65534',
'lxc.idmap = g 0 0 1',
'lxc.idmap = g 1 1 1',
'lxc.idmap = g 2 100002 65534',
'lxc.mount.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
'lxc.mount.entry = /tmp tmp none create=dir,rw,bind 0 0',
'');
$_ = App::LXC::Container::Run->new('run-test-1', 'root', '/', 'do', 'it');
check_config_object($_,
'valid configuration 1',
[[audio => '-'],
[command => ['do', 'it']],
[dir => '/'],
[gateway => '^$'],
[gids => [1]],
[init => CONF_ROOT . '/run-test-1/lxc-run.sh'],
[ip => '^$'],
[mounts => {'/tmp' => 1}],
[name => 'run-test-1'],
[network => 0],
[network_type => 'N'],
[rc => LXC_LINK . '/run-test-1.conf'],
[root => CONF_ROOT . '/run-test-1'],
[running => 0],
[uids => [1]],
[user => 'root'],
[x11 => '-']]);
# using container to test some _check_running errors:
$ENV{PATH} = ''; # sub-program will fail
stderr_like
{ eval '$_->_check_running();'; }
qr{^.*"lxc-ls": [^:]+App/LXC/Container/Run\.pm line \d+\.$},
'failing lxc-ls has correct output';
like($@,
#########################################################################
# using (modified!) container to test local network restrictions:
$_->{ip} = '10\.0\.3\.234';
output_like # 1 - not yet restricted
{ $_->_local_net(); }
qr{^$},
qr{^$},
'1st test with mocked local net OK';
output_like # 2 - everything has been already set-up
{ $_->_local_net(); }
qr{^$},
qr{^$},
'2nd test with mocked local net OK';
output_like # 3 - 1st add fails
{ eval '$_->_local_net();'; }
qr{^$},
qr{^nft: add chain inet lxc localfilter: mockup failed$},
'3rd test with mocked local net OK';
output_like # 4 - insert fails
{ eval '$_->_local_net();'; }
qr{^$},
qr{^nft: insert rule inet lxc forward jump localfilter: mockup failed$},
'4th test with mocked local net OK';
output_like # 5 - 2nd add fails
{ eval '$_->_local_net();'; }
qr{^$},
qr{^nft: add rule inet lxc localfilter ip saddr .+ reject: mockup failed$},
'5th test with mocked local net OK';
#########################################################################
# testing set-up of user accounts in (modified!) container:
_setup_dir('/etc');
_setup_file('/etc/passwd',
'root:x:0:0:root:/root:/bin/sh',
'daemon:x:1:1:daemon:/bin:/usr/sbin/nologin');
_setup_file('/etc/shadow',
'root:!:19191:0:99999:99:::',
'daemon:*:19191:0:99999:99:::');
_setup_file('/etc/group',
'root:x:0:',
'daemon:x:1:',
'other:x:42:some,root,one',
'dummy:x:12345:dummy');
_setup_file('/etc/gshadow',
'root:*::',
'daemon:*::');
$App::LXC::Container::Run::_root_etc = TMP_PATH . '/etc/';
_remove_file('/lxc/run-test-1/etc/group');
_remove_file('/lxc/run-test-1/etc/gshadow');
_remove_file('/lxc/run-test-1/etc/passwd');
_remove_file('/lxc/run-test-1/etc/shadow');
_remove_dir(TMP_PATH . '/lxc/run-test-1/etc');
eval '$_->_prepare_user();';
like($@,
qr{^can't open .+/run-test-1/etc/group': No such file or directory$re_eval},
'non-existing target /etc has correct output (for /etc/group)');
_setup_dir('/lxc/run-test-1/etc');
_setup_file('/lxc/run-test-1/etc/gshadow'); # for successful unlink of it
_chmod(0, '/etc/shadow');
eval '$_->_prepare_user();';
like($@,
qr{can't open .+tmp/etc/shadow': Permission denied$re_eval},
'failing read-access to mocked /etc/shadow has correct output');
_chmod(0644, '/etc/shadow');
_chmod(0555, '/lxc/run-test-1/etc');
eval '$_->_prepare_user();';
like($@,
qr{can't remove .+tmp/lxc/run-test-1/etc/group': Permission denied$re_eval},
'failing write-access to target /etc has correct output');
_chmod(0755, '/lxc/run-test-1/etc');
$_->{mounts}{'/etc'} = 1;
output_like
{ $_->_prepare_user(); }
qr{^$}, qr{^$},
'_prepare_user for mapped /etc looks correct';
delete $_->{mounts}{'/etc'};
$_->{mounts}{'/etc/group'} = 1;
$_->{mounts}{'/etc/gshadow'} = 1;
$_->{mounts}{'/etc/passwd'} = 1;
my $re_account_files = 'group gshadow passwd shadow';
output_like
{ $_->_prepare_user(); }
qr{^$},
qr{^broken user mapping - check mounting of $re_account_files$re_msg_tail},
'_prepare_user for only 3 mapped account files looks correct';
$_->{mounts}{'/etc/shadow'} = 1;
output_like
{ $_->_prepare_user(); }
qr{^$}, qr{^$},
'_prepare_user for all 4 mapped account files looks correct';
#########################################################################
# check writing of startup script for 1st configuration:
_remove_file('/lxc/run-test-1/lxc-run.sh');
_setup_file('/lxc/run-test-1/lxc-run.sh');
_chmod(0444, '/lxc/run-test-1/lxc-run.sh');
eval '$_->_write_init_sh();';
like($@,
qr{can't open .+tmp/lxc/run-test-1/lxc-run.sh': Permission denied$re_eval},
'failing write-access to startup script lxc-run.sh has correct output');
_remove_file('/lxc/run-test-1/lxc-run.sh');
$_->{running} = 0;
eval '$_->_write_init_sh();';
is($@, '', 'creating minimal startup script run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-1/lxc-run.sh',
{DISPLAY => '!DISPLAY',
PULSE => '!PULSE_SERVER',
XAUTHORITY => '!XAUTHORITY',
cd => 'cd "/"',
dns => '!nameserver',
exec => "exec 'do' 'it'",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
$_->{running} = 1;
eval '$_->_write_init_sh();';
is($@, '', 'creating 2nd minimal startup script run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-1/lxc-run.sh',
{DISPLAY => '!DISPLAY',
PULSE => '!PULSE_SERVER',
XAUTHORITY => '!XAUTHORITY',
cd => 'cd "/"',
dns => '!nameserver',
exec => "exec 'do' 'it'",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
$_->{user} = 'bin';
eval '$_->_write_init_sh();';
is($@, '', 'creating variant 1 for other user run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-1/lxc-run.sh',
{DISPLAY => '!DISPLAY',
PULSE => '!PULSE_SERVER',
XAUTHORITY => '!XAUTHORITY',
cd => 'cd "/"',
dns => '!nameserver',
exec =>
"exec su bin -s /bin/sh -c 'do \"\\\$@\"' -- dummy_argv0 'it'",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
$_->{command} = ['do'];
eval '$_->_write_init_sh();';
is($@, '', 'creating variant 2 for other user run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-1/lxc-run.sh',
{DISPLAY => '!DISPLAY',
PULSE => '!PULSE_SERVER',
XAUTHORITY => '!XAUTHORITY',
cd => 'cd "/"',
dns => '!nameserver',
exec => "exec su bin -s /bin/sh -c 'do'",
gateway => '!gateway',
$_->{command} = ['do', 'mixed', 'q"uot"ed', "par'ameters"];
eval '$_->_write_init_sh();';
is($@, '', 'creating startup script with quoted parameters run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-1/lxc-run.sh',
{DISPLAY => '!DISPLAY',
PULSE => '!PULSE_SERVER',
XAUTHORITY => '!XAUTHORITY',
cd => 'cd "/"',
dns => '!nameserver',
exec => "exec 'do' 'mixed' 'q\"uot\"ed' \"par'ameters\"",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
#########################################################################
# testing mocked runs (even the failing ones must run in subprocesses,
# otherwise Devel::Cover gets confused):
$ENV{PATH} = ''; # lxc-execute and lxc-attach will fail
my $re_output =
"using 'PoorTerm' as UI\n" .
'.+"lxc-execute": [^:]+App/LXC/Container/Run\.pm line \d+\.' . "\n" .
"call to 'lxc-execute' failed: No such file or directory at -e line \\d\\.";
$_ = _sub_perl('use App::LXC::Container;
$_ = App::LXC::Container::Run->new
("run-test-1", "root", "/", "command");
$_->_run();');
like($_, qr{^$re_output$}, 'lxc-execute fails with empty PATH');
$re_output =
"using 'PoorTerm' as UI\n" .
'.+"lxc-attach": [^:]+App/LXC/Container/Run\.pm line \d+\.' . "\n" .
"call to 'lxc-attach' failed: No such file or directory at -e line \\d\\.";
$_ = _sub_perl('use App::LXC::Container;
$_ = App::LXC::Container::Run->new
("run-test-1", "root", "/", "command");
$_->{running} = 1;
$_->_run();');
like($_, qr{^$re_output$}, 'lxc-attach fails with empty PATH');
$ENV{PATH} = $test_path; # back to normal
$_ = _sub_perl('use App::LXC::Container;
$_ = App::LXC::Container::Run->new
("run-test-1", "root", "/", "command");
$_->_run();');
like($_, qr{^using 'PoorTerm' as UI$},
'_run in 1st mockup test (lxc-execute) seems correct');
$_ = _sub_perl('use App::LXC::Container;
$_ = App::LXC::Container::Run->new
("run-test-1", "root", "/", "command");
$_->{running} = 1;
$_->_run();');
like($_, qr{^using 'PoorTerm' as UI$},
'_run in 2nd mockup test (lxc-attach) seems correct');
_setup_dir('/lxc/run-test-1/.xauth-dir');
_setup_file('/lxc/run-test-1/.xauth-dir/.Xauthority', 42);
_chmod(0555, '/lxc/run-test-1/.xauth-dir');
$_ = _sub_perl('use App::LXC::Container;
$_ = App::LXC::Container::Run->new
("run-test-1", "root", "/", "command");
$_->_run();');
$re_output =
"using 'PoorTerm' as UI\n" .
"can't remove .+tmp/lxc/run-test-1/.xauth-dir/.Xauthority': " .
'Permission denied at -e line \d\.';
like($_, qr{^$re_output$},
'_run in 3rd mockup test (lxc-execute protected .Xauthority) fails correct');
_chmod(0755, '/lxc/run-test-1/.xauth-dir');
_remove_file('/lxc/run-test-1/.xauth-dir/.Xauthority');
_remove_dir('/lxc/run-test-1/.xauth-dir');
#########################################################################
# tests with 2nd valid configuration:
_setup_dir('/lxc/run-test-2');
_remove_file('/lxc/run-test-2.conf');
_setup_file('/lxc/run-test-2.conf',
'#MASTER:G42,X,A',
'lxc.rootfs.path=' . CONF_ROOT . '/run-test-2',
'lxc.net.0.ipv4.address = 10.0.3.42/24',
'lxc.idmap = u 0 100000 65536',
'lxc.idmap = g 0 100000 65536',
'lxc.mount.entry = tmpfs dev/shm tmpfs create=dir,rw 0 0',
'lxc.mount.entry = /tmp tmp none create=dir,rw,bind 0 0',
'');
_setup_dir('/lxc/run-test-2/etc');
$_ = App::LXC::Container::Run->new('run-test-2', 'root', '/', 'do', 'it');
check_config_object($_,
'valid configuration 2',
[[audio => 'A'],
[command => ['do', 'it']],
[dir => '/'],
[gateway => '^10\.0\.3\.1$'],
[gids => []],
[init => CONF_ROOT . '/run-test-2/lxc-run.sh'],
[ip => '^10\.0\.3\.42$'],
[mounts => {'/tmp' => 1}],
[name => 'run-test-2'],
[network => 42],
[network_type => 'G'],
[rc => LXC_LINK . '/run-test-2.conf'],
[root => CONF_ROOT . '/run-test-2'],
[running => 0],
[uids => []],
[user => 'root'],
[x11 => 'X']]);
#########################################################################
# check writing of startup script for 2nd configuration:
_remove_file('/lxc/run-test-2/lxc-run.sh');
_remove_file('/home/.Xauthority');
_setup_file('/home/.Xauthority');
_chmod(0600, '/home/.Xauthority');
system('cp', '-a',
T_PATH . '/mockup-files/.Xauthority',
HOME_PATH . '/.Xauthority') == 0
or die "can't cp mockup '.Xauthority: $!\n";
$ENV{DISPLAY} = ':0';
$ENV{XAUTHORITY} = HOME_PATH . '/.Xauthority';
_remove_file('/lxc/run-test-2/.xauth-root/.Xauthority');
_remove_dir(TMP_PATH . '/lxc/run-test-2/.xauth-root');
_chmod(0555, '/lxc/run-test-2');
eval '$_->_write_init_sh();'; # 1 - creating .xauth directory fails
like($@,
qr{^can't create .+/lxc/run-test-2/.xauth-root': Permission denied$re_eval},
'failing write-access for .xauth directory has correct output');
_chmod(0755, '/lxc/run-test-2');
eval '$_->_write_init_sh();'; # 2 - "empty original" .Xauthority fails
like($@,
qr{^call to 'xauth list' failed: no :0$re_eval},
'missing .Xauthority entry fails correctly');
eval '$_->_write_init_sh();'; # 3 - writing .Xauthority fails
like($@,
qr{^call to 'xauth -b -f [^']+/\.Xauthority add [^']+' failed: \d+$re_eval},
'failing write-access for .Xauthority has correct output');
eval '$_->_write_init_sh();'; # 4 - run without error (D+XA)
is($@, '', 'creating startup script with full X11 access run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-2/lxc-run.sh',
{DISPLAY => 'export DISPLAY=:0',
PULSE => 'export PULSE_SERVER=10\.0\.3\.1',
XAUTHORITY => 'export XAUTHORITY=/\.xauth-root/\.Xauthority',
cd => 'cd "/"',
dns => 'echo "nameserver \$gateway" >/etc/resolv\.conf',
exec => "exec 'do' 'it'",
gateway => 'gateway=10\.0\.3\.1',
route => 'ip route add default via "\$gateway"',
shebang => '#!/bin/sh'});
ok(-f TMP_PATH . '/lxc/run-test-2/.xauth-root/.Xauthority',
'1st .Xauthority file has been created in correct location');
$_->{running} = 1;
eval '$_->_write_init_sh();'; # 5 - run without error (D+XA, 2nd)
is($@, '', 'creating startup script for 2nd X11 access run without problems');
check_config_file(TMP_PATH . '/lxc/run-test-2/lxc-run.sh',
{DISPLAY => 'export DISPLAY=:0',
PULSE => 'export PULSE_SERVER=10\.0\.3\.1',
XAUTHORITY => 'export XAUTHORITY=/\.xauth-root/\.Xauthority',
cd => 'cd "/"',
dns => '!gateway',
exec => "exec 'do' 'it'",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
my $user = defined $ENV{USER} ? $ENV{USER} : 'root' ;
$_->{user} = $user;
eval '$_->_write_init_sh();'; # 6 - run without error (D+XA, 2nd user)
is($@, '',
"creating startup script for 2nd user's X11 access run without problems");
check_config_file(TMP_PATH . '/lxc/run-test-2/lxc-run.sh',
{DISPLAY => 'export DISPLAY=:0',
PULSE => 'export PULSE_SERVER=10\.0\.3\.1',
XAUTHORITY => "export XAUTHORITY=/\.xauth-$user/\.Xauthority",
cd => 'cd "/"',
dns => '!gateway',
exec =>
"exec su $user -s /bin/sh -c 'do \"..\"' -- dummy_argv0 'it'",
gateway => '!gateway',
route => '!route',
shebang => '#!/bin/sh'});
ok(-f TMP_PATH . '/lxc/run-test-2/.xauth-' . $user . '/.Xauthority',
'2nd .Xauthority file has been created in correct location');
$_->{user} = 'root' ;
$_->{running} = 0;
( run in 0.693 second using v1.01-cache-2.11-cpan-99c4e6809bf )